////////////////////////////////////////////////
//	File	:	"CNoteManager.h"
//
//	Author	:	Alyssa Dettling (AD)
//
//	Purpose	:	To contain and manage all of our notes
/////////////////////////////////////////////////
#include "CNoteManager.h"
#include "CBase.h"
#include "CNote.h"
#include "CGamePlayState.h"
#include "CGame.h"
#include "CSGD_EventSystem.h"
#include "CSGD_MessageSystem.h"
#include "CSGD_FModManager.h"
#include "CWinGameState.h"
#include <fstream>
#include "Debug.h"
#include "CPlayer.h"

CNoteManager* CNoteManager::sm_pInstance = NULL;

#define EASY_MODIFIER 70.8142857
#define MEDIUM_MODIFIER 50.8142857
#define HARD_MODIFIER 90.8142857

#define EASY_DELAY 0.9
#define MEDIUM_DELAY 0.7
#define HARD_DELAY 0.5

#define DISSONANCE_FACTOR 60
#define BASE_VELOCITY -250

CNoteManager* CNoteManager::GetInstance(void)
{
	if (!sm_pInstance)
		sm_pInstance = new CNoteManager;
	return sm_pInstance;
}

void CNoteManager::DeleteInstance(void)
{
	if (sm_pInstance)
	{
		delete sm_pInstance;
		sm_pInstance = NULL;
	}
}

CNoteManager::CNoteManager(void)
{
	//resize the dead list to 80 and reserve 80 worth of space so it won't grow unless it reaches capacity
	m_vDeadList.resize(500 );

	//pre-allocate space for our good notes
	for(int i=0; i<NUM_GOOD_NOTES; ++i)
	{
		CNote* pGoodNote = new CNote();

		pGoodNote->SetType(CBase::OBJECT_GOOD_NOTE);

		m_vDeadList.push_back(pGoodNote);
	}

	//pre-allocate space for our bad notes
	for(int i=0; i<NUM_BAD_NOTES; ++i)
	{
		CNote* pBadNote =  new CNote();

		pBadNote->SetType(CBase::OBJECT_BAD_NOTE);

		m_vDeadList.push_back(pBadNote);
	}

	CSGD_EventSystem* pES = CSGD_EventSystem::GetInstance();
	pES->RegisterClient("BadNoteMissed", this);
	pES->RegisterClient("GoodNoteHit", this);
	pES->RegisterClient("BadNoteHit", this);

	m_nSoundID = -1;
}

CNoteManager::~CNoteManager(void)
{
	CSGD_EventSystem::GetInstance()->UnregisterClient(this);
}

void CNoteManager::UpdateObjects(float fElapsedTime)
{
	static float delayCounter = 0;
	static float endTimer = 0;
	CSGD_FModManager* pFM = CSGD_FModManager::GetInstance();
	if(m_nSoundID != -1 && pFM->IsSoundPlaying(m_nSoundID))
	{
		Channel* pChannel;
		unsigned int position;
		unsigned int randNum;
		float volume;
		pChannel = pFM->GetChannel(m_nSoundID);
		pChannel->getPosition(&position, FMOD_TIMEUNIT_MS);

		delayCounter += fElapsedTime;

		if(delayCounter >= m_fUpdateDelay && m_vBeatList.size() && position >= m_vBeatList.front())
		{
			delayCounter = 0.0f;
			float percent;
			if(m_vDeadList.size() == 0)
			{
				for(int i=0; i<NUM_GOOD_NOTES; ++i)
				{
					CBase* pNote =  new CNote();

					m_vDeadList.push_back(pNote);
				}
			}
			switch(m_nCurrentState)
			{
			case INTRO:
				SendOutGoodNote(position);
//				pFM->PlayNoise(m_nSoundID, 1.0f, 50);
				if((position/(float)m_nLengthOfSong) >= 0.05f)
				{
					m_fStartLERP = (float)(m_nLengthOfSong*0.05f*0.001f);
					m_fEndLERP = (float)(m_nLengthOfSong*0.1f*0.001f);
					m_nCurrentState = RISE;
					CONSOLEMSG << "Entering Rise State...\n\n";
				}
				break;
			case RISE:
				SendOutNote(position);
				percent = (Lerp(m_fStartLERP, m_fEndLERP, ((float)(position*0.001f) - m_fStartLERP)/(m_fEndLERP - m_fStartLERP)) - m_fStartLERP) / (m_fEndLERP - m_fStartLERP);
				//pFM->PlayNoise(m_nSoundID, 1.0f, (int)(percent*DISSONANCE_FACTOR));
				if((position/(float)m_nLengthOfSong) >= 0.1f)
				{
					m_nCurrentState = MIDDLE;
					CONSOLEMSG << "Entering Middle State...\n\n";
				}
				break;
			case MIDDLE:
				SendOutNote(position);
				randNum = rand() % 10;
				/*if(randNum < 2)
					pFM->PlayNoise(m_nSoundID, 1.0f, 120);
				else 
					pFM->PlayNoise(m_nSoundID, 1.0f, 0);*/

				if((position/(float)m_nLengthOfSong) >= 0.65f)
					m_nCurrentState = FALL;
				break;
			case FALL:
				SendOutNote(position);
				percent = (Lerp(m_fEndLERP, m_fStartLERP, ((float)(position*0.001f) - m_fStartLERP)/(m_fEndLERP - m_fStartLERP)) - m_fStartLERP) / (m_fEndLERP - m_fStartLERP);
				percent = 1 - (percent*-1);
				//pFM->PlayNoise(m_nSoundID, 1.0f, (int)(percent*DISSONANCE_FACTOR));
				if((position/(float)m_nLengthOfSong) >= 0.15f)
				{
					pFM->GetChannel(m_nSoundID)->getVolume(&m_fStartVolume);
					//pFM->PlayNoise(m_nSoundID, 1.0f, 0);
					m_nCurrentState = END;
					CONSOLEMSG << "Entering END State...\n\n";
				}
				break;
			case END:
				endTimer += fElapsedTime;
				if(endTimer > 10)
				{
					float progress = 1 - (FLOAT)(((float)m_nHitWhite + (float)m_nMissedBlack)/(float)m_nNotesCreated);
					switch(m_nDifficulty)
					{
					case EASY:
						if(progress >= 0.6f)
							m_bWin = true;
						else
							m_bWin = false;

						CGame::GetInstance()->AddState(CWinGameState::GetInstance());
						
						break;
					case MEDIUM:
						if(progress >= 0.7f)
							m_bWin = true;
						else
							m_bWin = false;

							CGame::GetInstance()->AddState(CWinGameState::GetInstance());
						break;
					case HARD:
						if(progress >= 0.8f)
							m_bWin = true;
						else
							m_bWin = false;
							
						CGame::GetInstance()->AddState(CWinGameState::GetInstance());
						
						break;
					}
				}
				/// transition to win or lose!
				// NOT WORKING
			/*	pFM->GetChannel(m_nSoundID)->getVolume(&volume);
				volume = Lerp(m_fStartVolume, 0.0f, volume/m_fStartVolume);
				volume = 1 - (volume*-1);
				pFM->SetVolume(m_nSoundID, volume);*/
				break;
				
			}
		}
	}	
}


