//	GamePlay.cpp - The main gameplay state.
//  ----------------------------------------------------------------------------
//	This file is part of 'The Future Will Only Be Dystopian if Capitalism
//	Remains Undefeated', a game.
//	Copyright (C) 2009  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 "GamePlay.h"
#include "Random.h"
#include "Images.h"
#include "Debilitation.h"
#include "GameCounter.h"

#include "behaviours/WorkerBehaviour.h"
#include "behaviours/PlayerBehaviour.h"
#include "behaviours/PoliceBehaviour.h"

#include "NiallsDrawingLib/ImageLoader.h"
#include "NiallsDrawingLib/Drawer.h"

#include "sound/Sounds.h"

#include <cmath>
#include <iostream>
#include <algorithm>

using namespace std;

//------------------------------------------------------------------------------
GamePlay::GamePlay(Application *app):
GameState(app),
background(0),
helpText1(0),
helpText2(0),
playerLines(1.0f),
endGame(0.0f),
helpFade1(3.0f),
helpFade2(3.0f)
{
	int i;
	int x, y;

	whichWorld = (int)(Random::getGlobal().getRandomFloat()*12.5f);

	//Setup some random capitalist positions.
	for(i=0;i<NumCapitalists;++i)
	{
		x = 192 + (288*(i%3)) - 32;
		x += (int)((Random::getGlobal().getRandomFloat()-0.5f) * 256.0f);
		y = 192 + (320*(i%2)) - 32;
		y += (int)((Random::getGlobal().getRandomFloat()-0.5f) * 256.0f);
		capitalists[i].setPosition(x, y);
		capitalists[i].setGamePlay(this);
	}

	//Setup the Proles' behaviours.
	for(i=0;i<NumProles;++i)
		proles[i].setBehaviour(new WorkerBehaviour());

	//Add the player's character.
	player = new PlayerBehaviour(); //This will be deleted when the Prole dies or is deleted themselves.
	proles[0].setBehaviour(player);
	proles[0].setRevolutionary(true);
	proles[0].triggerFlash();

	//Setup the positions where worlds may be drawn.
	for(y=0;y<4;++y)
	{
		for(x=0;x<6;++x)
		{
			World w;

			w.x = (x*256)-128;
			w.y = (y*256)-128;

			possiblePositions.push_back(w);
		}
	}
	for(y=0;y<3;++y)
	{
		for(x=0;x<4;++x)
		{
			World w;

			w.x = (x*256);
			w.y = (y*256);

			possiblePositions.push_back(w);
		}
	}
}

//------------------------------------------------------------------------------
GamePlay::~GamePlay()
{
	int i;

	for(i=0;i<debilitations.size();++i)
		delete debilitations[i];
}

