//	FIXME:	add each to bot to bot_list actorlist
#include	"world.h"

#include	"ai.h"
#include	"ai_utils.h"
#include	"ai_move.h"
#include	"ai_weapons.h"
#include	"ai_task.h"
#include	"ai_frames.h"

#include	"skat.h"
#include	"nodelist.h"
#include	"bot.h"
#include	"client.h"
#include	"actorlist.h"

////////////////////////////////////////////////////////////////////////////////
// exports
////////////////////////////////////////////////////////////////////////////////

DllExport	void	monster_bot (userEntity_t *self);

///////////////////////////////////////////////////////////////////////////////////
// bot specific funcitons
///////////////////////////////////////////////////////////////////////////////////

void	bot_close_melee (userEntity_t *self);
void	bot_far_melee (userEntity_t *self);

////////////////////////////////
//	bot_init_frametable
//
////////////////////////////////

void	bot_init_frametable (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	
	//	currently for chaingunner model
/*
	//	standing fire
	ai_set_sequence_data (self, hook->frames->atak [0], -1, NULL, -1, NULL, SEQUENCE_TRANS, 4, 0, -1);
	//	walking, one-handed fire
	ai_set_sequence_data (self, hook->frames->atak [1], -1, NULL, -1, NULL, SEQUENCE_TRANS, 2, 0, -1);
	//	running, two-handed fire
	ai_set_sequence_data (self, hook->frames->atak [2], -1, NULL, -1, NULL, 0, -1, 0, -1);
	//	running, two-handed firing left
	ai_set_sequence_data (self, hook->frames->atak [3], -1, NULL, -1, NULL, 0, -1, 0, -1);
	//	running, one-handed firing right
	ai_set_sequence_data (self, hook->frames->atak [4], -1, NULL, -1, NULL, 0, -1, 0, -1);
	//	hovering and firing
	ai_set_sequence_data (self, hook->frames->atak [5], -1, NULL, -1, NULL, SEQUENCE_TRANS, 0, 0, -1);
*/

	ai_set_sequence_data (self, hook->frames->atak [0], -1, NULL, -1, NULL, SEQUENCE_NONE, -1, SEQUENCE_NONE, -1, FRAME_LOOP);

	ai_set_sequence_data (self, hook->frames->walk [0], -1, NULL, -1, NULL, SEQUENCE_NONE, -1, SEQUENCE_NONE, -1, FRAME_LOOP);

	ai_set_sequence_data (self, hook->frames->run  [0], -1, NULL, -1, NULL, SEQUENCE_NONE, -1, SEQUENCE_NONE, -1, FRAME_LOOP);
}

////////////////////////////////
//	bot_choose_weapon
//
////////////////////////////////

#define	PULSERIFLE	0
#define	CORDITE		1
#define	ROCKET		2
#define	CROSSBOW		3 

