using System;
using System.Collections;
using System.Threading;
// Below are the DirectX files required for DirectSound
using Microsoft.DirectX.DirectSound;
using Buffer = Microsoft.DirectX.DirectSound.Buffer;
using Microsoft.DirectX.AudioVideoPlayback;

using Icarus.Utilities.Data;

//TODO: Maybe it would be a good idea to rethink the interface of the sound engine. It's kind of awkward to use right now.

namespace Icarus.SoundEngine {
    /// <summary>
    /// The SoundEngine of Icarus Game Engine.
    /// </summary>
    public class SoundEngine {

        #region Instance Variables
        /// <summary>
        /// Reference to singleton SoundSettings class.
        /// </summary>
        private SoundSettings settings = null;
        /// <summary>
        /// Reference to next available SoundBuffer.
        /// </summary>
        private int currentFreeBuffer = 0;
        /// <summary>
        /// The device for the SoundEngine.
        /// </summary>
        private Device engineDevice = null;
        /// <summary>
        /// Overlays that plays music.
        /// </summary>
        private MusicStream[] overlay = null;

		/// <summary>
		/// The cache of sounds to keep loaded.
		/// </summary>
		private Cache mSoundCache = new Cache(SoundSettings.Instance.gs_n_soundEffects);
        #endregion

        #region Singleton Stuff
        /* --------------- */
        /* Singleton Stuff */
        /* --------------- */

        /// <summary>
        /// The singleton class instance of this class.
        /// </summary>
        private static SoundEngine instance = new SoundEngine();
		
        /// <summary>
        /// Private singleton constructor.
        /// </summary>
        private SoundEngine() {
            settings = SoundSettings.Instance;
            overlay = new MusicStream[settings.gs_n_bgMusic];
            currentFreeBuffer = 0;
        }

        /// <summary>
        /// Accessor/Modifier for the singleton class instance.
        /// </summary>
        public static SoundEngine Instance {
            get { return instance; }
        }
        #endregion

        #region Initialization
        /* -------------- */
        /* Initialization */
        /* -------------- */

        /// <summary>
        /// Initializes the Device for the SoundEngine.
        /// </summary>
        /// <returns>Returns true if successful, false if failed.</returns>
        public bool Initialize(System.Windows.Forms.Control owner) {      
            try {
                engineDevice = new Device();
                engineDevice.SetCooperativeLevel(owner, CooperativeLevel.Priority);
                return true;
            } catch {
                return false;
            }
        }//end Initialize()
        #endregion

        #region Volume Acessing/Modifying
        /* -------------------------- */
        /* Volume Accessing/Modifying */
        /* -------------------------- */

        ///	<summary>
        /// Sets the volume for game sounds.
        /// </summary>
        /// <param name="vol">Volume for sounds.</param>
        public void SetSoundVolume(int vol) {
            if (vol >= 0 && vol <= 100) {
                settings.gs_soundVolume = vol;
            }

			IDictionaryEnumerator enm = mSoundCache.GetEnumerator();
			while (enm.MoveNext())
			{
				SecondaryBuffer buffer = enm.Value as SecondaryBuffer;
				buffer.Volume = (settings.gs_soundVolume - 100) * 50;
			}
        }//end SetSoundVolume(int)

        /// <summary>
        /// Accessor for the current volume for sounds.
        /// </summary>
        /// <returns>Volume for sounds.</returns>
        public int GetSoundVolume() {
            return settings.gs_soundVolume;
        }//end GetSoundVolume()

        /// <summary>
        /// Sets the volume for game music.
        /// </summary>
        /// <param name="vol">Volume for music.</param>
        public void SetMusicVolume(int vol) {
            int i;

            if (vol >= 0 && vol <= 100) {
                settings.gs_musicVolume = vol;
            }
      
            for (i = 0; i < settings.gs_n_bgMusic; i++) {
                if (overlay[i] != null) {
                    overlay[i].SetVolume((settings.gs_musicVolume - 100) * 50);
                }
            }
        }//end SetMusicVolume(int)

        /// <summary>
        /// Accessor for the current volume for music.
        /// </summary>
        /// <returns>Volume for music.</returns>
        public int GetMusicVolume() {
            return settings.gs_musicVolume;
        }//end GetMusicVolume()
        #endregion

        #region Sound Playing
        /* ------------- */
        /* Sound Playing */
        /* ------------- */

		/// <summary>
		/// Plays a random sound from the directory specified.
		/// </summary>
		/// <param name="dirId"></param>
		/// <returns></returns>
		public bool PlayRandomSoundFrom(string dirId)
		{
			string snd = Icarus.Resources.ResourceManager.Instance.GetRandomSoundFrom(dirId);

			if (snd != null) 
			{
				return PlaySound(snd); 
			}
			else
			{
				return false;
			}
		}

