#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	"actorlist.h"

////////////////////////////////////////////////////////////////////////////////
// defines
////////////////////////////////////////////////////////////////////////////////

#define	PRISONER_SPEED			200
#define	REACHED_GOAL_DIST		32.0

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

DllExport	void	monster_prisoner (userEntity_t *self);
DllExport	void	func_prisoner_respawn (userEntity_t *self);

////////////////////////////////////////////////////////////////////////////////
// frame data
////////////////////////////////////////////////////////////////////////////////

static	int	prisoner_count;

///////////////////////////////////////////////////////////////////////////////////
// prisoner specific funcitons
///////////////////////////////////////////////////////////////////////////////////

#ifdef	OLDAI
/*
=============
prisoner_wander_sound

exits if sound_time is not up
otherwise, % chance a sound will be played
=============
*/

void	prisoner_wander_sound (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	float			r;

	if (hook->sound_time >= gstate->time)
		return;
/*
	// play a sound
	r = rnd ();
	if (r < 0.5)
		gstate->sound (self, CHAN_AUTO, "monsters/prisoner/prisonerwander1.wav", 255, ATTN_NORM);
	else
		gstate->sound (self, CHAN_AUTO, "monsters/prisoner/prisonerwander2.wav", 255, ATTN_NORM);
*/
	hook->sound_time = gstate->time + 10.0 + rnd () * 30.0;
}

///////////////////////////////
//	prisoner_sight_sound
//
///////////////////////////////

void	prisoner_sight_sound (userEntity_t *self)
{
/*
	if (rnd () < 0.5)
		gstate->sound (self, CHAN_AUTO, "monsters/prisoner/prisonersight1.wav", 255.0, ATTN_NORM);
	else
		gstate->sound (self, CHAN_AUTO, "monsters/prisoner/prisonersight2.wav", 255.0, ATTN_NORM);
*/
}
#endif

////////////////////////////////
//	prisoner_init_frametable
//
////////////////////////////////

void	prisoner_init_frametable (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	
	ai_set_sequence_data (self, hook->frames->atak [0], 0, "monsters/skel/skelattack1.wav", -1, NULL, 0, -1, 0, -1, FRAME_ONCE);
	
	ai_set_sequence_data (self, hook->frames->stand [0], -1, NULL, -1, NULL, 0, -1, 0, -1, FRAME_LOOP);
}

///////////////////////////////
//	prisoner_find_target
//
//	searches the entity list for targets and items
//	returns true if an item or targetable (enemy) entity was found
///////////////////////////////

int		prisoner_find_target (userEntity_t *self)
{
	userEntity_t	*head;
	userEntity_t	*enemy;
	int				dist, enemy_d;
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	enemy = NULL;
	enemy_d = 150.0;
	for (head = alist_FirstEntity (client_bot_list); head; head = alist_NextEntity (client_bot_list))
	{
		if (head->health > 0 && !(head->flags & FL_NOTARGET))
		{
			if (head->deadflag == DEAD_NO)
			{
				dist = com->Visible (self, head);

				if (dist && dist < enemy_d)
				{
					enemy = head;
					enemy_d = dist;
				}
			}
		}
	}

	if (enemy && enemy_d < 150.0)
	{
		if (hook->sight_sound) hook->sight_sound (self);		

		self->enemy = enemy;
		ai_set_goal (self, self->enemy, self->enemy->s.origin, GOAL_ENEMY);

		ai_set_think (self, hook->begin_follow, "prisoner_find_target");

		return	TRUE;
	}

	return	FALSE;
}

///////////////////////////////
//	prisoner_wander
//
///////////////////////////////

