//	Prole.cpp - The prole class.
//  ----------------------------------------------------------------------------
//	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 "Prole.h"
#include "Images.h"
#include "Random.h"
#include "TwoFloats.h"
#include "states/GamePlay.h"
#include "behaviours/WorkerBehaviour.h"
#include "behaviours/RevolutionaryBehaviour.h"
#include "NiallsDrawingLib/Drawer.h"
#include "NiallsDrawingLib/ImageLoader.h"
#include "sound/Sounds.h"

#include <cmath>
#include <algorithm>

using namespace std;

//------------------------------------------------------------------------------
uint32_t Prole::image = 0;
uint32_t Prole::starPuff = 0;

//------------------------------------------------------------------------------
Prole::Prole():
behaviour(0),
bounceCount(0),
revolutionary(false),
revolConvinced(0.0f),
red(1.0f),
green(1.0f),
blue(1.0f),
lifeFade(true),
trail(false),
life(1.0f),
starSize(1.0f),
flash(0.0f)
{
	int i;
	float dist;

	xPos = Random::getGlobal().getRandomFloat() * (1024.0f-32.0f);
	yPos = Random::getGlobal().getRandomFloat() * (768.0f-32.0f);
	xDir = Random::getGlobal().getRandomFloat()-0.5f;
	yDir = Random::getGlobal().getRandomFloat()-0.5f;
	dist = 1.0f/sqrtf((xDir*xDir)+(yDir*yDir));
	if(dist == 0.0f)
	{
		xDir = 0.0f;
		yDir = 1.0f;
	}
	else
	{
		xDir *= dist;
		yDir *= dist;
	}

	strength = 0.1f + ((Random::getGlobal().getRandomFloat()-0.5f) * 0.05f);

	for(i=0;i<TrailSize;++i)
	{
		trailX[i] = xPos;
		trailY[i] = yPos;
	}
}

//------------------------------------------------------------------------------
Prole::~Prole()
{
	if(behaviour)
		delete behaviour;
}

//------------------------------------------------------------------------------
void Prole::setBehaviour(ProleBehaviour *b)
{
	if(behaviour)
		delete behaviour;
	behaviour = b;
}

//------------------------------------------------------------------------------
void Prole::move(GamePlay *gp)
{
	if(!bounceCount)
	{
		if(behaviour)
			behaviour->act(this, gp);
	}
	else
	{
		xPos += xDir * (6.0f * (bounceCount/30.0f));
		yPos += yDir * (6.0f * (bounceCount/30.0f));

		if(xPos < 0.0f)
			xPos = 0.0f;
		else if(xPos > (1024.0f-32.0f))
			xPos = (1024.0f-32.0f);
		if(yPos < 0.0f)
			yPos = 0.0f;
		else if(yPos > (768.0f-32.0f))
			yPos = (768.0f-32.0f);

		--bounceCount;
	}
}

//------------------------------------------------------------------------------
void Prole::bounceFrom(float x, float y)
{
	float tempX = xPos - x;
	float tempY = yPos - y;
	float dist = 1.0f/sqrtf((tempX*tempX) + (tempY*tempY));

	tempX *= dist;
	tempY *= dist;

	xDir = -tempY;
	yDir = tempX;


	bounceCount = 25;
}

//------------------------------------------------------------------------------
void Prole::draw(Drawer *d)
{
	int i;

	//Load images if we need to.
	if(!image)
	{
		ImageLoader imLoad;
		ImageData data;

		data = imLoad.loadImage(Images::prole_png, Images::prole_pngSize);
		image = d->loadImage(data.data, data.width, data.height);

		data = imLoad.loadImage(Images::starpuff_png, Images::starpuff_pngSize);
		starPuff = d->loadImage(data.data, data.width, data.height);
	}

	//Draw the connections to this Prole's revolutionary neighbours.
	/*for(i=(neighbours.size()-1);i>=0;--i)
	{
		if(!neighbours[i]->getRevolutionary())
		{
			neighbours.erase(neighbours.begin()+i);
			continue;
		}
		d->drawLine(xPos+16,
					yPos+16,
					neighbours[i]->getX()+16,
					neighbours[i]->getY()+16,
					1.0f,
					0.0f,
					0.0f,
					neighbours[i]->getLife());
	}*/

	//Draw the trail.
	if(trail)
	{
		for(i=0;i<TrailSize;++i)
		{
			if(i < (TrailSize-1))
			{
				trailX[i] = trailX[i+1];
				trailY[i] = trailY[i+1];
			}

			d->drawImage(image, trailX[i], trailY[i], 1.0f, 1.0f, red, green, blue, (float)i/(float)TrailSize);
		}
		trailX[7] = xPos;
		trailY[7] = yPos;
	}

	//Draw the Prole.
	if(starSize == 1.0f)
	{
		if(lifeFade)
			d->drawImage(image, xPos, yPos, 1.0f, 1.0f, red, green, blue, life);
		else
			d->drawImage(image, xPos, yPos, 1.0f, 1.0f, red, green, blue, 1.0f);

		if(flash > 0.0f)
		{
			d->drawImage(image,
						 xPos,
						 yPos,
						 1.0f+(flash*3.0f),
						 1.0f+(flash*3.0f),
						 red,
						 green,
						 blue,
						 1.0f-flash);
			flash += 0.02f;
			if(flash > 1.0f)
				flash = 0.02f;
		}
	}
	else if(starSize > 0.5f)
		d->drawImage(image, xPos, yPos, (starSize-0.5f)*2.0f, (starSize-0.5f)*2.0f, 1.0f, 1.0f, 1.0f, life);

	//Draw the birth star.
	if(starSize < 1.0f)
	{
		starSize += 0.05f;
		if(starSize > 1.0f)
			starSize = 1.0f;

		d->drawImage(starPuff, (xPos-16), (yPos-16), starSize, starSize, 1.0f, 1.0f, 1.0f, (1.0f-starSize));
	}
}