void CNoteManager::AddNote(CNote* pObject)
{
	m_vDeadList.push_back(pObject);
}


void CNoteManager::RemoveAllObjects(void)
{
	for(unsigned int i=0 ;i<m_vDeadList.size(); ++i)
	{
		delete m_vDeadList[i];
	}

	m_vDeadList.clear();
}

void CNoteManager::LoadFile1(char* pFileName, unsigned int difficulty)
{
	CSGD_FModManager* pFM = CSGD_FModManager::GetInstance();
	std::size_t length;
	unsigned int beat;
	char szBuffer[256] = {};
	m_nDifficulty = difficulty;
	std::string cName = pFileName;
	cName += ".bin";
	std::fstream fin(cName.c_str(), std::ios_base::binary | std::ios_base::in);
	/*fin.read((char*)&length, sizeof(std::size_t));
	fin.read(szBuffer, sizeof(szBuffer));*/
	m_nSoundID = CSGD_FModManager::GetInstance()->LoadSound(pFileName);
	fin.read((char*)&length, sizeof(std::size_t));
	for(std::size_t i=0; i<length; ++i)
	{
		fin.read((char*)&beat, sizeof(unsigned int));
		m_vBeatList.push_back(beat);
	}
	/*char pLocation[256] = {};
	strcpy_s(pLocation, strlen(pSongLocation)+1, pSongLocation);
	fin.read(pSongLocation, sizeof(pLocation));*/
	fin.close();

	switch(m_nDifficulty)
	{
	case EASY:
		m_fUpdateDelay = (float)EASY_DELAY;
		beatAnalyzer = new CBeatAnalyzer(m_nSoundID, EASY_MODIFIER);
		break;
	case MEDIUM:
		m_fUpdateDelay = (float)MEDIUM_DELAY;
		beatAnalyzer = new CBeatAnalyzer(m_nSoundID, MEDIUM_MODIFIER);
		break;
	case HARD:
		m_fUpdateDelay = (float)HARD_DELAY;
		beatAnalyzer = new CBeatAnalyzer(m_nSoundID, HARD_MODIFIER);
		break;
	}

	m_nLengthOfSong = pFM->GetSoundLength(m_nSoundID);
	m_nCurrentState = INTRO;
	CONSOLEMSG << "Entering Intro State...\n\n";

	m_nMissedBlack = 0;
	m_nHitWhite = 0;
	m_nNotesCreated = 0;

	if(!pFM->IsSoundPlaying(m_nSoundID))
		pFM->PlaySoundA(m_nSoundID);
}

void CNoteManager::UnloadFile(void)
{
	CSGD_FModManager::GetInstance()->UnloadSound(m_nSoundID);
	m_vBeatList.clear();
	delete beatAnalyzer;
}

