//Copyright (C) 2007 SiegeLord
//See ReadMe.txt and License.txt for copying information

#include "ship.h"
#include "world.h"

#include "orders.h"
#include "selection.h"
#include "thruster.h"
#include "shipregistry.h"
#include "weapon.h"
#include "camera.h"
#include "flasher.h"


extern SCamera g_strCamera;
extern bool g_bDrawCollision;
extern SObjectEx* g_obRootObject;
extern int g_nPlayer;
extern SShip* g_obMotherShip[2];

SShip* CreateShip(SObject* parent)
{
	SShip* ship = new SShip();
	InitObject(ship, parent);
	ship->bbox.Set(-20, -20, 20, 20);
	ship->polar_accel.Set(20, 0.05);
	ship->polar_max_vel.Set(20, 0.05);
	ship->image = FindBitmap("ship1.tga");
	
	ship->armour = 4.0f;

	SThrusterParticleHolder* thruster = CreateThrusterParticleHolder(g_obRootObject, ship, FindBitmap("part.tga"), 0xff0500ff);
	thruster->SetPosition(28, 0);
	thruster->SetRotation(0);
	thruster->throttle = 50;
	thruster->color = 0xffff001f;
	thruster->throttle = 0;
	ship->right_thrusters.push_back(thruster);

	thruster = CreateThrusterParticleHolder(g_obRootObject, ship, FindBitmap("part.tga"), 0xff0500ff);
	thruster->SetPosition(0, 14);
	thruster->SetRotation(AL_PI / 2);
	thruster->throttle = 50;
	thruster->color = 0xffff001f;
	thruster->throttle = 0;
	ship->bottom_thrusters.push_back(thruster);

	thruster = CreateThrusterParticleHolder(g_obRootObject, ship, FindBitmap("part.tga"), 0xff0500ff);
	thruster->SetPosition(0, -14);
	thruster->SetRotation(-AL_PI / 2);
	thruster->throttle = 50;
	thruster->color = 0xffff001f;
	thruster->throttle = 0;
	ship->top_thrusters.push_back(thruster);

	thruster = CreateThrusterParticleHolder(g_obRootObject, ship, FindBitmap("part.tga"), 0xff0500ff);
	thruster->SetPosition(-28, 0);
	thruster->SetRotation(AL_PI);
	thruster->throttle = 50;
	thruster->color = 0xffff001f;
	thruster->throttle = 0;
	ship->left_thrusters.push_back(thruster);

	SWeaponSlot* slot = new SWeaponSlot;
	slot->position.Set(17, 0);
	slot->rotation = 0;

	ship->hitpoints = 100.0f;
	ship->max_hitpoints = 100.0f;

	ship->sense_range = 500.0f;

	//if(Rand() > 0.5)
	//	CreatePlasmaLauncher(ship, slot);
	//else
		CreateIonBeamCannon(ship, slot);

	ship->weapon_slots.push_back(slot);

	return ship;
}

SShip* CreateMotherShip(SObject* parent)
{
	SShip* ship = new SShip();
	InitObject(ship, parent);
	ship->type = mothership_type;
	ship->bbox.Set(-50, -50, 50, 50);
	ship->polar_accel.Set(0, 0);
	ship->polar_max_vel.Set(0, 0);
	ship->image = FindBitmap("mother.tga");

	ship->hitpoints = 600.0f;
	ship->max_hitpoints = 600.0f;

	ship->armour = 12.0f;

	ship->sense_range = 500.0f;

	SWeaponSlot* slot;
	slot = new SWeaponSlot;
	slot->position.Set(64, 46);
	slot->rotation = 0;

	CreatePlasmaLauncher(ship, slot);
	ship->weapon_slots.push_back(slot);

	slot = new SWeaponSlot;
	slot->position.Set(-64, 46);
	slot->rotation = AL_PI;

	CreateRocketLauncher(ship, slot);
	ship->weapon_slots.push_back(slot);

	slot = new SWeaponSlot;
	slot->position.Set(-64, -46);
	slot->rotation = AL_PI;

	CreatePlasmaLauncher(ship, slot);
	ship->weapon_slots.push_back(slot);

	slot = new SWeaponSlot;
	slot->position.Set(64, -46);
	slot->rotation = 0;

	CreateRocketLauncher(ship, slot);
	ship->weapon_slots.push_back(slot);

	CreateFlasher(ship, AL_PI, 2.0f, 0xffffffff)->SetPosition(110, 3);
	CreateFlasher(ship, AL_PI, 2.0f, 0xffffffff)->SetPosition(-110, 3);
	CreateFlasher(ship, 0, 2.0f, 0xffffffff)->SetPosition(-64, 3);
	CreateFlasher(ship, 0, 2.0f, 0xffffffff)->SetPosition(64, 3);

	return ship;
}