void	bot_choose_weapon (userEntity_t *self, float dist)
{
	playerHook_t		*hook = (playerHook_t *) self->userHook;
	userInventory_t		*inv;
	int					temp;

	ammo_t				*ammo [32];
	weapon_t			*weapon [32];

	///////////////////////////////////////////////////////////
	//	determine which weapons we have
	///////////////////////////////////////////////////////////

	//	clear the ammo and weapons arrays
	memset (ammo, 0x00, sizeof (ammo));
	memset (weapon, 0x00, sizeof (weapon));

	//	search through the whole inventory list in one pass
	for (inv = gstate->InventoryFirstItem (self->inventory); inv; inv = gstate->InventoryNextItem (inv))
	{
		if (inv)
		{
			if (inv->name)
			{
				//	look for weapons
				if (!stricmp (inv->name, "weapon_pulserifle"))
					weapon [PULSERIFLE] = weapon [CORDITE] = (weapon_t *) inv;
				else if (!stricmp (inv->name, "weapon_rocket"))
					weapon [ROCKET] = (weapon_t *) inv;
				else if (!stricmp (inv->name, "weapon_crossbow"))
					weapon [CROSSBOW] = (weapon_t *) inv;

				//	look for ammo
				else if (!stricmp (inv->name, "ammo_pulse"))
					ammo [PULSERIFLE] = (ammo_t *) inv;
				else if (!stricmp (inv->name, "ammo_cordite"))
					ammo [CORDITE] = (ammo_t *) inv;
				else if (!stricmp (inv->name, "ammo_rockets"))
					ammo [ROCKET] = (ammo_t *) inv;
				else if (!stricmp (inv->name, "ammo_bolts"))
					ammo [CROSSBOW] = (ammo_t *) inv;
			}
		}
	}

	///////////////////////////////////////////////////////////////////////
	//	we now have an array detailing which weapons and ammo we have
	//	use this data to determine the best weapon
	///////////////////////////////////////////////////////////////////////

	if (dist > 120.0 && weapon [ROCKET] && ammo [ROCKET]->count > 0)
	{
		//	bot has a rocket launcher with ammo and enemy is > 120 units away
		//	so use it
		self->curWeapon = (userInventory_t *) weapon [ROCKET];

		return;
	}
	if (weapon [PULSERIFLE] && ammo [PULSERIFLE]->count > 0)
	{
		gstate->Con_Printf ("chose pulserifle\n");

		//	we have pulse bullets, so choose the pulserifle and exit
		temp = 1;
		weapon [PULSERIFLE]->command ((userInventory_t *) weapon, "force_mode", &temp);
		weapon [PULSERIFLE]->command ((userInventory_t *) weapon, "set_ammo", ammo [PULSERIFLE]);
		self->curWeapon = (userInventory_t *) weapon [PULSERIFLE];

		return;
	}
	if (weapon [CROSSBOW] && ammo [CROSSBOW]->count > 0 && dist > 256.0)
	{
		//	we have the crossbow and enemy is close, so choose it and exit
		self->curWeapon = (userInventory_t *) weapon [CROSSBOW];

		return;
	}
	
	if (weapon [CORDITE] && ammo [CORDITE]->count > 0)
	{
		gstate->Con_Printf ("chose cordite\n");

		//	we have pulse bullets, so choose the pulserifle and exit
		temp = 0;
		weapon [CORDITE]->command ((userInventory_t *) weapon, "force_mode", &temp);
		weapon [CORDITE]->command ((userInventory_t *) weapon, "set_ammo", ammo [CORDITE]);
		self->curWeapon = (userInventory_t *) weapon [CORDITE];
			
		return;
	}			
}

//////////////////////////////////
//	bot_attack
//	
//	bot has started firing weapon at enemy
//	bot will remain in this routine as long as his enemy is visible
//
//	bot will fire weapon at enemy whenever hook->attack_finished <= gstate->time
//	and there is a clear shot
//////////////////////////////////

