#ifndef SOUNDMANAGER_H
#define SOUNDMANAGER_H

#include <string>
#include <map>
#include <list>
using namespace std;

#include "SoundDescriptor.h"
#include "Sound.h"
#include "SoundEngine.h"


namespace SoundSystem
{
	/**
	 * structure gerant les differents parametre necessairent pour configurer un son avant de le demarrer.
	 */
	struct SoundConfig
	{
		/**
		 * la position X du son
		 */
		float x;
		/**
		 * la position Y du son
		 */
		float y;
		/**
		 * la position Z du son
		 */
		float z;
		/**
		 * la distance minimum du son
		 */
		float min;
		/**
		 * la distance maximum du son
		 */
		float max;
		/**
		 * le son doit il boucler ou pas
		 */
		bool loop;
		/**
		 * le volume du son
		 */
		int volume;
		/**
		 * permet de savoir si le son doit etre traité comme un son 3D ou pas
		 */
		bool is3D;
		/**
		 * constructeur par defaut.
		 *
		 * met le son a la position {0,0,0}, la distance min a 0, la distance max a 1000, loop a false, is3D a true et
		 * le volume a 255
		 */
		SoundConfig()
		{
			x = 0.0f;
			y = 0.0f;
			z = 0.0f;
			min = 0.0f;
			max = 100.0f;
			loop = false;
			is3D = true;
			volume = 255;
		};

		/**
		 * constructeur parametant completement le SoundConfig
		 * 
		 * @param x : la position x du son
		 * @param y : la position y du son
		 * @param z : la position z du son
		 * @param min : le rayon minimum du son
		 * @param max : le rayon maximum du son
		 * @param loop : definit si le son boucle ou pas
		 * @param is3D : definit si le son est geré comme un son 3D ou pas
		 * @param volume : le volume sonore du son
		 */
		SoundConfig(float x, float y, float z, float min, float max, bool loop, bool is3D, int volume)
		{
			this->x = x;
			this->y = y;
			this->z = z;
			this->min = min;
			this->max = max;
			this->loop = loop;
			this->is3D = is3D;
			this->volume = volume;
		}
	};

	/**
	 * la classe gerant le lecture de sons sous diverses formes (STREAM, SAMPLE, son 3D, sons statiques, son dynamique)
	 *
	 * Cette classe gere 2 types de sons : les STREAM et les SAMPLE. Ces type de sont sont decrit par la suite sous 
	 * le nom de descripteur de son
	 *
	 * un STREAM est un descripteur lue a partir du disque en  streaming. les stream ne peuvent etre joué que par un son 
	 * a la fois.
	 *
	 * Un SAMPLE est un descripteur chargé en memoire qui peut etre joué par plusieurs son en même temps. Il sont donc plus
	 * flaxible que les STREAM mais necessitent plus de ressources memoire que les STREAM. Typiquement, les STREAM sont
	 * utilisé pour les sont ayant une taille importante sur le disque et n'etant joué que peut souvent, alors que
	 * les SAMPLE sont utilisé pour les sont utilisé frequament mais ayant une petite taille sur le disque.
	 *
	 * Le manager prend en compte 2 type de sons d'un point de vue gestion :
	 * - les sons statique : ce sont des sons qui ne peuvent pas etre modifié apres avoir été lancé. Leur gestion
	 * memoire est prise en compte par le manager. l'utilisateur a juste a lancer le sons sans s'occuper de
	 * sa destruction ou de son update. Il peut par contre interagire avec ces sons grace au methode stopAll() et 
	 * pauseAll() qui stoppent ou mettent en pause l'ensemble des sons joué sur le manager (y comprit les sons
	 * dynamiques.)
	 * - les sons dynamiques : ces sons sont prevu pour pouvoir etre modifié par l'utilisateur. En contrepatie, leur
	 * gestion est plus difficile car il doivent etre libéré par le manager.
	 */
	class SoundManager
	{
		friend class SoundEngine;
	private:

		/**
		 * la map des descripteurs de sons
		 */
		map<string,SoundDescriptor*> descripteurs;
		/**
		 * la map des sons en train d'etre joué.
		 */
		map<void*,Sound*> playedSounds;

