//	GamePlayState.cpp - The main gameplay state.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#include "DwindlingSounder.h"
#include "drawing/Drawer.h"
#include "GamePlayState.h"
#include "MiscHelper.h"

#include "World0.h"
#include "World1.h"
#include "World2.h"
#include "World3.h"
#include "World4.h"
#include "World5.h"
#include "World6.h"
#include "World7.h"

using namespace std;

//------------------------------------------------------------------------------
GameStateCreator<GamePlayState> GamePlayState::creator;

//------------------------------------------------------------------------------
GamePlayState::GamePlayState(Application *app, Sounder *sound):
GameState(app, sound),
endInterp(0.0f),
endJump(0.0f),
endCount(1600),
backingIndex(0),
worldIndex(0),
worldInterp(0.0f),
viewPos(640.0f, 360.0f),
flash(0.0f),
flashWait(false),
fadeIn(1.0f),
fadeOut(0.0f),
endScreen(false),
sounder((DwindlingSounder *)sound)
{
	worlds[0] = WorldPtr(new World0());
	worlds[1] = WorldPtr(new World1());
	worlds[2] = WorldPtr(new World2());
	worlds[3] = WorldPtr(new World3());
	worlds[4] = WorldPtr(new World4());
	worlds[5] = WorldPtr(new World5());
	worlds[6] = WorldPtr(new World6());
	worlds[7] = WorldPtr(new World7());

	backingTracks[0] = L"ClackyThing";
	backingTracks[1] = L"HandDrum";
	backingTracks[2] = L"Scraper";
	backingTracks[3] = L"";
	backingTracks[4] = L"DanglyDrumThing";
	backingTracks[5] = L"FingerBell";
	backingTracks[6] = L"RainStick";
	backingTracks[7] = L"SingingBowl";

	speechTracks[0] = L"Speech_World0";
	speechTracks[1] = L"Speech_World1";
	speechTracks[2] = L"Speech_World2";
	speechTracks[3] = L"Speech_World3";
	speechTracks[4] = L"Speech_World4";
	speechTracks[5] = L"Speech_World5";
	speechTracks[6] = L"Speech_World6";
	speechTracks[7] = L"Speech_World7";

	backingIndex = sounder->activateSound(backingTracks[worldIndex]);
	sounder->triggerSound(speechTracks[0]);
	sounder->setCallback(this);

	endPos[0] = TwoFloats((640.0f/5000.0f)*1280.0f, ((360.0f+1000.0f)/1800.0f)*720.0f);
	endPos[1] = TwoFloats((1920.0f/5000.0f)*1280.0f, ((0.0f+1000.0f)/1800.0f)*720.0f);
	endPos[2] = TwoFloats((2640.0f/5000.0f)*1280.0f, ((360.0f+720.0f)/1800.0f)*720.0f);
	endPos[3] = TwoFloats((3920.0f/5000.0f)*1280.0f, ((640.0f+1000.0f)/1800.0f)*720.0f);
	endPos[4] = TwoFloats((4560.0f/5000.0f)*1280.0f, ((-80.0f+1000.0f)/1800.0f)*720.0f);
	endPos[5] = TwoFloats((4000.0f/5000.0f)*1280.0f, ((-800.0f+1000.0f)/1800.0f)*720.0f);
	endPos[6] = TwoFloats((2720.0f/5000.0f)*1280.0f, ((-900.0f+1000.0f)/1800.0f)*720.0f);
	endPos[7] = TwoFloats((1696.0f/5000.0f)*1280.0f, ((-700.0f+1000.0f)/1800.0f)*720.0f);
}

//------------------------------------------------------------------------------
GamePlayState::~GamePlayState()
{

}

