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

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


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

DllExport	void	monster_inmater (userEntity_t *self);

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

static	float	flash_distance;
static	float	flash_delta_yaw;  // difference between entities view angle and angle to explosion

static	int		inmater_count;

///////////////////////////////////////////////////////////////////////////////////
// inmater specific funcitons
///////////////////////////////////////////////////////////////////////////////////

void	inmater_begin_attack (userEntity_t *self);

////////////////////////////////
//	inmater_init_frametable
//
////////////////////////////////

void	inmater_init_frametable (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	
	ai_set_sequence_data (self, hook->frames->run [0], hook->frames->run [0]->last - hook->frames->run [0]->first, "monsters/inmater/inmaterstep.wav", self->s.frame + 14, "monsters/inmater/inmaterstep.wav", 0, -1, 0, -1, FRAME_LOOP);
	ai_set_sequence_data (self, hook->frames->walk [0], hook->frames->walk [0]->last - hook->frames->walk [0]->first, "monsters/inmater/inmaterstep.wav", self->s.frame + 14, "monsters/inmater/inmaterstep.wav", 0, -1, 0, -1, FRAME_LOOP);

	ai_set_sequence_data (self, hook->frames->atak [0], 0, "monsters/inmater/inmatergibs.wav", -1, NULL, SEQUENCE_NONE, -1, SEQUENCE_NONE, -1, FRAME_ONCE);
	ai_set_sequence_data (self, hook->frames->atak [1], 0, "monsters/inmater/inmatergibs.wav", -1, NULL, SEQUENCE_NONE, -1, SEQUENCE_NONE, -1, FRAME_ONCE);
	ai_set_sequence_data (self, hook->frames->atak [3], 0, "monsters/inmater/inmatergibs.wav", -1, NULL, SEQUENCE_NONE, -1, SEQUENCE_NONE, -1, FRAME_ONCE);

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

////////////////////////////////
//	inmater_set_attack_seq
//
////////////////////////////////

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

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

//////////////////////////////
//	inmater_mate
//	
//	inmater is attacking a prisoner
//////////////////////////////

void	inmater_mate (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	userEntity_t	*pris = self->goalentity->owner;
	playerHook_t	*phook = (playerHook_t *) pris->userHook;
	Vector			dir;

	//////////////////////////////////////////////
	//	lift the prisoner
	//////////////////////////////////////////////
	
	if (hook->frame_flags & FRAME_PLAYSOUND1)
	{
		self->gravity = 0.5;

		com->SetVector (pris->velocity, 0, 0, 375.0);
		pris->flags = pris->flags - FL_ONGROUND;

		if (hook->sound1) gstate->sound (self, CHAN_AUTO, hook->sound1, 128, ATTN_NORM);
		hook->frame_flags -= (hook->frame_flags & FRAME_PLAYSOUND1);
	}

	//////////////////////////////////////////////
	//	throw the prisoner
	//////////////////////////////////////////////

	if (hook->frame_flags & FRAME_PLAYSOUND2)
	{
		self->gravity = 1.0;
		dir = Vector (pris->s.origin) - Vector (self->s.origin);
		dir = Vector (self->s.angles);
		dir.AngleVectors (forward, right, up);
		dir = forward;

		dir = dir * 250.0;
		dir.set (pris->velocity);
		pris->velocity [2] = 250;
		pris->flags = pris->flags - (pris->flags & FL_ONGROUND);

		if (hook->sound2) gstate->sound (self, CHAN_AUTO, hook->sound2, 128, ATTN_NORM);
		hook->frame_flags -= (hook->frame_flags & FRAME_PLAYSOUND2);
	}

	//////////////////////////////////////////////
	//	go back to wandering
	//////////////////////////////////////////////
	
	if (hook->frame_flags & FRAME_STATIC)
	{
		ai_debug_print (self, "going back to path_wander\n");

		hook->attack_dist = 256.0;
		hook->begin_attack = inmater_begin_attack;
		hook->dflags |= DFL_RANGEDATTACK;
		hook->ai_flags -= AI_ACTION;

		//	pop everythin up to and thru the TASK_CHASE for the prisoner
		ai_killed_enemy (self);

//		hook->begin_path_wander (self);

		gstate->sound (self, CHAN_AUTO, "monsters/inmater/inmatercomplete.wav", 255, ATTN_NORM);
		return;
	}
	else
		self->think = inmater_mate;

	self->nextthink = gstate->time + THINK_TIME;
}


//////////////////////////////
//  inmater_begin_mating
//
//	start inmater attacking a prisoner
//////////////////////////////

void	inmater_begin_mating (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	userEntity_t	*pris = self->goalentity->owner;
	playerHook_t	*phook = (playerHook_t *) pris->userHook;
	int				i;

	com->ZeroVector (self->velocity);

	ai_init_sequence (self, hook->frames->atak [3], SEQUENCE_ATAK, 3);

	inmater_mate (self);

	////////////////////////////////////////////////////////////////
	//	make the prisoner go into his appropriate death frame
	////////////////////////////////////////////////////////////////
	
	if (pris->className)
		ai_debug_print (self, "prisoner classname = %s", pris->className);

	ai_set_think (pris, phook->special_think, "inmater_begin_mating");	
}

//////////////////////////////
//	inmater_find_prisoner
//
//	finds the closest visible prisoner for the inmater to gib
//////////////////////////////

int	inmater_find_prisoner (userEntity_t *self)
{
	userEntity_t	*head, *enemy;
	float			dist, last_d;

	last_d = 256.0;
	enemy = NULL;
	head = alist_FirstEntity (monster_list);

	while (head)
	{
		if (head->className && head->deadflag == DEAD_NO)
		{
			if (!stricmp (head->className, "monster_prisoner"))
			{
				dist = qv_dist (self->s.origin, head->s.origin);
				if (dist < last_d)
				{
					if (com->Visible (self, head))
					{
						enemy = head;
						last_d = dist;
					}
				}
			}
		}

		head = alist_NextEntity (monster_list);
	}

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

		return	TRUE;
	}

	return	FALSE;
}