//------------------------------------------------------------------------------
void Prole::triggerFlash()
{
	flash = 0.02f;
}

//------------------------------------------------------------------------------
void Prole::setPosition(float x, float y)
{
	xPos = x;
	yPos = y;
}

//------------------------------------------------------------------------------
void Prole::setDirection(float x, float y)
{
	xDir = x;
	yDir = y;
}

//------------------------------------------------------------------------------
void Prole::setRevolutionary(bool val)
{
	revolutionary = val;
}

//------------------------------------------------------------------------------
void Prole::setColour(float r, float g, float b)
{
	red = r;
	green = g;
	blue = b;
}

//------------------------------------------------------------------------------
void Prole::setLifeFade(bool val)
{
	lifeFade = val;
}

//------------------------------------------------------------------------------
void Prole::setTrail(bool val)
{
	trail = val;
}

//------------------------------------------------------------------------------
void Prole::resetLife(GamePlay *gp)
{
	int i;

	life = 1.0f;

	xPos = Random::getGlobal().getRandomFloat() * (1024.0f-32.0f);
	yPos = Random::getGlobal().getRandomFloat() * (768.0f-32.0f);

	revolutionary = false;
	revolConvinced = 0.0f;

	for(i=0;i<neighbours.size();++i)
	{
		neighbours[i]->removeNeighbour(this);
		gp->removeRevolConnection(this, neighbours[i]);
	}
	neighbours.clear();

	if(behaviour)
		delete behaviour;
	behaviour = new WorkerBehaviour();

	starSize = 0.0f;

	Sounds::getInstance().playSound(Sounds::Birth);
}

//------------------------------------------------------------------------------
bool Prole::reduceLife(float val)
{
	bool retval = false;

	life -= ((strength * (1.0f/(1.0f+((float)neighbours.size()*0.125f)))) * val);
	//life -= strength;
	if(life <= 0.0f)
	{
		retval = true;
		//Sounds::getInstance().playSound(Sounds::Death);
	}

	return retval;
}

//------------------------------------------------------------------------------
void Prole::setStrength(float val)
{
	strength = val;
}

//------------------------------------------------------------------------------
void Prole::convince(Prole *p, GamePlay *gp)
{
	float tempf;

	revolConvinced += 0.4f;
	if(revolConvinced >= 1.0f)
	{
		revolutionary = true;
		neighbours.push_back(p);
		gp->addRevolConnection(this, p);

		delete behaviour;
		behaviour = new RevolutionaryBehaviour();

		xDir = Random::getGlobal().getRandomFloat();
		yDir = Random::getGlobal().getRandomFloat();
		tempf = 1.0f/sqrtf((xDir*xDir)+(yDir*yDir));
		xDir *= tempf;
		yDir *= tempf;

		xPos += xDir * 8.0f;
		yPos += yDir * 8.0f;

		Sounds::getInstance().playSound(Sounds::TurnRevolutionary);
	}
}

//------------------------------------------------------------------------------
void Prole::backToWorker(GamePlay *gp)
{
	int i;

	revolutionary = false;
	revolConvinced = 0.0f;

	for(i=0;i<neighbours.size();++i)
	{
		neighbours[i]->removeNeighbour(this);
		gp->removeRevolConnection(this, neighbours[i]);
	}
	neighbours.clear();

	if(behaviour)
		delete behaviour;
	behaviour = new WorkerBehaviour();
}

//------------------------------------------------------------------------------
void Prole::removeNeighbour(Prole *p)
{
	vector<Prole *>::iterator it = find(neighbours.begin(), neighbours.end(), p);

	if(it != neighbours.end())
		neighbours.erase(it);
}