        /// <summary>
        /// Plays the sound file at the established sound volume.
        /// </summary>
        /// <param name="sndFile">File to be played.</param>
        /// <returns> true if successful, false if failed.</returns>
        public bool PlaySound(string sndFile) {
			SecondaryBuffer buf = null;

			if (mSoundCache.Contains(sndFile))
			{
				buf = mSoundCache[sndFile] as SecondaryBuffer;
			}
			else
			{
				if (mSoundCache.CanAddElement)
				{
					buf = new SecondaryBuffer(sndFile, engineDevice);
					buf.Volume = (settings.gs_soundVolume - 100) * 50;
					mSoundCache.Add(sndFile, buf);
				}
				else
				{
					Console.WriteLine("Could not play sound. All sound buffers in use.");        
					return false;
				}
			}

			if (!buf.Status.Playing)
			{
				buf.Play(0, BufferPlayFlags.Default);
				Console.WriteLine("played");
			}

            return true;            
        }
        #endregion

        #region Music Playing
        /* ------------- */
        /* Music Playing */
        /* ------------- */

        /// <summary>
        /// Plays all streams currently loaded with music.
        /// </summary>
        public void PlayMusic() {
            for (int i = 0; i < settings.gs_n_bgMusic; i++) {
                if (overlay[i] != null) {
                    overlay[i].PlayMusic();
                }
            }
        }

		/// <summary>
		/// Sets the music to be played from the id of the music file. Note that all other music will be stopped and cleared. This music will be set up, but playing won't be started yet. 
		/// </summary>
		/// <param name="id"></param>
		public void SetMusicFromId(string id)
		{
			//Clear out the buffers:
			this.ClearMusic();

			//Queue this baby:
			overlay[0] = new MusicStream();
			overlay[0].gs_musicFilename = Icarus.Resources.ResourceManager.Instance.MusicPath + id;
		}

        /// <summary>
        /// Plays the music file on the specified MusicStream overlay.
        /// </summary>
        /// <param name="filename">File to be played.</param>
        /// <param name="stream">Stream to be played on.</param>
        public void PlayMusic(string filename, int stream) {
            if (overlay[stream] == null) {
                overlay[stream] = new MusicStream();
                overlay[stream].PlayMusic(filename);
            } else {
                overlay[stream].PlayMusic(filename);
            }
        }

        /// <summary>
        /// Stops all currently playing music.
        /// </summary>
        public void StopMusic() {
            for (int i = 0; i < settings.gs_n_bgMusic; i++) {
                StopMusic(i);
            }
        }

        /// <summary>
        /// Stops currently playing music on the specified MusicStream overlay.
        /// </summary>
        /// <param name="stream">Stream to be played on.</param>
        public void StopMusic(int stream) {
            if (overlay[stream] != null) {
                overlay[stream].StopMusic();
            }
        }

        /// <summary>
        /// Pauses all currently playing music. DOES NOT RESUME.
        /// </summary>
        public void PauseMusic() {
            for (int i = 0; i < settings.gs_n_bgMusic; i++) {        
                PauseMusic(i);
            }
        }

        /// <summary>
        /// Pauses music when music is currently playing and resumes music when music 
        /// is paused on the specified MusicStream overlay.
        /// </summary>
        /// <param name="stream">Stream to be paused on.</param>
        public void PauseMusic(int stream) {
            if (overlay[stream] != null) {
                overlay[stream].PauseMusic();
            }
        }

		/// <summary>
		/// Stops and clears out all music buffers.
		/// </summary>
		public void ClearMusic()
		{
			//Stop the music first:
			StopMusic();

			//Clear out the buffers:
			for (int i = 0; i < settings.gs_n_bgMusic; i++)
			{
				overlay[i] = null;
			}
		}
        #endregion

        #region Overlay Management
        /// <summary>
        /// Gets the filename of the specified music stream.
        /// </summary>
        /// <param name="stream">Stream to be requested.</param>
        /// <returns>Music file name of the stream.</returns>
        public string getStreamFilename(int stream) {
            if (overlay[stream] != null) {
                return overlay[stream].gs_musicFilename;
            } else {
                return "No file loaded.";
            }
        }
    
        /// <summary>
        /// Set the next file to stream
        /// </summary>
        /// <param name="filename">fileName of the next file</param>
        /// <param name="stream">stream to play on</param>
        public void SetNextFileOnStream(string filename, int stream) {
            if (overlay[stream] == null) {
                overlay[stream] = new MusicStream();        
            }
            overlay[stream].gs_musicFilename = filename;
        }

        /// <summary>
        /// Cleans out and removes a single music stream.
        /// </summary>
        /// <param name="stream">Stream to be removed.</param>
        public void RemoveStream(int stream) {
            if (overlay[stream] != null) {
                overlay[stream].StopMusic();
                overlay[stream] = null;
            }
        }
        #endregion

    }//end SoundEngine()
}
//Icarus.SoundEngine.SoundTester.Main();