void	prisoner_wander (userEntity_t *self)
{
#ifdef	OLDAI
	int				i;
	float			dist;
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	ai_debug_info (self, "ai_wander\n", NULL);

	if (!ai_client_near (self) && self->flags & FL_ONGROUND)
	{
		com->ZeroVector (self->velocity);
		ai_set_think (self, prisoner_wander, "prisoner_wander");

		return;
	}

	if (prisoner_find_target (self))
		return;

	hook->wander_sound (self);

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

	if (dist < 16.0 || hook->goal_search_time <= gstate->time)
	{
		//ai_debug_print (self, "search time expired, choosing new wander goal\n");
		if (!ai_choose_wander_goal (self))
		{
			ai_set_think (self, hook->begin_stand, "prisoner_wander");

			return;
		}
	}

	ai_frame_sounds (self);

//	ai_face_goal (self);

	// go to goal entity
	ai_movetogoal (self, hook->walk_speed, MOVE_FORWARD);

	if (!(hook->ai_flags & AI_JUMP) && !(hook->ai_flags & AI_TURN))
		ai_set_think (self, prisoner_wander, "prisoner_wander");
#endif

	playerHook_t	*hook = (playerHook_t *) self->userHook;
	float			dist;

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

	//////////////////////////////////////////////////////////
	//	if no client is near, then stop and do nothing
	//	this saves framerate when lots of monsters are wandering
	//////////////////////////////////////////////////////////
	
	if (!ai_client_near (self) && self->flags & FL_ONGROUND)
	{
		com->ZeroVector (self->velocity);

		ai_set_think (self, ai_wander, "prisoner_wander");
		return;
	}

	if (prisoner_find_target (self))
		return;

	if (hook->wander_sound) hook->wander_sound (self);

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

	ai_frame_sounds (self);

	/////////////////////////////////////////////////////////////////////////////
	//	temporary goal seeking (to get around obstructions)
	/////////////////////////////////////////////////////////////////////////////

	if (hook->goal_type == GOAL_TEMP)
	{
		ai_debug_print (self, "ai_wander: seeking temp goal", NULL);

		ai_seek_temporary_goal (self, dist);

		// only need to check this after movetogoal since that is when ai_jump is called
		if (!(hook->ai_flags & AI_JUMP) && !(hook->ai_flags & AI_TURN))
			ai_set_think (self, ai_path_follow, "prisoner_wander");

		return;
	}

	/////////////////////////////////////////////////////////////////////////////
	//	going towards real goal
	/////////////////////////////////////////////////////////////////////////////

	if (dist < REACHED_GOAL_DIST)// || hook->goal_search_time <= gstate->time)
	{
		if (!ai_choose_wander_goal (self))
		{
			ai_set_think (self, hook->begin_stand, "prisoner_wander");
			return;
		}
	}

	// go to goal entity
	ai_movetogoal (self, hook->walk_speed, MOVE_FORWARD);

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

}

///////////////////////////////
//	prisoner_begin_wander
//	
///////////////////////////////

void	prisoner_begin_wander (userEntity_t *self)
{
#ifdef	OLDAI
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	hook->th_state = TH_WANDER;

	if (!ai_choose_wander_goal (self))
	{
		self->nextthink = gstate->time + THINK_TIME;
		self->think = hook->begin_stand;
		return;
	}

	self->s.frame = hook->frames->walk [0]->first;
	hook->startFrame = self->s.frame;
	hook->endFrame = hook->frames->walk [0]->last;
	hook->frame_flags = FRAME_LOOP;
	hook->frameInc = 1;
	
	// frames on which we want to play a sound
	hook->sound1Frame = -1;
	hook->sound1 = NULL;
	hook->sound2Frame = -1;
	hook->sound2 = NULL;	
#endif

	playerHook_t	*hook = (playerHook_t *) self->userHook;

	hook->th_state = TH_WANDER;

	//	set up a new goal
	if (!ai_choose_wander_goal (self))
	{
		ai_set_think (self, hook->begin_stand, "prisoner_begin_wander");

		return;
	}

	ai_init_sequence (self, hook->frames->walk [0], SEQUENCE_WALK, 0);

	prisoner_wander (self);
}

/////////////////////////
//	prisoner_cower
//
/////////////////////////

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

	//ai_face_enemy (self);
	hook->dflags |= DFL_FACEENEMY;

//	ai_frame_sounds (self);

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

	if (dist > 200.0)
	{
		if (self->spawnflags & SPAWN_WANDER)
			// do a transition from cowering to standing
			hook->begin_transition (self, hook->frames->special [0], SEQUENCE_SPECIAL, 0, hook->begin_wander);
		else
			hook->begin_transition (self, hook->frames->special [0], SEQUENCE_SPECIAL, 0, hook->begin_stand);

		return;
	}

	if (hook->frame_flags & FRAME_STATIC)
	{
		ai_init_sequence (self, hook->frames->special [0], SEQUENCE_SPECIAL, 0);

		hook->frame_flags = FRAME_LOOP;
		hook->frameInc = 1;
	}

	ai_set_think (self, prisoner_cower, "prisoner_cower");
}

