//-----------------------------------------------------------------------------
// AudioManager.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
namespace Audio3D
{
    /// 
    /// Audio manager keeps track of what 3D sounds are playing, updating
    /// their settings as the camera and entities move around the world, and
    /// automatically disposing sound effect instances after they finish playing.
    /// 
    public class AudioManager : Microsoft.Xna.Framework.GameComponent
    {
        // List of all the sound effects that will be loaded into this manager.
        static string[] soundNames =
        {
            "CatSound0",
            "CatSound1",
            "CatSound2",
            "DogSound",
        };
        // The listener describes the ear which is hearing 3D sounds.
        // This is usually set to match the camera.
        public AudioListener Listener
        {
            get { return listener; }
        }
        AudioListener listener = new AudioListener();
        // The emitter describes an entity which is making a 3D sound.
        AudioEmitter emitter = new AudioEmitter();
        // Store all the sound effects that are available to be played.
        Dictionary soundEffects = new Dictionary();
        
        // Keep track of all the 3D sounds that are currently playing.
        List activeSounds = new List();
        public AudioManager(Game game)
            : base(game)
        { }
        /// 
        /// Initializes the audio manager.
        /// 
        public override void Initialize()
        {
            // Set the scale for 3D audio so it matches the scale of our game world.
            // DistanceScale controls how much sounds change volume as you move further away.
            // DopplerScale controls how much sounds change pitch as you move past them.
            SoundEffect.DistanceScale = 2000;
            SoundEffect.DopplerScale = 0.1f;
            // Load all the sound effects.
            foreach (string soundName in soundNames)
            {
                soundEffects.Add(soundName, Game.Content.Load(soundName));
            }
            base.Initialize();
        }
        /// 
        /// Unloads the sound effect data.
        /// 
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    foreach (SoundEffect soundEffect in soundEffects.Values)
                    {
                        soundEffect.Dispose();
                    }
                    soundEffects.Clear();
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }
        
        /// 
        /// Updates the state of the 3D audio system.
        /// 
        public override void Update(GameTime gameTime)
        {
            // Loop over all the currently playing 3D sounds.
            int index = 0;
            while (index < activeSounds.Count)
            {
                ActiveSound activeSound = activeSounds[index];
                if (activeSound.Instance.State == SoundState.Stopped)
                {
                    // If the sound has stopped playing, dispose it.
                    activeSound.Instance.Dispose();
                    // Remove it from the active list.
                    activeSounds.RemoveAt(index);
                }
                else
                {
                    // If the sound is still playing, update its 3D settings.
                    Apply3D(activeSound);
                    index++;
                }
            }
            base.Update(gameTime);
        }
        /// 
        /// Triggers a new 3D sound.
        /// 
        public SoundEffectInstance Play3DSound(string soundName, bool isLooped, IAudioEmitter emitter)
        {
            ActiveSound activeSound = new ActiveSound();
            // Fill in the instance and emitter fields.
            activeSound.Instance = soundEffects[soundName].CreateInstance();
            activeSound.Instance.IsLooped = isLooped;
            activeSound.Emitter = emitter;
            // Set the 3D position of this sound, and then play it.
            Apply3D(activeSound);
            activeSound.Instance.Play();
            // Remember that this sound is now active.
            activeSounds.Add(activeSound);
            return activeSound.Instance;
        }
        /// 
        /// Updates the position and velocity settings of a 3D sound.
        /// 
        private void Apply3D(ActiveSound activeSound)
        {
            emitter.Position = activeSound.Emitter.Position;
            emitter.Forward = activeSound.Emitter.Forward;
            emitter.Up = activeSound.Emitter.Up;
            emitter.Velocity = activeSound.Emitter.Velocity;
            activeSound.Instance.Apply3D(listener, emitter);
        }
        /// 
        /// Internal helper class for keeping track of an active 3D sound,
        /// and remembering which emitter object it is attached to.
        /// 
        private class ActiveSound
        {
            public SoundEffectInstance Instance;
            public IAudioEmitter Emitter;
        }
    }
}