//Copyright Paul Reiche, Fred Ford. 1992-2002

/*
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "../ship.h"
#include "utwig.h"
#include "resinst.h"

#include "uqm/globdata.h"
#include "libs/mathlib.h"

// Core characteristics
#define MAX_CREW 20
#define MAX_ENERGY 20
#define ENERGY_REGENERATION 0
#define ENERGY_WAIT 255
#define MAX_THRUST 36
#define THRUST_INCREMENT 6
#define THRUST_WAIT 6
#define TURN_WAIT 1
#define SHIP_MASS 8

// Weapon
#define WEAPON_ENERGY_COST 0
#define WEAPON_WAIT 7
#define UTWIG_OFFSET 9
#define MISSILE_SPEED DISPLAY_TO_WORLD (30)
#define MISSILE_LIFE 10
#define MISSILE_HITS 1
#define MISSILE_DAMAGE 1
#define MISSILE_OFFSET 1
#define LAUNCH_XOFFS0 DISPLAY_TO_WORLD (5)
#define LAUNCH_YOFFS0 -DISPLAY_TO_WORLD (18)
#define LAUNCH_XOFFS1 DISPLAY_TO_WORLD (13)
#define LAUNCH_YOFFS1 -DISPLAY_TO_WORLD (9)
#define LAUNCH_XOFFS2 DISPLAY_TO_WORLD (17)
#define LAUNCH_YOFFS2 -DISPLAY_TO_WORLD (4)

// Shield
#define SPECIAL_ENERGY_COST 1
#define SPECIAL_WAIT 12

static RACE_DESC utwig_desc =
{
	{ /* SHIP_INFO */
		FIRES_FORE | POINT_DEFENSE | SHIELD_DEFENSE,
		22, /* Super Melee cost */
		MAX_CREW, MAX_CREW,
		MAX_ENERGY >> 1, MAX_ENERGY,
		UTWIG_RACE_STRINGS,
		UTWIG_ICON_MASK_PMAP_ANIM,
		UTWIG_MICON_MASK_PMAP_ANIM,
		NULL, NULL, NULL
	},
	{ /* FLEET_STUFF */
		666 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
		{ /* Known location (center of SoI) */
			8534, 8797,
		},
	},
	{
		MAX_THRUST,
		THRUST_INCREMENT,
		ENERGY_REGENERATION,
		WEAPON_ENERGY_COST,
		SPECIAL_ENERGY_COST,
		ENERGY_WAIT,
		TURN_WAIT,
		THRUST_WAIT,
		WEAPON_WAIT,
		SPECIAL_WAIT,
		SHIP_MASS,
	},
	{
		{
			UTWIG_BIG_MASK_PMAP_ANIM,
			UTWIG_MED_MASK_PMAP_ANIM,
			UTWIG_SML_MASK_PMAP_ANIM,
		},
		{
			LANCE_BIG_MASK_PMAP_ANIM,
			LANCE_MED_MASK_PMAP_ANIM,
			LANCE_SML_MASK_PMAP_ANIM,
		},
		{
			NULL_RESOURCE,
			NULL_RESOURCE,
			NULL_RESOURCE,
		},
		{
			UTWIG_CAPTAIN_MASK_PMAP_ANIM,
			NULL, NULL, NULL, NULL, NULL
		},
		UTWIG_VICTORY_SONG,
		UTWIG_SHIP_SOUNDS,
		{ NULL, NULL, NULL },
		{ NULL, NULL, NULL },
		{ NULL, NULL, NULL },
		NULL, NULL
	},
	{
		0,
		CLOSE_RANGE_WEAPON,
		NULL,
	},
	(UNINIT_FUNC *) NULL,
	(PREPROCESS_FUNC *) NULL,
	(POSTPROCESS_FUNC *) NULL,
	(INIT_WEAPON_FUNC *) NULL,
	0,
	0, /* CodeRef */
};