SShip::~SShip()
{
	RemoveObjectFromQuadNode(this);
	RemoveFromSelection(this);
	UnRegisterShip(this);
	while(!order_list.empty())
	{
		delete order_list.front();
		order_list.pop_front();
	}
	const int size = weapon_slots.size();
	FOR1(size)
	{
		delete weapon_slots[ii];
	}
}

void SShip::TargetAllWeapons(vector2d& target_loc, SShip* target_ship)
{
	const int size = weapon_slots.size();
	FOR1(size)
	{
		if(weapon_slots[ii]->weapon)
		{
			int target_ID = -1;
			if(target_ship)
				target_ID = target_ship->ID;
			weapon_slots[ii]->weapon->Engage(target_loc, target_ID);
		}
	}
}

void SShip::Damage(float min_damage, float max_damage, float fraction)
{
	float damage = max_damage - armour;
	if(damage < min_damage)
		damage = min_damage;
	damage *= fraction;
	hitpoints -= damage;
}

void SShip::CeaseFire()
{
	const int size = weapon_slots.size();
	FOR1(size)
	{
		if(weapon_slots[ii]->weapon)
		{
			weapon_slots[ii]->weapon->DisEngage();
		}
	}
}

float SShip::GetSmallestWeaponRange()
{
	float range = 10000000.0f;
	const int size = weapon_slots.size();
	FOR1(size)
	{
		if(weapon_slots[ii]->weapon)
		{
			if(weapon_slots[ii]->weapon->range < range)
				range = weapon_slots[ii]->weapon->range;
		}
	}
	float max_dim = max(bbox.Max.x, bbox.Max.y);
	return range - max_dim;
}

void SShip::DeActivateAllThrusters()
{
	const int size1 = left_thrusters.size();
	FOR1(size1)
	{
		left_thrusters[ii]->throttle = 0;
	}
	const int size2 = right_thrusters.size();
	FOR1(size2)
	{
		right_thrusters[ii]->throttle = 0;
	}
	const int size3 = bottom_thrusters.size();
	FOR1(size3)
	{
		bottom_thrusters[ii]->throttle = 0;
	}
	const int size4 = top_thrusters.size();
	FOR1(size4)
	{
		top_thrusters[ii]->throttle = 0;
	}
}
	//right, top, left, bottom
void SShip::ActivateThrusters(int bank, float goal_vel)
{
	switch(bank)
	{
	case 0:
		{
			float delta = g_fDelta * polar_accel.y;
			if(fabs(goal_vel - polar_vel.y) < delta)
				polar_vel.y = goal_vel;
			else
			{
				const int size = right_thrusters.size();
				FOR1(size)
				{
					right_thrusters[ii]->throttle = 100;
				}
				polar_vel.y -= delta;
			}
		}
		break;
	case 1:
		{
			float delta = g_fDelta * polar_accel.x;
			if(fabs(goal_vel - polar_vel.x) < delta)
				polar_vel.x = goal_vel;
			else
			{
				const int size = top_thrusters.size();
				FOR1(size)
				{
					top_thrusters[ii]->throttle = 100;
				}
				polar_vel.x -= delta;
			}
		}
		break;
	case 2:
		{
			float delta = g_fDelta * polar_accel.y;
			if(fabs(goal_vel - polar_vel.y) < delta)
				polar_vel.y = goal_vel;
			else
			{
				const int size = left_thrusters.size();
				FOR1(size)
				{
					left_thrusters[ii]->throttle = 100;
				}
				polar_vel.y += delta;
			}
		}
		break;
	default:
		{
			float delta = g_fDelta * polar_accel.x;
			if(fabs(goal_vel - polar_vel.x) < delta)
				polar_vel.x = goal_vel;
			else
			{
				const int size = bottom_thrusters.size();
				FOR1(size)
				{
					bottom_thrusters[ii]->throttle = 100;
				}
				polar_vel.x += delta;
			}
		}
		break;
	}
}