void	bot_attack (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	int				i;
	float			dist;
	Vector			temp;

#ifdef	DEBUGINFO
	ai_debug_info (self, "bot_attack", NULL);
#endif

	///////////////////////////////////////////////////
	//	keep goal centered on enemy
	///////////////////////////////////////////////////

	hook->dflags |= DFL_FACEENEMY;
/*
	temp = self->goalentity->s.origin;
	com->CopyVector (self->goalentity->s.origin, self->enemy->s.origin);
	ai_face_goal (self);
	temp.set (self->goalentity->s.origin);
*/	
	///////////////////////////////////////////////////
	//	get distance to enemy
	///////////////////////////////////////////////////

	dist = qv_dist (self->enemy->s.origin, self->s.origin);

	///////////////////////////////////////////////////
	//	make sure we can still attack
	///////////////////////////////////////////////////

	if (self->enemy->deadflag != DEAD_NO)
	{
		gstate->Con_Printf ("dead enemy\n");
		self->nextthink = gstate->time + THINK_TIME;
		self->think = hook->begin_node_wander;

		return;
	}
	else if (dist > hook->attack_dist + 64.0 || !com->Visible (self, self->enemy))
	{
		gstate->Con_Printf ("can't see enemy\n");
		hook->begin_path_follow (self);

		return;
	}

	///////////////////////////////////////////////////
	//	play a sound on correct frames
	///////////////////////////////////////////////////
	
	if (hook->frame_flags & FRAME_PLAYSOUND1)
	{
		if (hook->sound1)
			gstate->sound (self, CHAN_AUTO, hook->sound1, 128, ATTN_NORM);
		hook->frame_flags = hook->frame_flags - FRAME_PLAYSOUND1;
		hook->sound1Frame = -1;
	}
	
	if (hook->frame_flags & FRAME_PLAYSOUND2)
	{
		if (hook->sound2)
			gstate->sound (self, CHAN_AUTO, hook->sound2, 128, ATTN_NORM);
		hook->frame_flags = hook->frame_flags - FRAME_PLAYSOUND2;
		hook->sound1Frame = -1;
	}

	if (gstate->time >= hook->attack_finished)
	{
		//	find the best weapon to use against this enemy
		bot_choose_weapon (self, dist);

		//	choose an optimal point of impact, taking bot skill into account
		//	bot_aim (self);

		//	already facing our enemy in yaw direction
		//	temporarily set pitch to face enemy
		temp = Vector (self->enemy->s.origin) - Vector (self->s.origin);
		temp.Normalize ();
		com->VecToAngles (temp, temp);
		temp.y = self->client->v_angle [0];
		self->client->v_angle [0] = temp.x;

		//	fire the current weapon
		if (self->curWeapon)
		{
			hook->weapon_fired = self->curWeapon;
			self->curWeapon->use (self->curWeapon, self);

			//	begin attack frames
			ai_init_sequence (self, hook->frames->atak [0], SEQUENCE_ATAK, 0);
		}

  		gstate->Con_Printf ("firing weapon\n");
		hook->attack_finished = gstate->time + 1.0;

		self->client->v_angle [0] = temp.y;
	}

	///////////////////////////////////////////////////
	//	reached the end of attack sequence
	//
	//	???
	//	when the end of the attack sequence is reached
	//	go to running frames.  When a shot is fired
	//	the new sequence will be started
	///////////////////////////////////////////////////

	if (hook->frame_flags & FRAME_LAST)
	{
//		return;
	}

	///////////////////////////////////////////////////
	//	strafe around target
	///////////////////////////////////////////////////

	ai_attack_move (self);
//	ai_movetogoal (self, hook->run_speed, MOVE_FORWARD);

	if (!(hook->ai_flags & AI_JUMP) && !(hook->ai_flags & AI_TURN))
		ai_set_think (self, bot_attack, "bot_attack");
}

////////////////////////////
//	bot_begin_attack
//
////////////////////////////

void	bot_begin_attack (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	float			dist;

	com->ZeroVector (self->velocity);

	hook->th_state = TH_ATTACK;
	hook->attack_finished = gstate->time + 1.0;

	ai_init_sequence (self, hook->frames->run [0], SEQUENCE_RUN, 0);

	bot_attack (self);
}

////////////////////////
//	bot_choose_netname
//
//	chooses a random name for a bot
//
//	grabs names from the file data\botnames.txt
//	if it exists, otherwise chooses from a list
//	of hardcoded names
//
//	maximum of 256 names will be read
////////////////////////

void	bot_choose_netname (userEntity_t *self)
{
	FILE		*f;
	int			num_names, i;
	char		fname [256];
	char		bot_names [256][40];

	strcpy (fname, gstate->basedir);
	strcat (fname, "\\data\\botnames.txt");
	if ((f = fopen (fname, "rt")))
	{
		for (i = 0; !feof (f); i++)
			fscanf (f, "%s", bot_names [i]);

		num_names = i;

		fclose (f);
	}
	else
	{
		strcpy (bot_names [0], "Zeus");
		strcpy (bot_names [1], "Ripley");
		strcpy (bot_names [2], "The Avatar");
		strcpy (bot_names [3], "The Dark One");
		strcpy (bot_names [4], "Moridin");
		strcpy (bot_names [5], "Shaidar Haran");
		strcpy (bot_names [6], "Amoeba God");
		strcpy (bot_names [7], "Lord Brutish");
		strcpy (bot_names [8], "Centrifuge");
		strcpy (bot_names [9], "Captain Kangaroo");
		strcpy (bot_names [10], "Wannabe");
		strcpy (bot_names [11], "Mr. Bill");
		strcpy (bot_names [12], "Killroy");
		strcpy (bot_names [13], "The Reaper");
		strcpy (bot_names [14], "Cornholio");
		strcpy (bot_names [15], "The Q Continuum");
		strcpy (bot_names [16], "Vehement");

		num_names = 17;
	}

	self->netname = bot_names [(int)floor ((float)num_names * rnd ())];
}