//------------------------------------------------------------------------------
void GamePlay::draw(Drawer *d)
{
	int i, j;
	float xP, yP, xW, yW;
	float dist;

	if(!background)
	{
		ImageLoader imLoad;
		ImageData data;

		data = imLoad.loadImage(Images::background_png, Images::background_pngSize);
		background = d->loadImage(data.data, data.width, data.height);

		data = imLoad.loadImage(Images::possibleworlds_png, Images::possibleworlds_pngSize);
		possibleWorlds = d->loadImage(data.data, data.width, data.height);

		i = 0;
		data = imLoad.loadImage(Images::world1_png, Images::world1_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world2_png, Images::world2_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world3_png, Images::world3_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world4_png, Images::world4_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world5_png, Images::world5_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world6_png, Images::world6_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world7_png, Images::world7_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world8_png, Images::world8_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world9_png, Images::world9_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world10_png, Images::world10_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world11_png, Images::world11_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::world12_png, Images::world12_pngSize);
		worlds[i] = d->loadImage(data.data, data.width, data.height);
		++i;

		data = imLoad.loadImage(Images::helptext1_png, Images::helptext1_pngSize);
		helpText1 = d->loadImage(data.data, data.width, data.height);

		data = imLoad.loadImage(Images::helptext2_png, Images::helptext2_pngSize);
		helpText2 = d->loadImage(data.data, data.width, data.height);
	}

	//Move the proles.
	for(i=0;i<NumProles;++i)
		proles[i].move(this);
	//Move the capitalists.
	for(i=0;i<NumCapitalists;++i)
		capitalists[i].move();
	//Calculate prole-capitalist collisions.
	capitalistCollisions();
	//Move the debilitation waves.
	for(i=(debilitations.size()-1);i>=0;--i)
	{
		if(debilitations[i]->move())
		{
			//Reduce the life of any Proles that are within the wave.
			xW = (float)debilitations[i]->getX();
			yW = (float)debilitations[i]->getY();
			for(j=0;j<NumProles;++j)
			{
				xP = proles[j].getX()+16.0f - xW;
				yP = proles[j].getY()+16.0f - yW;

				dist = sqrtf((xP*xP) + (yP*yP));
				if(dist < 768.0f)
				{
					if(proles[j].reduceLife(debilitations[i]->getStrength()))
					{
						proles[j].resetLife(this);
						if(j == 0)
						{
							player = new PlayerBehaviour(); //This will be deleted when the Prole dies or is deleted themselves.
							proles[0].setBehaviour(player);
							proles[0].setRevolutionary(true);
							playerLines = 1.0f;
						}
					}
				}
			}

			delete debilitations[i];
			debilitations.erase(debilitations.begin() + i);
		}
	}

	//Draw the background.
	d->drawImage(background, 0, 0);

	//Draw the world fragments.
	for(i=0;i<worldPositions.size();++i)
	{
		if(worldPositions[i].fade < 1.0f)
			worldPositions[i].fade += 0.005f;

		d->drawImage(worldPositions[i].image,
					 worldPositions[i].x,
					 worldPositions[i].y,
					 1.5f,
					 1.5f,
					 1.0f,
					 1.0f,
					 1.0f,
					 worldPositions[i].fade);
	}

	//Draw the player lines if necessary.
	if(playerLines > 0.0f)
	{
		xP = proles[0].getX() + 16.0f;
		yP = proles[0].getY() + 16.0f;

		//Left.
		d->drawLine(0.0f, yP, xP, yP, 1.0f, 0.0f, 0.0f, playerLines);
		//Right.
		d->drawLine(xP, yP, 1024.0f, yP, 1.0f, 0.0f, 0.0f, playerLines);
		//Top.
		d->drawLine(xP, 0.0f, xP, yP, 1.0f, 0.0f, 0.0f, playerLines);
		//Bottom.
		d->drawLine(xP, yP, xP, 768.0f, 1.0f, 0.0f, 0.0f, playerLines);

		playerLines -= 0.01f;
	}

	//Draw the revolutionary connections.
	for(i=0;i<revolConnections.size();++i)
	{
		d->drawLine(revolConnections[i].p1->getX()+16,
					revolConnections[i].p1->getY()+16,
					revolConnections[i].p2->getX()+16,
					revolConnections[i].p2->getY()+16,
					1.0f,
					0.0f,
					0.0f,
					revolConnections[i].p2->getLife());
	}

	//Draw the proles.
	for(i=0;i<NumProles;++i)
		proles[i].draw(d);

	//Draw the debilitation waves.
	for(i=0;i<debilitations.size();++i)
		debilitations[i]->draw(d);

	//Draw the capitalists.
	for(i=0;i<NumCapitalists;++i)
		capitalists[i].draw(d);

	//Draw the endgame if necessary.
	if(endGame > 0.0f)
	{
		d->drawImage(possibleWorlds, 0, 0, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, endGame);
		endGame += 0.005f;
	}

	//Draw the help text if necessary.
	if(helpFade1 > 1.0f)
	{
		d->drawImage(helpText1, 0, (768-224));
		helpFade1 -= 0.01f;
	}
	else if(helpFade1 > 0.0f)
	{
		d->drawImage(helpText1, 0, (768-224) - ((1.0f-helpFade1)*64.0f), 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, helpFade1);
		helpFade1 -= 0.015f;
	}
	else if(helpFade2 > 2.0f)
	{
		d->drawImage(helpText2, 0, (768-224), 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, (1.0f-(helpFade2-2.0f)));
		helpFade2 -= 0.01f;
	}
	else if(helpFade2 > 1.0f)
	{
		d->drawImage(helpText2, 0, (768-224));
		helpFade2 -= 0.01f;
	}
	else if(helpFade2 > 0.0f)
	{
		d->drawImage(helpText2, 0, (768-224) - ((1.0f-helpFade2)*64.0f), 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, helpFade2);
		helpFade2 -= 0.015f;
	}
}