void SShip::Draw()
{
	SetModelMatrix(net_transform);

	//float old = SetLineThickness(2.0f);

	zColor color = 0;

	if(player == 0)
		color = 0xffffff00;
	else
		color = 0xff0f0fff;

	float max_dim = max(bbox.Max.x, bbox.Max.y);

	DrawModeMask();
	DrawBitmap(image, -image->w / 2, -image->h /2, -8);
	DrawModeAlpha();

	float old_thick = SetLineThickness(2.0f);

	if(player == 0)
	{
		DrawRect(-2, -max_dim - 2, 2, -max_dim + 2, -10, color);
	}
	else
	{
		DrawCircle(0, -max_dim, 2, -10, color, 16);
	}

	//SetLineThickness(old);
	SetModelMatrix(g_trIdentity);

	vector2d cur_loc = GetGlobalPosition();

	if(g_bDrawCollision)
	{
		DrawRect(cur_loc.x + bbox.Min.x, cur_loc.y + bbox.Min.y,
			cur_loc.x + bbox.Max.x, cur_loc.y + bbox.Max.y, -10, 0xffffc0e1);
	}

	SetLineThickness(1.0f);

	if(flashy_timer > 0.0f)
	{
		int state = int(flashy_timer / 0.25f);
		if(state % 2 == 0)
		{
			DrawCircle(cur_loc.x, cur_loc.y, max_dim, -10, color, 32);
		}
	}

	if(selected)
	{
		if(hitpoints > - 0.1)
		{
			vector2d hbar_pos = cur_loc;
			float hbar_zoom = g_strCamera.zoom;

			g_strCamera.trans.TransformVector(&hbar_pos);
			SetCameraMatrix(g_trIdentity);

			DrawFilledRect(hbar_pos.x - hbar_zoom * max_dim, hbar_pos.y - hbar_zoom * (max_dim - 2),
						   hbar_pos.x + hbar_zoom * max_dim, hbar_pos.y - hbar_zoom * (max_dim - 4), -8, 0xff000050);
			DrawFilledRect(hbar_pos.x - hbar_zoom * max_dim, hbar_pos.y - hbar_zoom * (max_dim - 2),
 						   hbar_pos.x - hbar_zoom * max_dim + hbar_zoom * 2 * max_dim * (hitpoints / max_hitpoints), hbar_pos.y - hbar_zoom * (max_dim - 4), -8, 0xff00ff00);

			SetCameraMatrix(g_strCamera.trans);
		}

		DrawCircle(cur_loc.x, cur_loc.y, max_dim, -10, color, 32);


		//this just does a connect-the-dot on all of the orders
		if(order_list.size() > 0 && player == g_nPlayer)
		{
			bool first = false;
			vector2d dest = cur_loc;
			for(list<SOrder*>::iterator it = order_list.begin(); it != order_list.end(); it++)
			{
				if((*it)->order_type == move_order)
				{
					vector2d pol_dest = ((SMoveOrder*)(*it))->destination;
					dest.Set(pol_dest.x * cosf(pol_dest.y), pol_dest.x * sinf(pol_dest.y));
				}
				if(first)
				{
					vector2d dir = dest - cur_loc;
					dir.Normalize();
					DrawLine(cur_loc.x + dir.x * max_dim, cur_loc.y + dir.y * max_dim,
						dest.x, dest.y, -10, 0xff0fff0f);
					first = false;
				}
				else
					DrawLine(cur_loc.x, cur_loc.y, dest.x, dest.y, -10, 0xff0fff0f);
				cur_loc = dest;
			}
		}
	}

	SetLineThickness(old_thick);
	DrawModeSolid();
}

int SShip::Handle()
{
	polar_pos += polar_vel * g_fDelta;

	UpdatePolarPosition();
	UpdatePolarRotation();
	UpdateBBox();
	UpdateQuadTree(this);
	DeActivateAllThrusters();

	flashy_timer -= g_fDelta;

	if(hitpoints < 0)
	{
		if(type == mothership_type)
			g_obMotherShip[player] = 0;
		order_list.clear();
		CeaseFire();
		SDieOrder* order = new SDieOrder();
		order->delay = Rand(0.0f, 2.0f);
		order->ship = this;
		order_list.push_back(order);
	}

	//now, handle the orderz

	if(order_list.size() == 0)
	{
		SIdleOrder* order = new SIdleOrder();
		order->ship = this;
		order_list.push_front(order);
	}

	if(order_list.front()->Handle())
	{
		delete order_list.front();
		order_list.pop_front();
	}


	return 0;
}

void SShip::SetPolarPosition(float r, float theta)
{
	polar_pos.Set(r, theta);
	UpdatePolarPosition();
	UpdatePolarRotation();
	UpdateBBox();
}

void SShip::UpdatePolarPosition()
{
	SetPosition(polar_pos.x * cosf(polar_pos.y), polar_pos.x * sinf(polar_pos.y));

}

void SShip::UpdatePolarRotation()
{
	SetRotation(polar_pos.y + AL_PI / 2);
}