//////////////////////////////
//	inmater_wait
//
//	inmater has turned towards prisoner and
//	is waiting a short time before gibbing prisoner
//////////////////////////////

void	inmater_wait	(userEntity_t *self)
{
	int				i;
	float			r;
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	playerHook_t	*ehook = (playerHook_t *) self->enemy->userHook;
	userEntity_t	*prisoner;

	if (self->goalentity->owner)
		ai_debug_print (self, "goal is %s", self->goalentity->owner->className);
	else
		ai_debug_print (self, "goal is temporary\n");

	if (hook->attack_finished <= gstate->time)
	{
		/////////////////////////////////////////
		//	turn off the targetted forcefield
		/////////////////////////////////////////

		if (self->owner)
		{
			if (self->owner->think)
				self->owner->use (self->owner, self, self);
		}

		/////////////////////////////////////////
		//	make the prisoner cower 
		/////////////////////////////////////////
		
		ehook = (playerHook_t *) self->enemy->userHook;
		prisoner = self->enemy;
		prisoner->enemy = self;

		//	make the prisoner follow the inmater
		ai_set_goal (prisoner, prisoner->enemy, prisoner->enemy->s.origin, GOAL_ENEMY);
		ehook->begin_follow (prisoner);

		/////////////////////////////////////////
		//	make the inmater follow the prisoner
		/////////////////////////////////////////

		//	push a TASK_CHASE on the inmater's task stack
		task_chase (self, self->enemy);

		//	set up the inmater to pork the prisoner rather than attack normally
		hook->begin_attack = inmater_begin_mating;
		hook->attack_dist = 64.0;
		hook->dflags -= DFL_RANGEDATTACK;

		hook->begin_follow (self);
		hook->ai_flags |= AI_ACTION;

		/////////////////////////////////////////
		//	make the prisoner scream
		/////////////////////////////////////////

		r = (int) (rnd () * 3);
		if (r == 0)
			gstate->sound (self, CHAN_AUTO, "monsters/prisoner/prisonerno1.wav", 255, ATTN_NORM);
		else if (r == 1)
			gstate->sound (self, CHAN_AUTO, "monsters/prisoner/prisonerno2.wav", 255, ATTN_NORM);
		else
			gstate->sound (self, CHAN_AUTO, "monsters/prisoner/prisonerno3.wav", 255, ATTN_NORM);

		return;
	}

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

//////////////////////////////
//	inmater_begin_wait
//
//	inmater has turned towards prisoner and
//	is waiting a short time before gibbing prisoner
//////////////////////////////

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

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

	inmater_wait (self);
}