//------------------------------------------------------------------------------
void GamePlay::keyDown(const SDL_keysym& keyPressed)
{
	if((keyPressed.sym == SDLK_ESCAPE) || (endGame >= 1.0f))
		quit();
	/*else if(keyPressed.sym == SDLK_f)
		proles[0].triggerFlash();*/
	else if(player)
		player->keyDown(keyPressed);
}

//------------------------------------------------------------------------------
void GamePlay::keyUp(const SDL_keysym& keyPressed)
{
	if(player)
		player->keyUp(keyPressed);
}

//------------------------------------------------------------------------------
void GamePlay::mouseDown(int button, int posX, int posY)
{
	
}

//------------------------------------------------------------------------------
void GamePlay::mouseMove(int posX, int posY)
{
	
}

//------------------------------------------------------------------------------
void GamePlay::addWave(int x, int y, float val)
{
	Debilitation *d = new Debilitation(x, y, val);

	debilitations.push_back(d);
}

//------------------------------------------------------------------------------
void GamePlay::addRevolConnection(Prole *p1, Prole *p2)
{
	RevolConnection conn;

	conn.p1 = p1;
	conn.p2 = p2;

	revolConnections.push_back(conn);
}

//------------------------------------------------------------------------------
void GamePlay::removeRevolConnection(Prole *p1, Prole *p2)
{
	int i;

	for(i=0;i<revolConnections.size();++i)
	{
		if((revolConnections[i].p1 == p1) && (revolConnections[i].p2 == p2))
			revolConnections.erase(revolConnections.begin()+i);
		else if((revolConnections[i].p1 == p2) && (revolConnections[i].p2 == p1))
			revolConnections.erase(revolConnections.begin()+i);
	}
}

//------------------------------------------------------------------------------
void GamePlay::killPlayer()
{
	proles[0].resetLife(this);
	player = new PlayerBehaviour();
	proles[0].setBehaviour(player);
	proles[0].setRevolutionary(true);
	playerLines = 1.0f;
}

//------------------------------------------------------------------------------
void GamePlay::capitalistCollisions()
{
	int i, j;
	float pX, pY;
	float cX, cY;

	for(i=0;i<NumProles;++i)
	{
		pX = proles[i].getX()+16.0f;
		pY = proles[i].getY()+16.0f;
		for(j=0;j<NumCapitalists;++j)
		{
			cX = capitalists[j].getX()+32.0f - pX;
			cY = capitalists[j].getY()+32.0f - pY;

			//1024 = 32*32
			if(((cX*cX)+(cY*cY)) < 1024.0f)
			{
				proles[i].bounceFrom(cX+pX, cY+pY);

				if(proles[i].getRevolutionary() &&
				   (proles[i].getNeighbours().size() > 0))
				{
					float dist = sqrtf((cX*cX)+(cY*cY));
					float tempf = proles[i].getNeighbours().size() * 0.005f;

					if(tempf > 0.02f)
						tempf = 0.02f;

					if(capitalists[j].reduceLife(tempf, (cX/dist), (cY/dist)))
					{
						generateWorlds();
						GameCounter::getInstance().incrementCount("DeadCapitalists");
						if(GameCounter::getInstance().getCount("DeadCapitalists") > 5)
							endGame = 0.05f;

						Sounds::getInstance().playSound(Sounds::CapitalistDeath);
					}
				}
				//Randomly convert Workers to Police.
				else if(!proles[i].getRevolutionary() &&
						(Random::getGlobal().getRandomFloat() > 0.75f) &&
						(PoliceBehaviour::numPolice < 10))
				{
					PoliceBehaviour *b = new PoliceBehaviour();

					proles[i].setBehaviour(b);

					//Sounds::getInstance().playSound(Sounds::CapitalistCollision);
				}
				//else
					//Sounds::getInstance().playSound(Sounds::CapitalistCollision);
				break;
			}
		}
	}
}

//------------------------------------------------------------------------------
void GamePlay::generateWorlds()
{
	int tempint;
	int imageCount = 6;

	while(imageCount >= 0)
	{
		World w;

		if(possiblePositions.empty())
			break;

		tempint = (int)(Random::getGlobal().getRandomFloat() * (float)possiblePositions.size());

		w = possiblePositions[tempint];
		w.image = worlds[whichWorld];
		w.fade = 0.0f;
		worldPositions.push_back(w);

		possiblePositions.erase(possiblePositions.begin()+tempint);

		--imageCount;

		++whichWorld;
		whichWorld %= 12;
	}
}
