/*
Solar Conquest
Copyright (C) 2006 Greg Beaman

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 2 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#define PROJECTILE_CACHE_SIZE 3000

class CProjectile
{
private:
	Vector startPosition;
	Vector position;
	float angle;

	//Lunatic settings
	float strafeDelay;
	int strafeDir;

	int team;

	CProjectileData* data;

public:
	float lifeTime;
	float smokeTime;
	int input_turn; //for seekers
	
	void* target;
	CProjectile* prevObj;
	CProjectile* nextObj;
	
	CProjectile(CProjectileData* projData);
	~CProjectile();
	void Reset(CProjectileData* projData);

	void Update(float elapsedTime);
	void PhysicsUpdate(float elapsedTime);
	void Render(int cameraX, int cameraY);
	
	void SetTeam(int t);
	void SetPosition(float x, float y);
	void SetPosition(Vector pos);
	void SetAngle(float nAngle);
	
	CProjectileData* GetData();
	int GetHealth();
	int GetState();
	int GetTeam();
	float GetSpeed();
	Vector GetPosition();
	float GetAngle();
};

CProjectile::CProjectile(CProjectileData* projData)
{
	Reset(projData);
}

CProjectile::~CProjectile()
{
}

void CProjectile::Reset(CProjectileData* projData)
{
	if (projData)
	{
		data = projData;
		lifeTime = data->lifeTime;
	}
	else
		data = NULL;

	position = CreateVector(0,0);
	angle = 0;
	
	team = 0;
	
	smokeTime = 0;
	input_turn = 0;

	strafeDelay = 0;
	strafeDir = 0;
	
	prevObj = NULL;
	nextObj = NULL;
	target = NULL;
}

void CProjectile::Update(float elapsedTime)
{
	if (lifeTime > 0)
		lifeTime -= elapsedTime;
//		lifeTime -= elapsedTime/100;
	if (smokeTime > 0)
		smokeTime -= elapsedTime;
//		smokeTime -= elapsedTime/100;
	
	PhysicsUpdate(elapsedTime);
}

void CProjectile::PhysicsUpdate(float elapsedTime)
{
	if (elapsedTime <= 0)
		return;

	angle -= (input_turn * data->turnRate) * elapsedTime;
	angle = CapAngle(angle);

	if (data->strafeSpeed > 0)
	{
		strafeDelay -= elapsedTime;
		if (strafeDelay <= 0)
		{
			strafeDelay = rand()%8 * 0.1;
			if (strafeDir < 0)
				strafeDir = 1;
			else
				strafeDir = -1;
		}
		position.x += (GetSin(angle+(3.14/2))*data->strafeSpeed*strafeDir) * elapsedTime;
		position.y += (GetCos(angle+(3.14/2))*data->strafeSpeed*strafeDir) * elapsedTime;
	}
//This is for inaccurate missiles/rockets, not seekers
/*	if (data->turnRate > 0)
	{
		turnDelay -= elapsedTime;
		if (turnDelay <= 0)
		{
			turnDelay = rand()%5 * 0.1;
			if (input_turn < 0)
				input_turn = 1;
			else
				input_turn = -1;
		}
	}*/
	
	position.x += (GetSin(angle)*data->maxSpeed) * elapsedTime;
	position.y += (GetCos(angle)*data->maxSpeed) * elapsedTime;
}