////////////////////////
//	monster_bot
//
//	sets up an entity to be a deathmatch bot
//	does not spawn the entity, that is done
//	by the server if it is a map entity or 
//	by bot_spawn
//
//	FIXME: won't spawn directly from a map
//	because node list isn't set up correctly
////////////////////////

void	monster_bot (userEntity_t *self)
{
	playerHook_t		*hook;

	hook = ai_init_monster (self, TYPE_BOT);
	
	////////////////////////////////////////////////////////////////
	// add to actor_list, bot_list, monster_bot_list and client_bot_list
	////////////////////////////////////////////////////////////////

	alist_add (actor_list, self);
	alist_add (bot_list, self);
	alist_add (monster_bot_list, self);
	alist_add (client_bot_list, self);

	self->className = "monster_bot";

	bot_choose_netname (self);

	gstate->SetModel (self, "models/hiro.mdl");
	gstate->SetSize (self, -16.0, -16.0, -24.0, 16.0, 16.0, 32.0);

	////////////////////////////////////////////////////////////////
	// set up animation stuff
	////////////////////////////////////////////////////////////////

	ai_get_sequences (self);

	bot_init_frametable (self);

	///////////////////////////////////////////////////////////
	// set up pointers to this creature's ai functions
	///////////////////////////////////////////////////////////

	hook->begin_node_wander = ai_begin_node_wander;
	hook->begin_attack = bot_begin_attack;
//	hook->begin_follow = NULL;

	hook->think_time = 0.05;

	///////////////////////////////////////////////////////////
	//	set attributes
	///////////////////////////////////////////////////////////

	hook->dflags -= DFL_EVIL;
	hook->dflags = DFL_RANGEDATTACK + DFL_CANSTRAFE + DFL_GOOD;

	hook->run_speed = 320;
	hook->walk_speed = 320;
	hook->attack_dist = 768;

	self->health = 100;
	hook->max_health = 100;
	hook->pain_chance = 10;
	self->mass = 1.0;

	self->ang_speed [YAW] = 600.0;

	self->translucent = 0;

    self->inventory = gstate->InventoryNew();

	///////////////////////////////////////////////////////////
	//	start thinking
	///////////////////////////////////////////////////////////

	ai_get_spawn_node (self);

	task_node_wander (self);
	self->think = hook->begin_node_wander;
//	self->nextthink = gstate->time + 0.1;
	self->postthink = Client_PostThink;

	gstate->bprint ("%s joined the game\n", self->netname);
}

/////////////////////////////////////////
//	bot_get_spawnpoint
//
/////////////////////////////////////////

userEntity_t *bot_get_spawnpoint(void)
{
	userEntity_t *spot;

	if (gstate->deathmatch)
		spot = com->DeathmatchSpawnPoint ("info_player_deathmatch");
	else
	{
		spot = com->SelectSpawnPoint ("info_player_start", global_spawntarget);
		com->free (global_spawntarget);
		global_spawntarget = NULL;
	}

	return	spot;
}

////////////////////////
//	bot_spawn
//
//	spawns a deathmatch opponent
//	then calls monster_bot to initialize it
////////////////////////

void	bot_spawn (userEntity_t *self)
{
	userEntity_t	*bot, *spot;

	bot = gstate->SpawnEntity ();

	// look for place to put player
	spot = bot_get_spawnpoint ();

	if (!spot)
	{
		if (gstate->deathmatch)
			com->Warning ("No info_player_deathmatch spawn points.");
		else
			com->Warning ("No info_player_start spawn points.");
	}
	else
	{
		com->CopyVector (bot->s.origin, spot->s.origin);
		com->CopyVector (bot->s.angles, spot->s.angles);
	}

	monster_bot (bot);

//	task_node_wander (bot);
}