///////////////////////////////
//	prisoner_begin_cower
//	
//	prisoner cowers and faces his enemy
///////////////////////////////

void	prisoner_begin_cower (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	com->ZeroVector (self->velocity);

	hook->th_state = TH_ATTACK;

	ai_init_sequence (self, hook->frames->special [0], SEQUENCE_SPECIAL, 0);

	hook->frame_flags = FRAME_ONCE;
	hook->frameInc = 1;
	
	ai_set_sequence_sounds (self, self->s.frame + 1, "monsters/prisoner/prisonertalk1.wav", -1, NULL);

	prisoner_cower (self);
}

/*
=============
prisoner_stand

=============
*/

void	prisoner_stand (userEntity_t *self)
{
	int				i;
	float			dist;
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	playerHook_t	*ohook = (playerHook_t *) hook->owner->userHook;

	if (prisoner_find_target (self))
		return;

	ai_frame_sounds (self);

	// if self is too far away from master then begin to follow
	ai_set_think (self, prisoner_stand, "prisoner_stand");
}

/*
=============
prisoner_begin_stand

=============
*/

void	prisoner_begin_stand	(userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	hook->th_state = TH_STAND;

	com->ZeroVector (self->velocity);
	
	self->enemy = self;
	ai_set_goal (self, self, self->s.origin, GOAL_ENEMY);

	ai_init_sequence (self, hook->frames->stand [0], SEQUENCE_STAND, 0);

	com->ZeroVector (self->velocity);
	ai_set_goal (self, self, self->s.origin, GOAL_TEMP);
#ifdef	OLDAI
	self->s.frame = hook->frames->stand [0]->first;
	hook->startFrame = self->s.frame;
	hook->endFrame = hook->frames->stand [0]->last;
	hook->frame_flags = FRAME_STATIC;
	hook->frameInc = 1;	
#endif
	prisoner_stand (self);
}

/*
=========
prisoner_holy_shit_the_inmater_has_it_in_me

special think function for when an inmater is gibbing a prisoner
=========
*/

void	prisoner_holy_shit_the_inmater_has_it_in_me	(userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	int				i;
	userEntity_t	*ent;

	gstate->Con_Dprintf ("Holy shit!  The inmater is in me!!!\n");

	com->SetVector (self->view_ofs, 0, 0, -8.0);
	self->deadflag = DEAD_DYING;
	self->takedamage = DAMAGE_NO;
	self->solid = SOLID_NOT;
	self->movetype = MOVETYPE_BOUNCE;

	i = (int)(rnd () * 2);
	self->s.frame = hook->frames->death [1]->first;
	hook->endFrame = hook->frames->death [1]->last;
	hook->startFrame = self->s.frame;
	hook->frame_flags = FRAME_ONCE;
	hook->frameInc = 1;	

	ai_set_sequence_sounds (self, -1, NULL, -1, NULL);

	if (self->target)
	{
		ent = com->FindTarget (self->target);
		if (ent)
		{
			if (ent->use)
			{
				//	use the prisoner respawn
				ent->goalentity = self;
				ent->use (ent, self, self);
			}
		}
	}

//	gstate->sound (self, CHAN_AUTO, "monsters/prisoner/prisonerdeath1.wav", 255, ATTN_NORM);

	ai_die (self);
}

/*
=============
monster_prisoner
=============
*/

