#include "SoundEngine.h"

using namespace SoundSystem;
#include "cstdio"
#include "fmod/fmod.h"
#ifdef WIN32
#define NOMINMAX // pour novodex
#include <windows.h>
#else
#include "fmod/wincompat.h"
#endif


SoundEngine * SoundEngine::instance = NULL;

SoundEngine::SoundEngine(void)
{
	masterVolume = 255;
	musicManagers = new map<void *,MusicManager*>();
	soundManagers = new map<void *,SoundManager*>();
}

SoundEngine::~SoundEngine(void)
{
	if (musicManagers->size()!= 0)
	{
		fprintf(stderr,"Des MusicManager (%d) n'ont pas été detruits par le SoundEngine... Veuillez verifier la destruction des MusicManager\n",musicManagers->size());
		for (map<void*,MusicManager *>::iterator it = musicManagers->begin();it != musicManagers->end(); it ++)
		{
			it->second->stop();
			delete it->second;
		}
	}
	musicManagers->clear();
	delete musicManagers;
	if (soundManagers->size()!= 0)
	{
		fprintf(stderr,"Des MusicManager (%d) n'ont pas été detruits par le SoundEngine... Veuillez verifier la destruction des MusicManager\n",soundManagers->size());
		for (map<void*,SoundManager *>::iterator it = soundManagers->begin();it != soundManagers->end(); it ++)
		{
			it->second->stopAll();
			delete it->second;
		}
	}
	soundManagers->clear();
	delete soundManagers;
}

SoundEngine &SoundEngine::getInstance()
{
	if (instance == NULL)
	{
		instance = new SoundEngine();
	}
	return *instance;
}

void SoundEngine::destroy()
{
	delete instance;
	instance = NULL;
}

MusicManager * SoundEngine::createMusicManager()
{
	MusicManager * ret;
	try
	{
		ret = new MusicManager();
		(*musicManagers)[ret] = ret;
	}
	catch(...)
	{
		// l'exception peut provenire soit du new, soit de l'affectation dans la Map...
		fprintf(stderr,"Impossible d'instancier un nouveau MusicManager...\n");
		return NULL;
	}
	return ret;
}

void SoundEngine::freeMusicManager(MusicManager * manager)
{
	map<void *,MusicManager *>::iterator it = musicManagers->find(manager);
	if (it != musicManagers->end())
	{
		delete it->second;
		it = musicManagers->erase(it);
	}
	else
	{
		fprintf(stderr,"essai de suppression d'un MusicManager n'etant pas present dans le SoundEngine...\n");
		fflush(stderr);
	}
}

SoundManager * SoundEngine::createSoundManager()
{
	SoundManager * ret;
	try
	{
		ret = new SoundManager();
		(*soundManagers)[ret] = ret;
	}
	catch(...)
	{
		// l'exception peut provenire soit du new, soit de l'affectation dans la Map...
		fprintf(stderr,"Impossible d'instancier un nouveau soundManager...\n");
		return NULL;
	}
	return ret;

}

void SoundEngine::freeSoundManager(SoundManager * manager)
{
	map<void *,SoundManager *>::iterator it = soundManagers->find(manager);
	if (it != soundManagers->end())
	{
		delete it->second;
		it = soundManagers->erase(it);
	}
	else
	{
		fprintf(stderr,"essai de suppression d'un SoundManager n'etant pas present dans le SoundEngine...\n");
		fflush(stderr);
	}

}