//------------------------------------------------------------------------------
void GamePlayState::draw(DrawerPtr d)
{
	int i;
	float tempf;
	float tempSize;
	TwoFloats tempPos;
	ThreeFloats scales;
	ThreeFloats backCol;
	const ThreeFloats black;
	const ThreeFloats white(1.0f);
	const TwoFloats origin(0.0f);
	const TwoFloats startPos(640.0f, 360.0f);
	const TwoFloats viewTL(viewPos - (d->getDrawingAreaSize()*0.5f));
	const TwoFloats viewBR(viewTL + d->getDrawingAreaSize());
	
	//Draw background.
	if(worldInterp > 0.0f)
	{
		backCol = worlds[worldIndex]->getBackgroundCol() * (1.0f - worldInterp);
		backCol += worlds[worldIndex+1]->getBackgroundCol() * worldInterp;
	}
	else
		backCol = worlds[worldIndex]->getBackgroundCol();

	d->fillRect(origin, d->getDrawingAreaSize(), backCol);

	//Draw world(s).
	for(i=0;i<8;++i)
	{
		scales = worlds[i]->getScales();
		tempSize = worlds[i]->getOverallScale() * 1024.0f;
		tempPos = worlds[i]->getRing1Pos() - (tempSize*0.5f);

		if((((tempPos.x+tempSize) > viewTL.x) && (tempPos.x < viewBR.x)) &&
		   (((tempPos.y+tempSize) > viewTL.y) && (tempPos.y < viewBR.y)))
		{
			tempPos = worlds[i]->getRing1Pos();
			tempSize = worlds[i]->getOverallScale() * scales.x * 512.0f;
			d->drawImage(L"Circle",
						 (tempPos - tempSize) - viewTL,
						 TwoFloats(tempSize/512.0f, tempSize/512.0f),
						 worlds[i]->getRing1Col(),
						 0.9f);
			
			tempPos = worlds[i]->getRing2Pos();
			tempSize = worlds[i]->getOverallScale() * scales.y * 512.0f;
			d->drawImage(L"Circle",
						 (tempPos - tempSize) - viewTL,
						 TwoFloats(tempSize/512.0f, tempSize/512.0f),
						 worlds[i]->getRing2Col(),
						 0.9f);
			
			tempPos = worlds[i]->getRing3Pos();
			tempSize = worlds[i]->getOverallScale() * scales.z * 512.0f;
			d->drawImage(L"Circle",
						 (tempPos - tempSize) - viewTL,
						 TwoFloats(tempSize/512.0f, tempSize/512.0f),
						 worlds[i]->getRing3Col(),
						 0.9f);

			if(!endScreen)
				worlds[i]->tick();
		}
	}

	if(endScreen)
	{
		if(viewPos != startPos)
		{
			viewPos += (startPos-viewPos) * 0.1f;
			if(viewPos.getDistance(startPos) < 4.0f)
				viewPos = startPos;
		}

		if(endInterp < 1.0f)
		{
			endInterp += 0.002f;

			for(i=0;i<8;++i)
			{
				tempPos = worlds[i]->getStartPosition() * (1.0f-endInterp);
				tempPos += endPos[i] * endInterp;

				worlds[i]->setPosition(tempPos);
				worlds[i]->setOverallScale(0.2f + ((1.0f-endInterp)*0.8f));
			}
		}
		else if(endJump > 0.0f)
		{
			endJump += 0.02f;
			tempf = 1.0f - ((cosf(endJump * 2.0f * NIALL_PI)*0.5f)+0.5f);

			tempf *= 0.1f;
			tempf += 0.2f;
			for(i=0;i<8;++i)
				worlds[i]->setOverallScale(tempf);

			if(endJump > 1.0f)
				endJump = 0.0f;
		}

		if(flash <= 1.0f)
		{
			flash += 0.03f;

			tempf = 1.0f - ((cosf(flash * 2.0f * NIALL_PI)*0.5f) + 0.5f);
			d->fillRect(origin, d->getDrawingAreaSize(), white, tempf);
		}

		--endCount;
		if(endCount <= 0)
		{
			fadeOut += 0.01f;
			sounder->stopMusic();
		}
	}
	else
	{
		if(worldInterp > 0.0f)
		{
			worldInterp += 0.01f;
			if(worldInterp >= 1.0f)
			{
				worldInterp = 0.0f;
				++worldIndex;
				sounder->setNoiseLevel(1.0f - ((float)worldIndex/8.0f));

				sounder->deactivateSound(backingIndex);
				backingIndex = sounder->activateSound(backingTracks[worldIndex]);
				sounder->triggerSound(speechTracks[worldIndex]);
				if(worldIndex == 3)
					sounder->playMusic(L"Music");
			}

			tempf = (1.0f - ((cosf(worldInterp * NIALL_PI)*0.5f)+0.5f));
			viewPos = worlds[worldIndex]->getStartPosition() * (1.0f-tempf);
			if(worldIndex < 7)
				viewPos += worlds[worldIndex+1]->getStartPosition() * tempf;
		}
		else if(worlds[worldIndex]->worldDone() && (worldIndex < 7))
		{
			if(flashWait)
			{
				flash += 0.03f;
				if(flash >= 1.0f)
				{
					worldInterp = 0.01f;
					flash = 0.0f;
					flashWait = false;
				}
			}
			else
			{
				flashWait = true;
				flash = 0.03f;
			}

			tempf = 1.0f - ((cosf(flash * 2.0f * NIALL_PI)*0.5f) + 0.5f);
			d->fillRect(origin, d->getDrawingAreaSize(), white, tempf);
		}
		else if((fadeOut <= 0.0f) && worlds[worldIndex]->worldDone())
		{
			tempf = 1.0f - ((cosf(flash * 2.0f * NIALL_PI)*0.5f) + 0.5f);
			d->fillRect(origin, d->getDrawingAreaSize(), white, tempf);

			if(flashWait)
			{
				flash += 0.03f;
				if(flash >= 1.0f)
				{
					//fadeOut = 0.02f;
					sounder->setNoiseLevel(0.0f);
					sounder->deactivateSound(backingIndex);
					//sounder->stopMusic();
					endScreen = true;
					flash = 0.0f;
					flashWait = false;
					worldIndex = 7;

					//Fix World 3's ring colours.
					{
						ThreeFloats ringCols[3];

						ringCols[0] = ThreeFloats(253.0f/255.0f, 149.0f/255.0f, 0.0f/255.0f);
						ringCols[1] = ThreeFloats(4.0f/255.0f, 7.0f/255.0f, 61.0f/255.0f);
						ringCols[2] = ThreeFloats(163.0f/255.0f, 159.0f/255.0f, 255.0f/255.0f);
						worlds[3]->setRingColours(ringCols);
					}
				}
			}
			else
			{
				flashWait = true;
				flash = 0.03f;
			}
		}
	}

	if(fadeIn > 0.0f)
	{
		d->fillRect(origin, d->getDrawingAreaSize(), black, fadeIn);

		fadeIn -= 0.02f;
	}
	else if(fadeOut > 0.0f)
	{
		d->fillRect(origin, d->getDrawingAreaSize(), black, fadeOut);

		fadeOut += 0.00066f;
		if(fadeOut > 1.0f)
			quit();
	}
}

//------------------------------------------------------------------------------
void GamePlayState::keyDown(const wstring& action,
							const wstring& keyPressed,
							int keyModifier)
{
	if(action == L"Escape")
	{
		if(endScreen)
		{
			fadeOut += 0.01f;
			sounder->stopMusic();
		}
		else
			quit();
	}
#ifdef DEBUG
	else if(action == L"P1Right")
		worldInterp = 0.01f;
#endif
}

//------------------------------------------------------------------------------
void GamePlayState::mouseMove(const TwoFloats& pos)
{
	if(worldInterp == 0.0f)
		worlds[worldIndex]->mouseMove(pos);
}

//------------------------------------------------------------------------------
void GamePlayState::barStart()
{
	int i;

	for(i=0;i<8;++i)
		worlds[i]->barStart();
	if(endScreen)
		endJump = 0.02f;
}