static COUNT
initialize_lance (ELEMENT *ShipPtr, HELEMENT WeaponArray[])
{
	COUNT i;
	STARSHIP *StarShipPtr;
	MISSILE_BLOCK MissileBlock;

	GetElementStarShip (ShipPtr, &StarShipPtr);
	MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
	MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
	MissileBlock.sender = ShipPtr->playerNr;
	MissileBlock.flags = IGNORE_SIMILAR;
	MissileBlock.speed = MISSILE_SPEED;
	MissileBlock.hit_points = MISSILE_HITS;
	MissileBlock.damage = MISSILE_DAMAGE;
	MissileBlock.life = MISSILE_LIFE;
	MissileBlock.preprocess_func = NULL;
	MissileBlock.blast_offs = MISSILE_OFFSET;
	MissileBlock.pixoffs = 0;

	for (i = 0; i < 3; ++i)
	{
		COUNT angle;
		SIZE sin0 = 0, cos0 = 0;
		SIZE sin1sin0, cos1sin0, cos1cos0, sin1cos0;

		switch (i)
		{
			case 0:
				cos0 = LAUNCH_XOFFS0;
				sin0 = LAUNCH_YOFFS0;
				break;
			case 1:
				cos0 = LAUNCH_XOFFS1;
				sin0 = LAUNCH_YOFFS1;
				break;
			case 2:
				cos0 = LAUNCH_XOFFS2;
				sin0 = LAUNCH_YOFFS2;
				break;
		}
		angle = FACING_TO_ANGLE (MissileBlock.face) + QUADRANT;
		cos1cos0 = COSINE (angle, cos0);
		sin1sin0 = SINE (angle, sin0);
		sin1cos0 = SINE (angle, cos0);
		cos1sin0 = COSINE (angle, sin0);

		cos0 = cos1cos0 - sin1sin0;
		sin0 = sin1cos0 + cos1sin0;
		MissileBlock.cx = ShipPtr->next.location.x + cos0;
		MissileBlock.cy = ShipPtr->next.location.y + sin0;
		WeaponArray[(i << 1)] = initialize_missile (&MissileBlock);

		cos0 = -cos1cos0 - sin1sin0;
		sin0 = -sin1cos0 + cos1sin0;
		MissileBlock.cx = ShipPtr->next.location.x + cos0;
		MissileBlock.cy = ShipPtr->next.location.y + sin0;
		WeaponArray[(i << 1) + 1] = initialize_missile (&MissileBlock);
	}

	return (6);
}

static void
utwig_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
		COUNT ConcernCounter)
{
	SIZE ShieldStatus;
	STARSHIP *StarShipPtr;
	EVALUATE_DESC *lpEvalDesc;

	GetElementStarShip (ShipPtr, &StarShipPtr);

	lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
	if (StarShipPtr->RaceDescPtr->ship_info.energy_level == 0)
		ShieldStatus = 0;
	else
	{
		ShieldStatus = -1;
		if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == ENTICE)
		{
			ShieldStatus = 0;
			if (!(lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE))
				lpEvalDesc->MoveState = PURSUE;
			else if (lpEvalDesc->ObjectPtr->mass_points
					|| (lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT))
			{
				if ((lpEvalDesc->which_turn >>= 1) == 0)
					lpEvalDesc->which_turn = 1;

				if (lpEvalDesc->ObjectPtr->mass_points)
					lpEvalDesc->ObjectPtr = 0;
				else
					lpEvalDesc->MoveState = PURSUE;
				ShieldStatus = 1;
			}
		}
	}

	if (StarShipPtr->special_counter == 0)
	{
		StarShipPtr->ship_input_state &= ~SPECIAL;
		if (ShieldStatus)
		{
			if ((ShieldStatus > 0 || lpEvalDesc->ObjectPtr)
					&& lpEvalDesc->which_turn <= 2
					&& (ShieldStatus > 0
					|| (lpEvalDesc->ObjectPtr->state_flags
					& PLAYER_SHIP) /* means IMMEDIATE WEAPON */
					|| PlotIntercept (lpEvalDesc->ObjectPtr,
					ShipPtr, 2, 0))
					&& (TFB_Random () & 3))
			{
				StarShipPtr->ship_input_state |= SPECIAL;
				StarShipPtr->ship_input_state &= ~WEAPON;
			}

			lpEvalDesc->ObjectPtr = 0;
		}
	}

	if (StarShipPtr->RaceDescPtr->ship_info.energy_level
			&& (lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX])->ObjectPtr)
	{
		STARSHIP *EnemyStarShipPtr;

		GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
		if (!(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags
				& IMMEDIATE_WEAPON))
			lpEvalDesc->MoveState = PURSUE;
	}
	ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
}