///////////////////////////////
//	inmater_begin_action
//
//	inmater has reached a path corner with an
//	action field.  Determine if there is an
//	action here that the inmater can perform
//	and if so, start doing it.
///////////////////////////////

int	inmater_begin_action (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	userEntity_t	*corner = self->goalentity->owner;
	mpathHook_t		*chook = (mpathHook_t *) corner->userHook;
	userEntity_t	*target;
	int				i;
	Vector			dir;

	ai_debug_print (self, "inmater_begin_action\n");

	///////////////////////////////////////////////////////////
	//	search through all the actions on this path corner
	///////////////////////////////////////////////////////////

	for (i = 0; i < 4 && chook->action [i] != NULL; i++)
	{
		if (strstr (chook->action [i], "mate") == chook->action [i])
		{
			//	the first 4 letters of this action are "mate"
			//	the rest of the string should be the targetname of
			//	a forcefield

			//	save the path_corner's last target
			self->target = corner->targetname;
			
			//	find the targetted forcefield and save it as inmater's owner
			self->owner = com->FindTarget (&chook->action [i][5]);

			if (self->owner)
				ai_debug_print (self, "forcefield %s targetted.\n", self->owner->className);
			else
				com->Error ("No forcefield targetted for mating!!!\n");

			/////////////////////////////////////////////////////
			//	find a prisoner and begin turning towards him
			/////////////////////////////////////////////////////

			if (inmater_find_prisoner (self))
			{
				//camera_set_target (hook->camera, self->enemy);

				///////////////////////////////////////////////////////
				//	do a turn towards prisoner, then go to begin_wait
				///////////////////////////////////////////////////////
				
				hook->ai_flags |= AI_CONTINUE;
				hook->continue_think = inmater_begin_wait;

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

				//	wait 1.5 seconds before killing prisoner
				hook->attack_finished = gstate->time + 1.5;

				//////////////////////////////////////////////////////
				//	play "prisoner targetted..." sound
				//////////////////////////////////////////////////////

				if (rnd () < 0.1)
					gstate->sound (self, CHAN_AUTO, "monsters/inmater/inmatertarget1.wav", 255, ATTN_NORM);
				else
					gstate->sound (self, CHAN_AUTO, "monsters/inmater/inmatertarget2.wav", 255, ATTN_NORM);

				hook->ai_flags |= AI_ACTION;

				return TRUE;
			}
			else if (self->owner)
			{
				// toggle forcefield if it is off
				if (!self->owner->think)
					self->owner->use (self->owner, self, self);
			}
			else 
				return FALSE;
		}
	}

	return	FALSE;
}

///////////////////////////////////////////////////////////////////////////////
//	inmater_end_attack
//
//	ai_should_follow sets up appropriate think
///////////////////////////////////////////////////////////////////////////////

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

	ai_should_follow (self);

	//	leaving, so pop off TASK_ATTACK
	task_pop (&hook->task_stack);
}

//////////////////////////////
//	inmater_attack
//
//	ai for following/approaching an enemy
//////////////////////////////

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

	hook->dflags |= DFL_FACEENEMY;

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

	if (hook->frame_flags & FRAME_PLAYSOUND1)
	{
		hook->frame_flags = hook->frame_flags - FRAME_PLAYSOUND1;
				
		ai_fire_curWeapon (self, Vector (self->s.origin), Vector (self->enemy->s.origin), 0, 0);
	}
/*	
	if (hook->frame_flags & FRAME_PLAYSOUND2)
	{
		hook->frame_flags = hook->frame_flags - FRAME_PLAYSOUND2;
				
		ai_fire_curWeapon (self, Vector (self->s.origin), Vector (self->enemy->s.origin), 0, 0);
	}
*/	
	if (hook->frame_flags & FRAME_STATIC)
	{
		hook->frame_flags -= FRAME_STATIC;

		if (self->enemy->deadflag != DEAD_NO)
		{
			//	transition back to stand, then go to ai_killed_enemy
			ai_begin_transition (self, hook->frames->trans [1], SEQUENCE_TRANS, 1, ai_killed_enemy);

			return;
		}
		else if (dist > hook->attack_dist || !com->Visible (self, self->enemy))
		{
			//	transition back to stand, then go to inmater_end_attack
			ai_begin_transition (self, hook->frames->trans [1], SEQUENCE_TRANS, 1, inmater_end_attack);

			return;
		}
		else
			inmater_set_attack_seq (self);
	}

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

