#include "BreakoutApp.h"

#include "GameConfig.h"
#include "BreakoutLGLines.h"
#include "EventManager.h"
#include "InputEvents.h"
#include "ApplicationEvents.h"
#include "BreakoutEvents.h"

#include "InputHandler.h"
#include "SoundManager.h"

#include "BreakoutGameScene.h"
#include "BreakoutSplashScreenScene.h"
#include "BreakoutMainMenuScene.h"
#include "BreakoutPauseScene.h"
#include "BreakoutGameOverScene.h"
#include "BreakoutErrorScene.h"

#include "Profiler.h"
#include "GameException.h"
#include <iostream>

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

BreakoutApp::BreakoutApp()
: m_currentScene(eSplashScreenScene), m_bQuitGame(false), m_pScreen(0), m_bgMusicID(0)
{
	for(int i = 0; i < eNumSceneTypes; i++)
		m_sceneArray[i] = 0;
}

BreakoutApp::~BreakoutApp()
{
}

void BreakoutApp::Run()
{
	SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_AUDIO);
	
	try
	{
		Initialise();
	}
	catch(GameException e)
	{
		DebugUtil::outputDebugText("BreakoutApp: Error occured during initialisation, quitting game.\n");
		m_bQuitGame = true;
	}
	
	if(!m_bQuitGame)
	{
		ChangeScene(eMainMenuScene);
		SoundManager::getSingleton()->PlaySoundID(m_bgMusicID, true);
	}

	unsigned int lastUpdate = 0, lastFPSUpdate = 0, numFramesThisSecond = 0;
	while(!m_bQuitGame) 
	{
		try
		{			
			InputHandler::getSingleton()->Update();
			if(lastUpdate + GAME_TICK_RATE < SDL_GetTicks())
			{
				EventManager::getSingleton()->ProcessQueue();
				Update();
				lastUpdate = SDL_GetTicks();
			}

			SoundManager::getSingleton()->Update();
			
			Draw();
			numFramesThisSecond++;

			if(lastFPSUpdate + 1000 < SDL_GetTicks())
			{
				DebugUtil::outputDebugText("Frames Per Second: %d\n", (int)numFramesThisSecond);
				numFramesThisSecond = 0;
				lastFPSUpdate = SDL_GetTicks();
			}
		}
		catch(GameException e)
		{
			switch(e.GetSeverity())
			{
				case eFatalError:
					m_bQuitGame = true;
					break;
				
				case eMinorError:
				case eMajorError:
					if(m_currentScene != eErrorScene)
						ChangeScene(eErrorScene);
					break;
			}
		}
	}

	Shutdown();
}

void BreakoutApp::Draw()
{
	if(!m_bQuitGame)
	{
		m_sceneArray[m_currentScene]->Draw();
		SDL_Flip(m_pScreen);
	}
}

void BreakoutApp::Update()
{	
	if(!m_bQuitGame)
	{
		m_sceneArray[m_currentScene]->Update();

		InputHandler* pInput = InputHandler::getSingleton();
		if(pInput->IsKeyDown(eKeyButtonL) && pInput->IsKeyDown(eKeyButtonR) && pInput->IsKeyDown(eKeyButtonStart))
			m_bQuitGame = true;
	}
}

void BreakoutApp::Initialise()
{
	Profiler* pProfile = new Profiler("Visualisation");
	pProfile->Start();
	Visualisation::getSingleton()->Initialise(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH);
	pProfile->End();
	pProfile->PrintResults();
	delete pProfile;
	DebugUtil::outputDebugText("Visualisation initialised.\n");

#ifdef _WIN32
	SDL_WM_SetCaption("Break Down!", 0);
#endif

	m_pScreen = Visualisation::getSingleton()->GetScreen();

	pProfile = new Profiler("SoundManager");
	pProfile->Start();
	SoundManager::getSingleton()->Initialise(22050, 512);
	pProfile->End();
	pProfile->PrintResults();
	delete pProfile;
	DebugUtil::outputDebugText("SoundManager initialised.\n");

	pProfile = new Profiler("Breakout Scenes");
	pProfile->Start();
	InitialiseScenes();
	pProfile->End();
	pProfile->PrintResults();
	delete pProfile;
	DebugUtil::outputDebugText("Breakout scenes initialised.\n");	

	//Draw splash screen once while song creation happens, this is due to song creation being
	//rather slow.
	m_sceneArray[m_currentScene]->Activate();
	Draw();

	pProfile = new Profiler("Song Creation");
	pProfile->Start();
	m_bgMusicID = SoundManager::getSingleton()->CreateSoundSong(true, 60, 5, 12, 4000);
	pProfile->End();
	pProfile->PrintResults();
	delete pProfile;
	DebugUtil::outputDebugText("Test song created.\n");

 	for(int i = 0; i < NUM_BOX_COLOURS; i++)
	{
		ColourRGB randomColour = ColourRGB::GetRandomColour();
		ColourRGB darkerRColour = randomColour;
		darkerRColour.r /= 2;
		darkerRColour.g /= 2;
		darkerRColour.b /= 2;
		SpriteID sprID = Visualisation::getSingleton()->CreateSpriteBox(MEDIUM_BOX_WIDTH, MEDIUM_BOX_HEIGHT, darkerRColour,  randomColour, 2);
 		m_vBoxSprites.push_back(sprID);	
	}

/*	EventManager::getSingleton()->AddEventListener(ButtonInputEvent::cEventName, this);*/ //Uncomment for exception testing
	EventManager::getSingleton()->AddEventListener(ApplicationQuitEvent::cEventName, this);
	EventManager::getSingleton()->AddEventListener(BreakoutStartGameEvent::cEventName, this);
	EventManager::getSingleton()->AddEventListener(BreakoutPauseGameEvent::cEventName, this);
	EventManager::getSingleton()->AddEventListener(BreakoutUnpauseGameEvent::cEventName, this);
	EventManager::getSingleton()->AddEventListener(BreakoutGameOverEvent::cEventName, this);

	unsigned int splashStartTime = SDL_GetTicks();
	while(splashStartTime + SPLASH_SCREEN_TIME > SDL_GetTicks())
		Draw();
}