static void
utwig_collision (ELEMENT *ElementPtr0, POINT *pPt0,
		ELEMENT *ElementPtr1, POINT *pPt1)
{
	if (ElementPtr0->life_span > NORMAL_LIFE
			&& (ElementPtr1->state_flags & FINITE_LIFE)
			&& ElementPtr1->mass_points)
		ElementPtr0->life_span += ElementPtr1->mass_points;

	collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
}

static void
utwig_preprocess (ELEMENT *ElementPtr)
{
	STARSHIP *StarShipPtr;
	PRIMITIVE *lpPrim;

	if (ElementPtr->state_flags & APPEARING)
	{
		ElementPtr->collision_func = utwig_collision;
	}

	GetElementStarShip (ElementPtr, &StarShipPtr);
	if (ElementPtr->life_span > (NORMAL_LIFE + 1))
	{
		DeltaEnergy (ElementPtr,
				ElementPtr->life_span - (NORMAL_LIFE + 1));

		ProcessSound (SetAbsSoundIndex (
				StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1),
				ElementPtr);
	}

	if (!(StarShipPtr->cur_status_flags & SPECIAL))
		StarShipPtr->special_counter = 0;
	else if (StarShipPtr->special_counter % (SPECIAL_WAIT >> 1) == 0)
	{
		if (!DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
			StarShipPtr->RaceDescPtr->ship_info.ship_flags &=
					~(POINT_DEFENSE | SHIELD_DEFENSE);
		else if (StarShipPtr->special_counter == 0)
		{
			StarShipPtr->special_counter =
					StarShipPtr->RaceDescPtr->characteristics.special_wait;
			ProcessSound (SetAbsSoundIndex (
					StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2),
					ElementPtr);
		}
	}

	lpPrim = &(GLOBAL (DisplayArray))[ElementPtr->PrimIndex];
	if (StarShipPtr->special_counter == 0)
	{
		// The shield is off.
		SetPrimColor (lpPrim,
				BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1C, 0x00), 0x78));
		ElementPtr->colorCycleIndex = 0;
		ElementPtr->life_span = NORMAL_LIFE;
		SetPrimType (lpPrim, STAMP_PRIM);
	}
	else
	{
		// The shield is on.

		/* Originally, this table also contained the now commented out
		 * entries. It then used some if statements to skip over these.
		 * The current behaviour is the same as the old behavior,
		 * but I am not sure that the old behavior was intended. - SvdB
		 */
		static const Color colorTable[] =
		{
			BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7a),
			//BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7b),
			BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7c),
			//BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7d),
			BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7e),
			//BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7f),
			BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x2a),
			//BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2b),
			BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2c),
			BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2d),
			BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2e),
			//BUILD_COLOR (MAKE_RGB15_INIT (0x0B, 0x00, 0x00), 0x2f),
		};
		const size_t colorTableCount =
				sizeof colorTable / sizeof colorTable[0];

		if (StarShipPtr->weapon_counter == 0)
			++StarShipPtr->weapon_counter;

		// colorCycleIndex is actually 1 higher than the entry in colorTable
		// which is currently used, as it is 0 when the shield is off,
		// and we don't want to skip over the first entry of the table.
		ElementPtr->colorCycleIndex++;
		if (ElementPtr->colorCycleIndex == colorTableCount + 1)
			ElementPtr->colorCycleIndex = 1;

		SetPrimColor (lpPrim, colorTable[ElementPtr->colorCycleIndex - 1]);

		ElementPtr->life_span = NORMAL_LIFE + 1;
		SetPrimType (lpPrim, STAMPFILL_PRIM);
	}
}

RACE_DESC*
init_utwig (void)
{
	RACE_DESC *RaceDescPtr;

	utwig_desc.preprocess_func = utwig_preprocess;
	utwig_desc.init_weapon_func = initialize_lance;
	utwig_desc.cyborg_control.intelligence_func = utwig_intelligence;

	RaceDescPtr = &utwig_desc;

	return (RaceDescPtr);
}