void	monster_prisoner (userEntity_t *self)
{
	playerHook_t		*hook;
/*
	prisoner_count++;
	if (prisoner_count > 1)
	{
		gstate->RemoveEntity (self);
		return;
	}
*/
	hook = ai_init_monster (self, TYPE_PRISONER);

	self->className = "monster_prisoner";
	self->netname = "prisoner";

	// precache the little bastard's sound
/*
	gstate->Precache_Sound ("monsters/prisoner/prisonerattack1.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonerattack2.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonerdeath1.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonerdeath2.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonerpain1.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonerpain2.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonersight1.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonersight2.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonerwander1.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonerwander2.wav");
*/
	gstate->Precache_Model ("models/prisoner.mdl");
	gstate->SetModel (self, "models/prisoner.mdl");
	gstate->SetSize (self, -16.0, -16.0, -24.0, 16.0, 16.0, 32.0);

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

//	ai_get_frames (self, "cowr", &cowerFirst [0], &cowerLast [0]);
//	ai_get_frames (self, "situp", &situpFirst [0], &situpLast [0]);

	ai_get_sequences (self);
	ai_get_sequence (self, "cowr", &hook->frames->special [0]);

	prisoner_init_frametable (self);

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

	hook->begin_wander = prisoner_begin_wander;	
	hook->begin_stand = prisoner_begin_stand;
	hook->begin_follow = prisoner_begin_cower;
	hook->special_think = prisoner_holy_shit_the_inmater_has_it_in_me;

	hook->run_speed = 25;
	hook->walk_speed = 25;

	self->spawnflags = self->spawnflags | SPAWN_WANDER;

	self->think = ai_get_spawn_node;
	self->nextthink = gstate->time + 0.2;	// node links are set up at 0.1 after map load
}

///////////////////////
// prisoner_respawn
///////////////////////

void	prisoner_respawn (userEntity_t *self)
{
	playerHook_t		*hook;
	int					i;

	hook = ai_init_monster (self, TYPE_PRISONER);

	self->className = "monster_prisoner";
	self->netname = "prisoner";

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

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

//	ai_get_frames (self, "cowr", &cowerFirst [0], &cowerLast [0]);
//	ai_get_frames (self, "situp", &situpFirst [0], &situpLast [0]);

	ai_get_sequences (self);
	ai_get_sequence (self, "cowr", &hook->frames->special [0]);

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

	hook->begin_wander = prisoner_begin_wander;	
	hook->begin_stand = prisoner_begin_stand;
	hook->begin_follow = prisoner_begin_cower;
	hook->special_think = prisoner_holy_shit_the_inmater_has_it_in_me;

	hook->run_speed = 25;
	hook->walk_speed = 25;

	self->spawnflags = self->spawnflags | SPAWN_WANDER;

	self->think = ai_get_spawn_node;
	self->nextthink = gstate->time + 0.2;	// node links are set up at 0.1 after map load
}

///////////////////////////
// respawn_drop
///////////////////////////

void	respawn_drop (userEntity_t *self)
{
	userEntity_t	*temp;
	playerHook_t	*thook = (playerHook_t *) self->goalentity->userHook;
	playerHook_t	*hook;

	gstate->Con_Dprintf ("respawn_drop\n");
/*
	temp = self->goalentity;
	temp->deadflag = DEAD_NO;
	temp->takedamage = DAMAGE_YES;
	temp->movetype = MOVETYPE_STEP;
	temp->health = 100.0;
	temp->solid = SOLID_BBOX;

	com->SetVector (self->view_ofs, 0, 0, -8.0);
	gstate->SetOrigin2 (temp, self->s.origin);
	com->CopyVector (temp->s.angles, self->s.angles);

	temp->prethink = ai_prethink;
	temp->think = thook->begin_wander;
	temp->nextthink = gstate->time + 0.1;
*/
	temp = gstate->SpawnEntity ();
	if (temp)
	{
		temp->target = self->targetname;
		com->CopyVector (temp->s.origin, self->s.origin);
		com->CopyVector (temp->s.angles, self->s.angles);

		prisoner_respawn (temp);
	}

	self->nextthink = -1;
	self->think = NULL;
}

///////////////////////////
// respawn_use
///////////////////////////

void	respawn_use (userEntity_t *self, userEntity_t *other, userEntity_t *activator)
{
	gstate->Con_Dprintf ("respawn_use, time = %f : %f\n", gstate->time, self->delay);

	self->nextthink = gstate->time + self->delay;
	self->think = respawn_drop;
}

///////////////////////////
// func_prisoner_respawn
///////////////////////////

void	func_prisoner_respawn (userEntity_t *self)
{
	int		i;

	self->delay = 1.0;

	for	(i = 0; self->epair [i].key != NULL; i++)
	{
		if (!stricmp (self->epair [i].key, "targetname"))
			self->targetname = self->epair [i].value;
		else if (!stricmp (self->epair [i].key, "time"))
			self->delay = atof (self->epair [i].value);
	}

	self->use = respawn_use;
	self->modelName = NULL;
}