#include "SoundManager.h"

#include "cstdio"
#include "StreamDescriptor.h"
#include "SampleDescriptor.h"


using namespace SoundSystem;


SoundManager::SoundManager(void)
{
	precache = false;
}

SoundManager::~SoundManager(void)
{
	// on supprime les staticSound
	clearStaticSound();
	if (playedSounds.size() > 0)
	{
		fprintf(stderr,"SoundManager : %d son on été joué sans jamais etre nettoyé...\n",playedSounds.size());
		// on les liberes de la memoire
		for(map<void*,Sound *>::iterator it = playedSounds.begin(); it != playedSounds.end(); it++)
		{
			fprintf(stderr,"\t%s : %s\n",it->second->getName().c_str(),getPathByName(it->second->getName()).c_str());
			delete it->second;
		}
		fflush(stderr);
		// et on nettoie la liste.
		playedSounds.clear();
	}
	// on supprime tout les SoundDescriptors. ceci a pour effet de stopper tout les sons joué sur les descripteurs.
	clear();
}

void SoundManager::addSound(const string & name,const string& path,DescriptorType type)
{
	SoundDescriptor * desc;
	switch (type)
	{
	case STREAM:
		desc = new StreamDescriptor();
		break;
	case SAMPLE:
		desc  = new SampleDescriptor();
		break;
	default:
		fprintf(stderr,"SoundManager::addSound(\"%s\",\"%s\",%d) error : parametre type invalide.Aborting program\n",path.c_str(),name.c_str(),type);
		fflush(stderr);
		abort();
	}
	desc->setPath(path);
	if (usePrecaching() && type == SAMPLE)
	{
		desc->load(false);
	}
	descripteurs[name] = desc;
}

bool SoundManager::soundExist(const string& soundName)
{
	return (descripteurs.find(soundName) != descripteurs.end());
}

void SoundManager::removeSound(const string &soundName)
{
	if (soundExist(soundName))
	{
		map<string,SoundDescriptor *>::iterator it = descripteurs.find(soundName);
		// destruction du SoundDescriptor
		delete it->second;
		it = descripteurs.erase(it);
	}
	else
	{
		fprintf(stderr,"essaie de supression d'un son n'etant pas dans le manager.\n");
	}
}

void SoundManager::clear()
{
	// on commence par stopper tout les son qui sont en train d'etre joué
	stopAll();
	// on libere la memoire alloué par les descripteurs
	for (map<string,SoundDescriptor*>::iterator it = descripteurs.begin(); it != descripteurs.end(); it++)
	{
		delete it->second;
	}
	// et on vide la liste des descripteurs.
	descripteurs.clear();
}

void SoundManager::pauseAll(bool pause)
{
	for (map<void*,Sound *>::iterator it = playedSounds.begin(); it != playedSounds.end(); it++)
	{
		it->second->pause(pause);
	}

}

void SoundManager::update(float x,float y, float z)
{
	// on supprime les sons statique qui ne sont pas en train de jouer
	for (list<Sound*>::iterator it = staticSounds.begin(); it != staticSounds.end(); it++)
	{
		if (!(*it)->isPlaying())
		{
			freeSound(*it);
			it = staticSounds.erase(it);
			if (it == staticSounds.end()){
				break;
			}
		}
	}
	for (map<void*,Sound *>::iterator it = playedSounds.begin(); it != playedSounds.end(); it++)
	{
		it->second->update(x,y,z);
	}

}

void SoundManager::stopAll()
{
	for (map<void*,Sound *>::iterator it = playedSounds.begin(); it != playedSounds.end(); it++)
	{
		it->second->stop();
	}
}

Sound * SoundManager::playDynamicSound(const string& soundName,SoundConfig &config)
{
	if (!soundExist(soundName)) return NULL;
	// on est desormais sur que le son existe dans la map.
	map<string,SoundDescriptor *>::iterator it = descripteurs.find(soundName);
	SoundDescriptor * descriptor = it->second;
	if (!descriptor->canPlay()) return NULL;
	// maintenant on sait que le son peut etre joué
	Sound *ret = new Sound();
	int channel;
	switch (descriptor->getType())
	{
	case STREAM:
		// on stop le stream
		descriptor->free();
		// on le recharge en memoire
		if(config.loop)
		{
			printf("manager loop\n");
		}
		else
		{
			printf("manager non loop\n");
		}
		descriptor->load(config.loop);
		channel = descriptor->play();
		ret->setChannel(channel);
		break;
	case SAMPLE:
		channel = descriptor->play();
		ret->setChannel(channel);
		ret->setLoop(config.loop);
		break;
	}
	ret->setName(soundName);
	ret->setPosition(config.x,config.y,config.z);
	ret->setVolume(config.volume);
	ret->setMinMax(config.min,config.max);
	ret->set3DSound(config.is3D);
	playedSounds[(void*)ret] = ret;
	return ret;
}

bool SoundManager::playStaticSound(const string& soundName,SoundConfig &config)
{
	bool ret = false;
	Sound * son = playDynamicSound(soundName,config);
	if (son != NULL)
	{
		ret = true;
		staticSounds.push_back(son);
	}
	else
	{
		fprintf(stderr,"cant start staticSound %s\n",soundName.c_str());
	}
	return ret;
}

void SoundManager::freeSound(Sound * &son)
{
	if (son == NULL) return;
	map<void*,Sound *>::iterator it = playedSounds.find((void*)son);
	if (it != playedSounds.end())
	{
		delete it->second;
		it = playedSounds.erase(it);
		// le son a été supprimé, on met le pointeur a NULL
		son = NULL;
	}
	else
	{
		fprintf(stderr,"tentative de supression d'un son non present dans le SoundManager\n");
		fflush(stderr);
	}
}

void SoundManager::setPrecaching(bool precaching)
{
	precache = precaching;
}

bool SoundManager::usePrecaching()
{
	return precache;
}

void SoundManager::precacheAll()
{
	for (map<string,SoundDescriptor*>::iterator it = descripteurs.begin(); it != descripteurs.end(); it++)
	{
		if (it->second->getType() == SAMPLE && !it->second->isLoaded())
		{
			it->second->load(false);
		}
	}
}

void SoundManager::clearStaticSound()
{
	// on libere chaque son
	for (list<Sound*>::iterator it = staticSounds.begin(); it != staticSounds.end(); it++)
	{
		freeSound(*it);
	}
	// et on vide le liste.
	staticSounds.clear();
}

int SoundManager::getPlayedSoundNumber()
{
	return playedSounds.size();
}

bool SoundManager::isPlayable(const string& soundName)
{
	if (!soundExist(soundName))return false;
	return descripteurs.find(soundName)->second->canPlay();
}

string SoundManager::getPathByName(const string & soundName)
{
	if (!soundExist(soundName)) return (string("Sound \"") + soundName + "\" not found");
	return descripteurs.find(soundName)->second->getPath();
}

string SoundManager::getPathBySound(Sound * son)
{
	if (son == NULL) return "NULL";
	if (playedSounds.find((void*)son) == playedSounds.end()) return "Sound not found";
	return getPathByName(son->getName());
}