void BreakoutApp::InitialiseScenes()
{
	m_sceneArray[eGameScene] = new BreakoutGameScene();
	DebugUtil::outputDebugText("Game Scene Created.\n");

 	m_sceneArray[eMainMenuScene] = new BreakoutMainMenuScene();
	DebugUtil::outputDebugText("Menu Scene Created.\n");

 	m_sceneArray[eGameOverScene] = new BreakoutGameOverScene();
	DebugUtil::outputDebugText("Game Over Scene Created.\n");

	m_sceneArray[ePauseScene] = new BreakoutPauseScene();
	DebugUtil::outputDebugText("Pause Scene Created.\n");

 	m_sceneArray[eSplashScreenScene] = new BreakoutSplashScreenScene();
	DebugUtil::outputDebugText("Splash Screen Scene Created.\n");

	m_sceneArray[eErrorScene] = new BreakoutErrorScene();
	DebugUtil::outputDebugText("Error Scene Created.\n");

	m_sceneArray[eGameScene]->Initialise();
	DebugUtil::outputDebugText("Game Scene Initialised.\n");
	m_sceneArray[eMainMenuScene]->Initialise();
	DebugUtil::outputDebugText("Menu Scene Initialised.\n");
	m_sceneArray[ePauseScene]->Initialise();
	DebugUtil::outputDebugText("Pause Scene Initialised.\n");
	m_sceneArray[eGameOverScene]->Initialise();
	DebugUtil::outputDebugText("Game Over Scene Initialised.\n");
	m_sceneArray[eSplashScreenScene]->Initialise();
	DebugUtil::outputDebugText("Splash Screen Scene Initialised.\n");
	m_sceneArray[eErrorScene]->Initialise();
	DebugUtil::outputDebugText("Error Scene Initialised.\n");


// 	for(int i = 0; i < eNumSceneTypes; i++)
//  		m_sceneArray[i]->Initialise();
}

void BreakoutApp::Shutdown()
{
	for(int i = 0; i < eNumSceneTypes; i++)
	{
		if(m_sceneArray[i])
		{
			delete m_sceneArray[i];
			m_sceneArray[i] = 0;
		}
	}

	SoundManager::getSingleton()->Shutdown();
	SoundManager::deleteSingleton();

	Visualisation::getSingleton()->Shutdown();
	Visualisation::deleteSingleton();

	GameException::OutputLog(std::cout);
}

bool BreakoutApp::HandleEvent(IEvent* pEvent)
{
	HashString evtType = pEvent->GetEventType();
	if(evtType == ApplicationQuitEvent::cEventName)
	{
		m_bQuitGame = true;
		return true;
	}

	if(evtType == BreakoutStartGameEvent::cEventName)
	{
		if(m_currentScene != eGameScene)
		{
			BreakoutGameScene* pGameScene = (BreakoutGameScene*)m_sceneArray[eGameScene];
			m_sceneArray[m_currentScene]->Deactivate();
			pGameScene->Reset();
			pGameScene->Activate();
			m_currentScene = eGameScene;
		}

		return true;
	}

	if(evtType == BreakoutPauseGameEvent::cEventName)
	{
		if(m_currentScene != ePauseScene)
			ChangeScene(ePauseScene);

		return true;
	}

	if(evtType == BreakoutUnpauseGameEvent::cEventName)
	{
		if(m_currentScene == ePauseScene || m_currentScene == eErrorScene)
			ChangeScene(eGameScene);

		return true;
	}

	if(evtType == BreakoutGameOverEvent::cEventName)
	{
		if(m_currentScene != eGameOverScene)
			ChangeScene(eGameOverScene);

		return true;
	}


// 	//Exception testing.
// 	if(pEvent->GetEventType() == ButtonInputEvent::cEventName)
// 	{
// 		ButtonInputEvent* pButtonEvent = (ButtonInputEvent*)pEvent;
// 		int key = pButtonEvent->GetKey();
// 		if(key == eKeyButtonSelect)
// 		{
// 			throw GameException(eMajorError, "Test Exception!");
// 			return true;
// 		}
// 	}

	return false;
}

void BreakoutApp::ChangeScene(const eSceneType &sceneType)
{
	m_sceneArray[m_currentScene]->Deactivate();
	m_currentScene = sceneType;
	m_sceneArray[m_currentScene]->Activate();
}
