#include "SoundManager.h"
#include "DebugUtil.h"

#include "SDL.h"

#include "TimeUtility.h"

#include "ISound.h"
#include "Song.h"

#include "InputEvents.h"
#include "EventManager.h"

#include "GameConfig.h"
#include "GameException.h"

#define MAX_WAVE_DIFF 20000

template<> SoundManager* TSingleton<SoundManager>::m_pInstance = NULL;

SoundManager::SoundManager()
: m_lastUpdate(0), m_volume(SOUND_MAX_VOLUME), m_pAudioSpec(0)
{

}

SoundManager::~SoundManager()
{
}

void SoundManager::Initialise(int freq /* = 44100 */, Uint16 samples /* = 1024 */, Uint8 channels /* = 1 */)
{
	m_pAudioSpec = new SDL_AudioSpec();
	SDL_AudioSpec* pDesiredSpec = new SDL_AudioSpec();

	pDesiredSpec->freq = freq;
	pDesiredSpec->format = AUDIO_S16;
	pDesiredSpec->samples = samples;
	pDesiredSpec->channels = channels;
	pDesiredSpec->callback = SoundManager::SDLAudioCallback;
	pDesiredSpec->userdata = 0;

	if(SDL_OpenAudio(pDesiredSpec, m_pAudioSpec) == -1)
	{
		throw GameException(eMajorError, "SoundManager: SDL_OpenAudio failed!");
		return; 
	}

	SDL_PauseAudio(false);

	EventManager::getSingleton()->AddEventListener(ButtonInputEvent::cEventName, this);
}

void SoundManager::Shutdown()
{
	SDL_CloseAudio();
	delete m_pAudioSpec;

	for(size_t i = 0; i < m_vSounds.size(); i++)
	{
		if(m_vSounds[i])
			delete m_vSounds[i];
	}

	m_vSounds.clear();
}

void SoundManager::Output()
{
	for(size_t i = 0; i < m_vSounds.size(); i++)
	{
		if(m_vSounds[i])
			m_vSounds[i]->Output();
	}
}

SoundID SoundManager::CreateSoundSong(const bool &bPreCreate, const UInt &bpm, const UInt &numUniqueSections, const UInt &lengthInSections, const UInt &timePerSection)
{
	Song* pSong = new Song();

	pSong->Initialise(bPreCreate, bpm, numUniqueSections, lengthInSections, timePerSection);

	m_vSounds.push_back((ISound*)pSong);

	return m_vSounds.size() - 1;
}

SoundInstanceID SoundManager::PlaySoundID(const SoundID &sid, bool loop)
{
	if(!m_vSounds[sid])
	{
		throw GameException(eMinorError, "SoundManager: PlaySoundID failed on Sound ID: %d", (int)sid);
		return 0; //Throw exception
	}
	
	SoundInstance* pInstance = new SoundInstance();

	pInstance->pSound = m_vSounds[sid];
	pInstance->isPaused = false;
	pInstance->isLooping = loop;
	pInstance->position = 0;

	m_vInstances.push_back(pInstance);

	return m_vInstances.size() - 1;
}

bool SoundManager::PauseSound(const SoundInstanceID &siid)
{
	if(m_vInstances[siid])
	{
		m_vInstances[siid]->isPaused = !m_vInstances[siid]->isPaused;

		return true;
	}

	return false;
}

bool SoundManager::StopSound(const SoundInstanceID &siid)
{
	if(m_vInstances[siid])
	{
		delete m_vInstances[siid];
		m_vInstances[siid] = 0;

		return true;
	}

	return false;
}

void SoundManager::Update()
{
// 	unsigned long dt = TimeUtility::getTime() - m_lastUpdate;
// 	for(int i = 0; i < m_vInstances.size(); i++)
// 	{
// 		if(m_vInstances[i] && m_vInstances[i]->isPaused != true)
// 			m_vInstances[i]->position += dt;
// 	}


}

void SoundManager::MixBuffers(Sint16 *pBuf1, Sint16 *pBuf2, size_t sz)
{
	for(size_t i = 0; i < sz; i++)
	{
		long int val = pBuf1[i] + pBuf2[i];
		if(val > 32767)
			val = 32767;
		if(val < -32768)
			val = -32768;

		pBuf1[i] = (Sint16)val;
	}
}

void SoundManager::HandleAudioBuffer(void* userdata, Uint8* stream, int len)
{
	unsigned long dt = TimeUtility::getTime() - m_lastUpdate;

	memset(stream, 0, len * sizeof(Uint8));
	Sint16* pRealStream = (Sint16*)stream;
	
	int rlen = len / sizeof(Sint16);
	Sint16* pTmpStream = new Sint16[rlen];

	double secsPerSampleset = (double)m_pAudioSpec->samples / (double)m_pAudioSpec->freq;

	for(size_t i = 0; i < m_vInstances.size(); i++)
	{
		if(m_vInstances[i])
		{
			SoundInstance* pInst = m_vInstances[i];
			if(pInst->isPaused == false)
			{
				m_vInstances[i]->position += dt;
				Sint16* pBuffer = pInst->pSound->GetBuffer(pInst->position, rlen, pInst->isLooping);
				if(pBuffer)
					MixBuffers(pRealStream, pBuffer, rlen);
					//SDL_MixAudio((Uint8*)pTmpStream)
				else
				{
					delete pInst;
					pInst = 0;
				}
			}
		}
	}

	ApplyVolumeToStream(pRealStream, rlen);
	m_lastUpdate = TimeUtility::getTime();

//	memcpy((Uint8*)pRealStream, (Uint8*)pTmpStream, rlen * sizeof(Sint16));
}

void SoundManager::IncreaseVolume()
{
	m_volume += SOUND_VOLUME_STEP;
	if(m_volume > SOUND_MAX_VOLUME)
		m_volume = SOUND_MAX_VOLUME;
}

void SoundManager::DecreaseVolume()
{
	m_volume -= SOUND_VOLUME_STEP;
	if(m_volume < 0)
		m_volume = 0;
}

bool SoundManager::HandleEvent(IEvent* pEvent)
{
	if(pEvent->GetEventType() == ButtonInputEvent::cEventName)
	{
		ButtonInputEvent* pButtonEvent = (ButtonInputEvent*)pEvent;
		int key = pButtonEvent->GetKey();

		if(key == eKeyButtonVolumeUp && pButtonEvent->IsKeyDown())
		{
			DebugUtil::outputDebugText("Volume Up Received!\n");
			IncreaseVolume();
			return true;
		}
		
		if(key == eKeyButtonVolumeDown && pButtonEvent->IsKeyDown())
		{
			DebugUtil::outputDebugText("Volume Down Received!\n");
			DecreaseVolume();
			return true;
		}
	}

	return false;
}

void SoundManager::SDLAudioCallback(void *userdata, Uint8 *stream, int len)
{
	SoundManager::getSingleton()->HandleAudioBuffer(userdata, stream, len);
}

void SoundManager::ApplyVolumeToStream(Sint16 *pStream, size_t sz)
{
	for(size_t i = 0; i < sz; i++)
	{
		pStream[i] = (pStream[i] * m_volume) / SOUND_MAX_VOLUME;
	}
}