void CNoteManager::SendOutGoodNote(unsigned int position)
{
	unsigned num = rand() % 4;
	unsigned velMod = rand() % 200;
	switch(num)
	{
	case 0:
		CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateGoodNoteMessage(m_vDeadList.back(), 800, 29, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
		break;
	case 1:
		CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateGoodNoteMessage(m_vDeadList.back(), 800, 139, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
		break;
	case 2: 
		CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateGoodNoteMessage(m_vDeadList.back(), 800, 249, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
		break;
	case 3: 
		CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateGoodNoteMessage(m_vDeadList.back(), 800, 359, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
		break;
	}

	++m_nNotesCreated;
	m_vDeadList.pop_back();
	while(m_vBeatList.size() && m_vBeatList.front() < position)
		m_vBeatList.pop_front();
}

void CNoteManager::SendOutNote(unsigned int position)
{
	unsigned goodNoteChance = 20 + m_nDifficulty*5;
	unsigned noteRand = rand()%100;
	unsigned num = rand() % 4;
	unsigned velMod = rand() % 200;
	
	if(noteRand <= goodNoteChance)
	{
		switch(num)
		{
		case 0:
			CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateGoodNoteMessage(m_vDeadList.back(), 800, 29, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
			break;
		case 1:
			CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateGoodNoteMessage(m_vDeadList.back(), 800, 139, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
			break;
		case 2: 
			CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateGoodNoteMessage(m_vDeadList.back(), 800, 249, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
			break;
		case 3: 
			CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateGoodNoteMessage(m_vDeadList.back(), 800, 359, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
			break;
		}
	}
	else
	{
		switch(num)
		{
		case 0:
			CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateBadNoteMessage(m_vDeadList.back(), 800, 29, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
			break;
		case 1:
			CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateBadNoteMessage(m_vDeadList.back(), 800, 139, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
			break;
		case 2: 
			CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateBadNoteMessage(m_vDeadList.back(), 800, 249, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
			break;
		case 3: 
			CSGD_MessageSystem::GetInstance()->SendMsg(new CCreateBadNoteMessage(m_vDeadList.back(), 800, 359, BASE_VELOCITY + (float)velMod, CNote::EIGHTH_NOTE));
			break;
		}
	}

	++m_nNotesCreated;
	m_vDeadList.pop_back();
	while(m_vBeatList.size() && m_vBeatList.front() < position)
		m_vBeatList.pop_front();
}

void CNoteManager::HandleEvent(CEvent* pEvent)
{
	if(pEvent->GetEventID() == "GoodNoteHit")
	{		
		CPlayer::GetInstance()->GetCurrAnimation()->SetSpriteFile("Sad.png");	
		CPlayer::GetInstance()->GetCurrAnimation()->SetCurrentAnim("Sad"); 	
		++m_nHitWhite;		
		
		unsigned int *badNotes = new unsigned int;
		unsigned int *totalNotes = new unsigned int;
		*badNotes = m_nHitWhite + m_nMissedBlack;
		*totalNotes = m_nNotesCreated;
		CSGD_EventSystem::GetInstance()->SendEvent("UpdateProgressBar", (void*)(badNotes), (void*)totalNotes);
		
	}
	else if(pEvent->GetEventID() == "BadNoteMissed")
	{
		++m_nMissedBlack;
		unsigned int *badNotes = new unsigned int;
		unsigned int *totalNotes = new unsigned int;
		*badNotes = m_nHitWhite + m_nMissedBlack;
		*totalNotes = m_nNotesCreated;
		CSGD_EventSystem::GetInstance()->SendEvent("UpdateProgressBar", (void*)(badNotes), (void*)totalNotes);
	}
	else 
		if(pEvent->GetEventID() == "BadNoteHit")	
		{		
			CPlayer::GetInstance()->GetCurrAnimation()->SetSpriteFile("Happy.png");		
			CPlayer::GetInstance()->GetCurrAnimation()->SetCurrentAnim("Happy"); 	

			unsigned int *badNotes = new unsigned int;
		unsigned int *totalNotes = new unsigned int;
		*badNotes = m_nHitWhite + m_nMissedBlack;
		*totalNotes = m_nNotesCreated;
		CSGD_EventSystem::GetInstance()->SendEvent("UpdateProgressBar", (void*)(badNotes), (void*)totalNotes);
		}	
}

bool CNoteManager::GetTempoEstimate(unsigned int &tempo)
{
	std::list<unsigned int>::iterator pIter = m_vBeatList.begin();
	if(m_vBeatList.size() < 2)
		return false;
	else
	{
		tempo = *pIter;
		++pIter;
		tempo = *pIter - tempo;
		return true;
	}
}

bool CNoteManager::GetPercentSongDone(float &percentage)
{
	CSGD_FModManager* pFM = CSGD_FModManager::GetInstance();
	if(pFM->IsSoundPlaying(m_nSoundID))
	{
		Channel* pChannel = pFM->GetChannel(m_nSoundID);
		unsigned int position;
		pChannel->getPosition(&position, FMOD_TIMEUNIT_MS);
		unsigned int length = pFM->GetSoundLength(m_nSoundID);	
		percentage = position / ((float)length);

		return true;
	}
	else
		return false;
}