void SoundEngine::start(int frequence,int channels,bool foregroundApp,string driver)
{
	int outputType = -1;
	if (driver == "")
	{
		outputType = -1;
	}
	else if (driver == "DSOUND")
	{
		outputType = FSOUND_OUTPUT_DSOUND;
	}
	else if (driver == "WINMM")
	{
		outputType = FSOUND_OUTPUT_WINMM;
	}
	else if (driver == "A3D")
	{
		outputType = FSOUND_OUTPUT_A3D;
	}
	else if (driver == "OSS")
	{
		outputType = FSOUND_OUTPUT_OSS;
	}
	else if (driver == "ESD")
	{
		outputType = FSOUND_OUTPUT_ESD;
	}
	else if (driver == "ALSA")
	{
		outputType = FSOUND_OUTPUT_ALSA;
	}
	else if (driver == "ASIO")
	{
		outputType = FSOUND_OUTPUT_ASIO;
	}
	else if (driver == "NOSOUND")
	{
		outputType = FSOUND_OUTPUT_NOSOUND;
	}
	FSOUND_SetOutput(outputType);
	switch(FSOUND_GetOutput())
	{
	case FSOUND_OUTPUT_DSOUND:
		fprintf(stdout,"SoundEngine::start : le driver selectionné est : FSOUND_OUTPUT_DSOUND\n");
		break;
	case FSOUND_OUTPUT_WINMM:
		fprintf(stdout,"SoundEngine::start : le driver selectionné est : FSOUND_OUTPUT_WINMM\n");
		break;
	case FSOUND_OUTPUT_A3D:
		fprintf(stdout,"SoundEngine::start : le driver selectionné est : FSOUND_OUTPUT_A3D\n");
		break;
	case FSOUND_OUTPUT_OSS:
		fprintf(stdout,"SoundEngine::start : le driver selectionné est : FSOUND_OUTPUT_OSS\n");
		break;
	case FSOUND_OUTPUT_ESD:
		fprintf(stdout,"SoundEngine::start : le driver selectionné est : FSOUND_OUTPUT_ESD\n");
		break;
	case FSOUND_OUTPUT_ALSA:
		fprintf(stdout,"SoundEngine::start : le driver selectionné est : FSOUND_OUTPUT_ALSA\n");
		break;
	case FSOUND_OUTPUT_ASIO:
		fprintf(stdout,"SoundEngine::start : le driver selectionné est : FSOUND_OUTPUT_ASIO\n");
		break;
	case FSOUND_OUTPUT_NOSOUND:
		fprintf(stdout,"SoundEngine::start : le driver selectionné est : FSOUND_OUTPUT_NOSOUND\n");
		break;
	}
	int flags = FSOUND_INIT_DSOUND_DEFERRED;
	if (FSOUND_Init(frequence,channels,flags) == FALSE)
	{
		fprintf(stderr,"Impossible de demarrer FMOD. Aucun son ne sera joué...\n");
		fflush(stderr);
	}
	if (foregroundApp)
	{
		FSOUND_SetHWND(NULL);
	}
	setMasterVolume(masterVolume);
	// on desactive l'attentuation de FMOD pour n'utilise que la notre.
	FSOUND_3D_SetRolloffFactor(0.0f);

}

void SoundEngine::stop()
{
	// on stop tout les music manager afin qu'il ferment leur stream
	for (map<void*,MusicManager *>::iterator it = musicManagers->begin();it != musicManagers->end(); it ++)
	{
		it->second->stop();
	}
	for (map<void*,SoundManager *>::iterator it = soundManagers->begin();it != soundManagers->end(); it ++)
	{
		it->second->stopAll();
	}

	FSOUND_Close();
}

void SoundEngine::setMasterVolume(int volume)
{
	if (volume < 0) volume = 0;
	else if (volume > 255) volume = 255;
	FSOUND_SetSFXMasterVolume(volume);
	masterVolume = volume;
}

int SoundEngine::getMasterVolume()
{
	return masterVolume;
}

bool SoundEngine::canPlaySound()
{
	return (FSOUND_GetChannelsPlaying() < FSOUND_GetMaxChannels());
}

void SoundEngine::update(float x, float y, float z, float dx, float dy, float dz)
{
	float pos[3] = {x,y,z};
	FSOUND_3D_Listener_SetAttributes(pos,NULL,dx,dy,dz,0,1,0);
	FSOUND_Update();
}


