//	RevolutionaryBehaviour.cpp - How Proles act once they become revolutionary.
//  ----------------------------------------------------------------------------
//	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 "RevolutionaryBehaviour.h"
#include "states/GamePlay.h"
#include "Capitalist.h"
#include "Random.h"

#include <cmath>

using namespace std;

//------------------------------------------------------------------------------
RevolutionaryBehaviour::RevolutionaryBehaviour():
convinceDelay(0)
{
	escapeDirX = Random::getGlobal().getRandomFloat();
	escapeDirY = Random::getGlobal().getRandomFloat();
}

//------------------------------------------------------------------------------
RevolutionaryBehaviour::~RevolutionaryBehaviour()
{
	
}

//------------------------------------------------------------------------------
void RevolutionaryBehaviour::act(Prole *p, GamePlay *gp)
{
	int i;
	float xDir, yDir;
	float tempX, tempY;
	int nearest = -1;
	float x = p->getX();
	float y = p->getY();
	//If it's within 128 units (close enough to 'see').
	//16384 = 128*128; we skip the sqrtf() call.
	float dist = 16384.0f;
	Prole *proles = gp->getProles();
	Capitalist *capitalists = gp->getCapitalists();
	vector<Prole *>& neighbours = p->getNeighbours();

	//Make sure the Prole is red.
	p->setColour(1.0f, 0.0f, 0.0f);
	p->setLifeFade(true);
	p->setTrail(false);

	//If we've lost our neighbours, or we're old and worn out, stop being a revolutionary.
	if(!neighbours.size() || (p->getLife() < 0.2f))
	{
		p->backToWorker(gp);
		return;
	}

	//Attack the nearest 'visible' capitalist.
	nearest = 0;
	dist = 16384.0f;
	for(i=0;i<GamePlay::NumCapitalists;++i)
	{
		tempX = capitalists[i].getX() - x;
		tempY = capitalists[i].getY() - y;

		tempX = (tempX*tempX)+(tempY*tempY);
		if(tempX < dist)
		{
			dist = tempX;
			nearest = i;
		}
	}

	if(dist < 8192.0f) //Attack the nearest 'visible' capitalist.
	{
 		tempX = capitalists[nearest].getX() - x;
		tempY = capitalists[nearest].getY() - y;

		dist = sqrtf((tempX*tempX)+(tempY*tempY));

		xDir = tempX/dist;
		yDir = tempY/dist;

		x += xDir * 2.0f * p->getLife();
		y += yDir * 2.0f * p->getLife();

		if(x < 0.0f)
		{
			x = 0.0f;
			xDir = -xDir;
		}
		else if(x > (1024.0f-32.0f))
		{
			x = 1024.0f-32.0f;
			xDir = -xDir;
		}
		if(y < 0.0f)
		{
			y = 0.0f;
			yDir = -yDir;
		}
		else if(y > (768.0f-32.0f))
		{
			y = 768.0f-32.0f;
			yDir = -yDir;
		}

		p->setPosition(x, y);
		p->setDirection(xDir, yDir);

		return;
	}

	//Find the nearest (non-revolutionary) Prole.
	nearest = -1;
	dist = 16384.0f;
	for(i=0;i<GamePlay::NumProles;++i)
	{
		if((&(proles[i]) != p) &&
		   !proles[i].getRevolutionary() &&
		   (proles[i].getLife() > 0.2f))
		{
			tempX = proles[i].getX() - x;
			tempY = proles[i].getY() - y;

			tempX = (tempX*tempX)+(tempY*tempY);
			if(tempX < dist)
			{
				nearest = i;
				dist = tempX;
				if(Random::getGlobal().getRandomFloat() > 0.66f)
					break; //Try and get related revolutionaries to chase after different workers.
			}
		}
	}
	if(nearest != -1)
		tempX = proles[nearest].getX() - x;
	else
		tempX = 1000000000000000.0f;
	if((nearest != -1) && (tempX < 2048)) //Only move towards Proles that are close enough to be 'seen'.
	{
		tempY = proles[nearest].getY() - y;

		tempX = (tempX*tempX)+(tempY*tempY);
		//256 = 16*16; we skip the sqrtf() call.
		if((tempX < 256.0f) && !convinceDelay) //If we're close enough, convince the prole.
		{
			neighbours.push_back(&(proles[nearest]));
			proles[nearest].convince(p, gp);
			convinceDelay = 15;
		}
		else //Otherwise move towards them.
		{
			tempX = proles[nearest].getX() - x;
			tempY = proles[nearest].getY() - y;
			dist = sqrtf((tempX*tempX) + (tempY*tempY));
			x += (tempX/dist) * 2.0f;
			y += (tempY/dist) * 2.0f;
			xDir = tempX/dist;
			yDir = tempY/dist;
		}
	}
	else if(neighbours.size() > 0)
	{
		float tempf;
		float tX, tY;
		float pX = 0.0f;
		float pY = 0.0f;
		float dX = 0.0f;
		float dY = 0.0f;
		float dX2 = 0.0f;
		float dY2 = 0.0f;
		float overSize = 1.0f/(float)neighbours.size();

		for(i=0;i<neighbours.size();++i)
		{
			pX += neighbours[i]->getX();
			pY += neighbours[i]->getY();
			tX = neighbours[i]->getX() - x;
			tY = neighbours[i]->getY() - y;
			tempf = sqrtf((tX*tX)+(tY*tY));
			if(tempf > 0.0f)
			{
				dX += tX/tempf;
				dY += tY/tempf;
			}
			else
			{
				dX = escapeDirX;
				dY = escapeDirY;
			}

			dX2 += neighbours[i]->getDirX();
			dY2 += neighbours[i]->getDirY();
		}
		dX *= overSize;
		dY *= overSize;
		dX2 *= overSize;
		dY2 *= overSize;
		pX *= overSize;
		pY *= overSize;

		//Steer away from nearby flockmates.
		xDir = -dX;
		yDir = -dY;

		//Steer towards flockmates' average heading.
		xDir += dX2;
		yDir += dY2;

		//Steer towards centre of flockmates.
		pX -= x;
		pY -= y;
		tempf = sqrtf((pX*pX)+(pY*pY));
		if(tempf > 0.0f)
		{
			xDir += pX/tempf;
			yDir += pY/tempf;
		}

		//Normalise the direction vector.
		tempf = sqrtf((xDir*xDir)+(yDir*yDir));
		if(tempf > 0.0f)
		{
			xDir /= tempf;
			yDir /= tempf;
		}

		x += xDir * 2.0f * p->getLife();
		y += yDir * 2.0f * p->getLife();
	}

	if(x < 0.0f)
	{
		x = 0.0f;
		xDir = -xDir;
	}
	else if(x > (1024.0f-32.0f))
	{
		x = 1024.0f-32.0f;
		xDir = -xDir;
	}
	if(y < 0.0f)
	{
		y = 0.0f;
		yDir = -yDir;
	}
	else if(y > (768.0f-32.0f))
	{
		y = 768.0f-32.0f;
		yDir = -yDir;
	}

	p->setPosition(x, y);
	p->setDirection(xDir, yDir);

	if(convinceDelay > 0)
		--convinceDelay;
}