		/**
		 * la liste des sons statics
		 */
		list<Sound*> staticSounds;

		/**
		 * le constructeur mis en privé afin d'obliger l'utilisateur a utiliser la friend class SoundEngine
		 * pour crée des SoundManager
		 */
		SoundManager(void);
		/**
		 * le destructeur mis en privé comme pour le constructeur.
		 */
		virtual ~SoundManager(void);

		/**
		 * permet de savoir si il faut precharger les sons en memoire
		 */
		bool precache;

		/**
		 * methode permettant de nettoyer tout les sons static de la memoire
		 */
		void clearStaticSound();

	public:
		/**
		 * methode permettant d'ajouter un descripteur de son dan sle manager.
		 *
		 * Un descriptur de son est un doublet <nom de fichier ; nom logique du son> qui permet par la suite
		 * d'appeler un son en fonction de son nom logique. Si le SoundManager est mis en mode precaching et
		 * que le son charger en un SAMPLE (mode par defaut), le son est précharger lors de l'ajout. ainsi, lors
		 * de la lecture, le lancement du son ne risque pas d'entrainer de ralentissement (mais par contre l'ajout
		 * du son sera plus lent.
		 *
		 * @param name : le nom logique du son. se nom servira par la suite a retrouver le son pour le jouer par exemple.
		 * @param path : le chemin du fichier physique sur le disque.
		 * @param type : le type de son. peut etre SAMPLE ou STREAM. la caleure par defaut est SAMPLE.
		 */
		void addSound(const string& name,const string& path,DescriptorType type = SAMPLE);

		/**
		 * methode permettant de savoir si un son est present dans le manager ou pas.
		 *
		 * @param soundName : le nom logique du son dont on veut tester l'existance dans le manager
		 * @return bool : true si le son est bien present dans le manager, false sinon.
		 */
		bool soundExist(const string& soundName);

		/**
		 * methode permettant de supprimer un son du manager. Attention, la suppression d'un son va stopper
		 * la lecture des sons sur celui ci.
		 *
		 * @param soundName : le nom logique du son a supprimer.
		 */
		void removeSound(const string &soundName);

		/**
		 * methode supprimant tout les descripteurs de son du manager et stoppant la lecture de tout les sons.
		 */
		void clear();

		/**
		 * methode mettant en pause/relancant l'ensemble des sons joué sur le manager.
		 *
		 * @param pause : le mode de pause de l'ensemble des sons :
		 * - true => tout les sons sont mis en pause.
		 * - false => tout les sons sont relancé.
		 */
		void pauseAll(bool pause);

		/**
		 * methode mettant a jour l'ensemble des sons joué sur le manager en fonction de la position de la camera
		 *
		 * @param x : la position x de la camera
		 * @param y : la position y de la camera
		 * @param z : la position z de la camera
		 */
		void update(float x,float y, float z);

		/**
		 * methode stoppant tout les sons du manager.
		 */
		void stopAll();

		/**
		 * methode jouant un son dynamique.
		 *
		 * Un son dynamique est un son sur lequel l'utilisateur peut faire des changements (position, volume...).
		 * Les son dynamique sont a mettr en opposition au son statiques sur lequelles l'utilisateur ne peut pas
		 * effectuer de changements mis a part a traver l'interface du manager (pauseAll(), stopAll()).
		 *
		 * Cette methode retourne un pointeur sur un Sound qui est alloué par le manager et doit IMPERATIVEMENT
		 * etre nettoyé par le manager avec la methode freeSound().
		 * 
		 * TODO : passer les constructeur/destucteurs de Sound en private et declarer SoundManager en friend.
		 *
		 * @param soundName : le nom logique du son que l'utilisateur veut jouer.
		 * @param config : structure permettant de configurer le son a jouer (position, volume, minMax ...)
		 *
		 * @return Sound : pointeur sur un Sound pouvant etre modifié par l'utilisateur. NULL en cas d'echec de
		 * lancement du son. Le pointeur retourné par cette methode doit IMPERATIVEMENT etre detruit par la methode 
		 * freeSound() du manager sur lequel a été appelé playDynamicSound().
		 */
		Sound * playDynamicSound(const string& soundName,SoundConfig &config = SoundConfig());