///////////////////////////////
//	inmater_loop_attack
//
//	ai for following/approaching an enemy
///////////////////////////////

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

	inmater_set_attack_seq (self);

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

	inmater_attack (self);
}


///////////////////////////////
//	inmater_begin_attack
//
//	ai for following/approaching an enemy
///////////////////////////////

void	inmater_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_begin_transition (self, hook->frames->trans [0], SEQUENCE_TRANS, 0, inmater_loop_attack);
}

/////////////////////////////
//	monster_inmater
//
//	smacks you down
/////////////////////////////

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

	inmater_count++;

//	if (inmater_count != 14)
//		return;

	self->className = "monster_inmater";
	self->netname = (char *) com->malloc (16, MEM_MALLOC);//"roboco inmater";

	sprintf (self->netname, "inmater %i", inmater_count);

	hook = ai_init_monster (self, TYPE_INMATER);

	gstate->Precache_Sound ("monsters/inmater/inmaterfire.wav");
	gstate->Precache_Sound ("monsters/inmater/corditebounce2.wav");
	gstate->Precache_Sound ("monsters/inmater/corditebounce3.wav");
	gstate->Precache_Sound ("monsters/inmater/corditebounce4.wav");
	gstate->Precache_Sound ("monsters/inmater/corditebounce5.wav");
	gstate->Precache_Sound ("monsters/inmater/corditebounce6.wav");
	gstate->Precache_Sound ("monsters/inmater/corditeexplode.wav");
	
	gstate->Precache_Sound ("monsters/inmater/inmaterstep.wav");
//	gstate->Precache_Sound ("monsters/inmater/inmaterstep.wav");

	gstate->Precache_Sound ("monsters/inmater/inmaterpain1.wav");
	gstate->Precache_Sound ("monsters/inmater/inmaterpain2.wav");
	gstate->Precache_Sound ("monsters/inmater/inmatersight1.wav");
	gstate->Precache_Sound ("monsters/inmater/inmatersight2.wav");
	gstate->Precache_Sound ("monsters/inmater/inmaterdeath1.wav");
	gstate->Precache_Sound ("monsters/inmater/inmaterdeath2.wav");
	gstate->Precache_Sound ("monsters/inmater/inmaterwander1.wav");
	gstate->Precache_Sound ("monsters/inmater/inmaterwander2.wav");

	gstate->Precache_Sound ("monsters/inmater/inmatertarget1.wav");
	gstate->Precache_Sound ("monsters/inmater/inmatertarget2.wav");
	gstate->Precache_Sound ("monsters/inmater/inmatergibs.wav");
	gstate->Precache_Sound ("monsters/inmater/inmatercomplete.wav");

	gstate->Precache_Sound ("monsters/prisoner/prisonerno1.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonerno2.wav");
	gstate->Precache_Sound ("monsters/prisoner/prisonerno3.wav");

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

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

	ai_get_sequences (self);

//	inmater_init_frametable (self);

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

	hook->begin_jump = NULL;
	hook->begin_attack = inmater_begin_attack;
	hook->begin_action = (think_t) inmater_begin_action;

	hook->run_speed = 40;
	hook->walk_speed = 40;
	hook->attack_dist = 250;
	self->ang_speed [YAW] = 180;

	self->mass = 5.0;		// about 2000 pounds
	
	com->SetVector (self->s.render_scale, 1.3, 1.3, 1.3);

	hook->max_jump_dist = 0;
	hook->attack_dist = 128.0;
	hook->max_health = 200.0;
	self->health = 200.0;

	hook->dflags |= DFL_RANGEDATTACK;

	self->think = ai_get_spawn_node;
	self->nextthink = gstate->time + 0.2;

	//////////////////////////////////////////////
	//	give inmater its uh, what... uh...
	//////////////////////////////////////////////
/*
	self->curWeapon = ai_weapon_create ("melee", melee_swing, ai_weapon_command);
	i = 10;
	ai_weapon_command (self->curWeapon, "base_damage", &i);
	ai_weapon_command (self->curWeapon, "rnd_damage", &i);
*/
	self->curWeapon = ai_init_weapon (self, 10, 10, 0, 0, 0, 128.0, Vector (0, 0, 0), "punch", melee_punch, WEAPON_TRACE);

}