void CProjectile::Render(int cameraX, int cameraY)
{
	if (Distance(cameraX+(g_screenWidth/2),cameraY+(g_screenHeight/2),position.x,position.y) <= ((g_screenWidth)+data->radius))
	{
//		glColor4f(data->colorR,data->colorG,data->colorB,data->colorA);
		if (data->type == PROJECTILE_TYPE_BULLET)
		{
			glBegin (GL_QUADS);
				float afact = data->colorA/2;
				float i;
				for (int z=1; z <= 3; z++)
				{
					i = z * 0.5;
					glColor4f(data->colorR,data->colorG,data->colorB,data->colorA-(afact*i));
					glVertex2f((position.x-i) - cameraX,(position.y-i) - cameraY);
					glVertex2f((position.x+i) - cameraX,(position.y-i) - cameraY);
					glVertex2f((position.x+i) - cameraX,(position.y+i) - cameraY);
					glVertex2f((position.x-i) - cameraX,(position.y+i) - cameraY);
				}
			glEnd();
		}
		else if (data->type == PROJECTILE_TYPE_ROCKET)
		{
/*			
			glBegin (GL_LINES);
				glVertex2f(((GetSin(angle) * 8) + position.x) - cameraX,((GetCos(angle) * 8) + position.y) - cameraY);
				glVertex2f(((GetSin(angle) * -8) + position.x) - cameraX,((GetCos(angle) * -8) + position.y) - cameraY);
			glEnd();
*/
			glBegin(GL_TRIANGLE_STRIP);
				glColor4f(data->colorR,data->colorG,data->colorB,0);
				//135
				glVertex2f((GetSin(angle-ToRadians(150))*10)+(position.x-cameraX),(GetCos(angle-ToRadians(150))*10)+(position.y-cameraY));
				glVertex2f((GetSin(angle+ToRadians(150))*10)+(position.x-cameraX),(GetCos(angle+ToRadians(150))*10)+(position.y-cameraY));
				glColor4f(data->colorR,data->colorG,data->colorB,data->colorA);
				glVertex2f((GetSin(angle)*12)+(position.x-cameraX),(GetCos(angle)*12)+(position.y-cameraY));
//				glVertex2f((position.x-cameraX),(position.y-cameraY));
			glEnd();
		}
		else if (data->type == PROJECTILE_TYPE_SMALLROCKET)
		{
/*			
			glBegin (GL_LINES);
				glVertex2f(((GetSin(angle) * 8) + position.x) - cameraX,((GetCos(angle) * 8) + position.y) - cameraY);
				glVertex2f(((GetSin(angle) * -8) + position.x) - cameraX,((GetCos(angle) * -8) + position.y) - cameraY);
			glEnd();
*/
			glBegin(GL_LINES);
				glColor4f(1,1,1,1);
				glVertex2f(position.x - cameraX, position.y - cameraY);
				glColor4f(0,0,0,0);
				glVertex2f(startPosition.x - cameraX, startPosition.y - cameraY);
			glEnd();
		}
		else if (data->type == PROJECTILE_TYPE_LASER)
		{
/*			
			glBegin (GL_LINES);
				glVertex2f(((GetSin(angle) * 8) + position.x) - cameraX,((GetCos(angle) * 8) + position.y) - cameraY);
				glVertex2f(((GetSin(angle) * -8) + position.x) - cameraX,((GetCos(angle) * -8) + position.y) - cameraY);
			glEnd();
*/
			glBegin (GL_TRIANGLES);
				glColor4f(data->colorR,data->colorG,data->colorB,0);
				glVertex2f(((GetSin(angle+2.89502) * 8.24621) + position.x) - cameraX,((GetCos(angle+2.89502) * 8.24621) + position.y) - cameraY);
				glColor4f(data->colorR,data->colorG,data->colorB,data->colorA);
				glVertex2f(((GetSin(angle+3.14) * 8) + position.x) - cameraX,((GetCos(angle+3.14) * 8) + position.y) - cameraY);
				glColor4f(data->colorR,data->colorG,data->colorB,0);
				glVertex2f(((GetSin(angle+0.243386) * 8.24621) + position.x) - cameraX,((GetCos(angle+0.243386) * 8.24621) + position.y) - cameraY);
				glColor4f(data->colorR,data->colorG,data->colorB,data->colorA);
				glVertex2f(((GetSin(angle+3.14) * 8) + position.x) - cameraX,((GetCos(angle+3.14) * 8) + position.y) - cameraY);
				glVertex2f(((GetSin(angle+6.28159) * 8) + position.x) - cameraX,((GetCos(angle+6.28159) * 8) + position.y) - cameraY);
				glColor4f(data->colorR,data->colorG,data->colorB,0);
				glVertex2f(((GetSin(angle+0.243386) * 8.24621) + position.x) - cameraX,((GetCos(angle+0.243386) * 8.24621) + position.y) - cameraY);
				glColor4f(data->colorR,data->colorG,data->colorB,data->colorA);
				glVertex2f(((GetSin(angle+3.14) * 8) + position.x) - cameraX,((GetCos(angle+3.14) * 8) + position.y) - cameraY);
				glColor4f(data->colorR,data->colorG,data->colorB,0);
				glVertex2f(((GetSin(angle+3.38498) * 8.24621) + position.x) - cameraX,((GetCos(angle+3.38498) * 8.24621) + position.y) - cameraY);
				glVertex2f(((GetSin(angle+6.03661) * 8.24621) + position.x) - cameraX,((GetCos(angle+6.03661) * 8.24621) + position.y) - cameraY);
				glColor4f(data->colorR,data->colorG,data->colorB,data->colorA);
				glVertex2f(((GetSin(angle+3.14) * 8) + position.x) - cameraX,((GetCos(angle+3.14) * 8) + position.y) - cameraY);
				glColor4f(data->colorR,data->colorG,data->colorB,0);
				glVertex2f(((GetSin(angle+6.03661) * 8.24621) + position.x) - cameraX,((GetCos(angle+6.03661) * 8.24621) + position.y) - cameraY);
				glColor4f(data->colorR,data->colorG,data->colorB,data->colorA);
				glVertex2f(((GetSin(angle+6.28159) * 8) + position.x) - cameraX,((GetCos(angle+6.28159) * 8) + position.y) - cameraY);
			glEnd();

/*			glBegin (GL_TRIANGLES);
				glColor4f(1,1,1,data->colorA);
				glVertex2f(((GetSin(angle+0.243386) * 4) + position.x) - cameraX,((GetCos(angle+0.243386) * 4) + position.y) - cameraY);
				glVertex2f(((GetSin(angle+2.89502) * 4) + position.x) - cameraX,((GetCos(angle+2.89502) * 4) + position.y) - cameraY);
				glVertex2f(((GetSin(angle+3.38498) * 4) + position.x) - cameraX,((GetCos(angle+3.38498) * 4) + position.y) - cameraY);
				glVertex2f(((GetSin(angle+6.28159) * 4) + position.x) - cameraX,((GetCos(angle+6.28159) * 4) + position.y) - cameraY);
			glEnd();
*/
		}
		else if (data->type == PROJECTILE_TYPE_FLAK)
		{
			glColor4f(data->colorR,data->colorG,data->colorB,data->colorA);
			glBegin(GL_TRIANGLE_FAN);
				glVertex2f(position.x-cameraX,position.y-cameraY);
				{
				for (int i=0; i <= 5; i++)
				glVertex2f((GetSin(ToRadians(360/5)*i)*4)+(position.x-cameraX),(GetCos(ToRadians(360/5)*i)*4)+(position.y-cameraY));
				}
			glEnd();
		}
		else if (data->type == PROJECTILE_TYPE_PLASMA)
		{
			glColor4f(data->colorR,data->colorG,data->colorB,data->colorA);

			glBegin(GL_QUADS);
				glVertex2f((GetSin(angle+3.14)*10 + position.x) - cameraX, (GetCos(angle+3.14)*10 + position.y) - cameraY);
				glVertex2f((GetSin(angle+1.57)*3 + position.x) - cameraX, (GetCos(angle+1.57)*3 + position.y) - cameraY);
				glVertex2f((GetSin(angle)*6 + position.x) - cameraX, (GetCos(angle)*6 + position.y) - cameraY);
				glVertex2f((GetSin(angle-1.57)*3 + position.x) - cameraX, (GetCos(angle-1.57)*3 + position.y) - cameraY);
			glEnd();
		}
	}
}