		/**
		 * methode jouant un son static.
		 *
		 * Les son static sont a mettre en oposition au son dynamique. Un son static ne peut pas etre modifié par 
		 * l'utilisateur une fois lancé. Seul les methode pauseAll() et stopAll() du manager peuvent changer leur 
		 * etat. Leur gestion memoire est prise en compte par le manager et donc l'utilisateur n'a pas a s'en occuper.
		 *
		 * En fait en interne, les sons static sont des sons dynamiques mais leur gestion memoire est prise en compte
		 * par le manager. Un son static qui s'arrete de joué est automatiquement supprimé par le manager.
		 *
		 * @param soundName : le nom logique du son a jouer.
		 * @param config : la structure de configuration du son (position, volume...)
		 *
		 * @return bool : true si le son a bien été lancé, false sinon.
		 */
		bool playStaticSound(const string& soundName,SoundConfig &config = SoundConfig());

		/**
		 * methode liberant un Sound alloué par la methode playDynamicSound().
		 *
		 * Cette methode prend une reference vers un pointeur de Sound, si le pointeur a bien été alloué par ce manager,
		 * il libere la memoire utilisée par le pointeur et met le pointeur a NULL. Par contre si le pointeur n'a pas
		 * été alloué par le manager, la methode affiche un message d'erreur et n'effectue pas de changement sur le
		 * pointeur (la memoire n'est donc par liberée.
		 */
		void freeSound(Sound* &son);

		/**
		 * methode permettant de definir si le manager doit utiliser le precaching ou pas.
		 *
		 * Le precaching consiste a charger les sons (uniquement les SAMPLE) en memoire lorsqu'ils sont ajouté au
		 * manager (methode addSound()). Le precaching pemet d'eviter que le chargement du son ne soit effectué 
		 * lors de la premiere lecture du son, ce qui peut entrainer de fort ralentissement si le son est important.
		 *
		 * @param precaching : le mode d'activation du precaching : true => utiliser le precaching, false => ne pas
		 * utiliser le precaching.
		 */
		void setPrecaching(bool precaching);

		/**
		 * methode permettant de savoir si le manager utilise le precaching.
		 *
		 * @return bool : true => le manager utilise le precaching, false => le manager n'utilise pas le precaching.
		 */
		bool usePrecaching();

		/**
		 * methode prechargeant en memoire l'ensemble des sons (uniqement les SAMPLE) en memoire.
		 */
		void precacheAll();

		/**
		 * methode retournant l'ensemble des sons joué par le manager.
		 *
		 * @return int : le nombre de son joué par le manager lors de l'appel.
		 */ 
		int getPlayedSoundNumber();

		/**
		 * methode permettant de savoir si un son du manager est jouable.
		 *
		 * Cette methode est principalement a utiliser lorsque le son desiré est une stream. En effet, les STREAM 
		 * ne peuvent etre joué qu'une seul fois en même temps. il faut donc, avant de faire appel a une methode 
		 * playStaticSound() ou playDynamicSound(), faire appel a cette methode pour savoir si le son est jouable
		 * ou pas.
		 *
		 * @param soundName : le nom logique du son a tester. si le son n'existe pas dans le manager, cette methode 
		 * retourne false.
		 *
		 * @return bool : true si le son peut etre joué, false sinon.
		 */
		bool isPlayable(const string& soundName);

		/**
		 * methode retournant le chemin d'un son en fonction de son nom logique.
		 *
		 * @param soundName : le nom du son dont on veut recuperer le chemin
		 *
		 * @return string : le chemin du son passé en parametre. Si le son ne peut etre trouvé, retourne 
		 * "Sound <soundName> not found"
		 */
		string getPathByName(const string & soundName);

		/**
		 * retourne le chemin du fichier d'un son joué sur le manager.
		 *
		 * @param son : le son dont on veut recuperer le chemin du fichier source
		 *
		 * @return string : le chemin du son passé en parametre. Si le son n'existe pas, la methode retourne
		 * "Sound not found".
		 *
		 * Si le pointeur vaut NULL, la methode retourne la chainne "NULL"
		 */
		string getPathBySound(Sound * son);
	};
}// namespace SoundSystem



#endif