void CProjectile::SetTeam(int t) { team = t; }
void CProjectile::SetPosition(Vector pos) { position = pos; startPosition = position; }
void CProjectile::SetPosition(float x, float y) { position = CreateVector(x,y); startPosition = position; }
void CProjectile::SetAngle(float nAngle) { angle = nAngle; }

CProjectileData* CProjectile::GetData() { return data; }
int CProjectile::GetTeam() { return team; }
Vector CProjectile::GetPosition() { return position; }
float CProjectile::GetAngle() { return angle; }

CProjectile* g_firstProjectile = NULL;
CProjectile* g_projectileCache = NULL;

//Just quickly places the Projectile Obj at the beginning of the linked list
void SetupProjectileCache()
{
	CProjectile* tproj;

	if (g_projectileCache)
	{
		CProjectile* cproj = g_projectileCache;
		while (cproj)
		{
			tproj = cproj->nextObj;
			delete cproj;
			cproj = tproj;
		}
		g_projectileCache = NULL;
	}

	for (int i=0; i <= PROJECTILE_CACHE_SIZE; i++)
	{
		tproj = new CProjectile(NULL);
		tproj->nextObj = g_projectileCache;
		g_projectileCache = tproj;
	}
}

CProjectile* AddProjectile(CProjectileData* data)
{
	if (!data)
		return NULL;
	
	CProjectile* newObj;

	if (g_projectileCache)
	{
		//Grab something out of the cache
		if (g_projectileCache->nextObj)
		{
			//Pull projectile out and remove it from cache
			newObj = g_projectileCache->nextObj;
			g_projectileCache->nextObj = newObj->nextObj;

			//Reset
			newObj->Reset(data);

			//Insert into world list
			newObj->nextObj = g_firstProjectile;
			if (g_firstProjectile)
				g_firstProjectile->prevObj = newObj;
			g_firstProjectile = newObj;

			//Return
			return newObj;
		}
		else
		{
			//Nothing in cache, manual add
			newObj = new CProjectile(data);
			if (!newObj)
				return NULL;
			newObj->nextObj = g_firstProjectile;
			if (g_firstProjectile)
				g_firstProjectile->prevObj = newObj;
			g_firstProjectile = newObj;
			return newObj;
		}
	}
	else
	{
		//No cache, manual add
		newObj = new CProjectile(data);
		if (!newObj)
			return NULL;
		newObj->nextObj = g_firstProjectile;
		if (g_firstProjectile)
			g_firstProjectile->prevObj = newObj;
		g_firstProjectile = newObj;
		return newObj;
	}
}

void DeleteProjectile(CProjectile* obj)
{
	if (!obj)
		return;

	if (obj->prevObj)
		obj->prevObj->nextObj = obj->nextObj;
	else
		g_firstProjectile = obj->nextObj;
	if (obj->nextObj)
		obj->nextObj->prevObj = obj->prevObj;

	//Save projectile for later by inserting into cache
	if (g_projectileCache)
	{
		obj->nextObj = g_projectileCache;
		g_projectileCache = obj;
	}
	else
		delete obj;
}

void DeleteAllProjectiles()
{
	CProjectile* curObj;
	CProjectile* tmpObj;
	curObj = g_firstProjectile;
	while (curObj)
	{
		tmpObj = curObj->nextObj;
		delete curObj;
		curObj = tmpObj;
	}
	g_firstProjectile = NULL;

	curObj = g_projectileCache;
	while (curObj)
	{
		tmpObj = curObj->nextObj;
		delete curObj;
		curObj = tmpObj;
	}
	g_projectileCache = NULL;
}
