#include	"world.h"

#include	"ai.h"			//	stuff global to all ai_ files
#include	"ai_utils.h"
#include	"ai_fly.h"
#include	"ai_move.h"

#include	"nodelist.h"
#include	"actorlist.h"

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

#define	TURN_RIGHT		1
#define	TURN_LEFT		2


////////////////////////////////
//	ai_should_fly
//
//	determines if current location is
//	open enough for a walking/flying 
//	monster to fly
////////////////////////////////

int	ai_should_fly (userEntity_t *self)
{
	Vector	end, org, ang;
	float	tr_dist [4], avg;

	ang = Vector (self->s.angles);
	ang.AngleVectors (forward, right, up);

	org = Vector (self->s.origin);

	end = org;
	end.z += 256;

	gstate->TraceLine (self->s.origin, end.vec (), FALSE, self, &trace);

	if (trace.fraction < 128.0)
		return	FALSE;

	org.z += 182.0;

	//	trace out approximately 256 units forward and up
	end = org + (forward * 182.0);
	gstate->TraceLine (self->s.origin, end.vec (), FALSE, self, &trace);
	tr_dist [0] = trace.fraction * 256.0;

	//	trace out approximately 256 units backwards and up
	end = org - (forward * 182.0);
	gstate->TraceLine (self->s.origin, end.vec (), FALSE, self, &trace);
	tr_dist [1] = trace.fraction * 256.0;

	//	trace out approximately 256 units right and up
	end = org + (right * 182.0);
	gstate->TraceLine (self->s.origin, end.vec (), FALSE, self, &trace);
	tr_dist [2] = trace.fraction * 256.0;

	//	trace out approximately 256 units
	end = org - (right * 182.0);
	end.z += 256;
	gstate->TraceLine (self->s.origin, end.vec (), FALSE, self, &trace);
	tr_dist [3] = trace.fraction * 256.0;

	avg = tr_dist [0] + tr_dist [1] + tr_dist [2] + tr_dist [3];
	avg = avg / 4;

	if (avg > 128.0)
		return	TRUE;
	
	return	FALSE;
}

////////////////////////////////
//	ai_fly_choose_turn
//
//	line ai_choose_turn, but doesn't set goal, and returns the
//	yaw of the chosen direction
////////////////////////////////

float	ai_fly_choose_turn (userEntity_t *self, Vector dest, Vector normal)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	Vector			dir, newdir, end;
	float			dot, yaw_dir;

	dir = Vector (self->s.origin) - dest;
	dir.z = 0;
	dir.Normalize ();

	//	right of wall normal
	newdir = Vector (normal.y, - normal.x, 0.0);
	
	// get cos of angle between goal direction and vector along wall
	dot = dir ^ newdir;

	if (dot > 0)
	{
		dir.x = - normal.y;
		dir.y = normal.x;

		hook->turn_dir = TURN_RIGHT;
	}
	else
	{
		dir.x = normal.y;
		dir.y =  - normal.x;
		
		hook->turn_dir = TURN_LEFT;
	}

	return	com->VecToYaw (dir);
}

////////////////////////////////
//	ai_flytogoal_z
//
//	flies directly to a goal
////////////////////////////////

void	ai_flytogoal_z (userEntity_t *self, float speed, int move_dir)
{

}

////////////////////////////////
//	ai_flytogoal
//
//	flies to a goal while remaining
//	at the same hieght if at all possible,
//	ie. this routine doesn't do up/down
//	movement.
//
//	if it is determined that up/down movement
//	is required to reach a goal, then ai_flytogoal_z
//	is called.
////////////////////////////////

void	ai_flytogoal (userEntity_t *self, float speed, int move_dir)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	Vector			dir, end, wall_normal, temp, ang, org;
	float			dist, yaw, move_dist, temp_speed;
	int				ter;

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

	// get forward, up and right vectors
	if (move_dir == MOVE_STRAFE || hook->dflags & DFL_FLYSWOOP)
	{
		// forward is direction to goal, but entity should not face it
		if (hook->dflags & DFL_FLYSWOOP)
			forward = (Vector (self->goalentity->s.origin) + Vector (0, 0, hook->fly_goal_offset)) - Vector (self->s.origin);
		else
			forward = Vector (self->goalentity->s.origin) - Vector (self->s.origin);

		forward.Normalize ();
		// get right vector
		right = Vector (forward.y, - forward.x, 0);

		// get up vector
		up = -1 * (forward % right);
		up.Normalize ();

		//	make entity face self->enemy
		hook->dflags |= DFL_FACEENEMY;
	}
	else if (move_dir == MOVE_UP)
	{
		forward = Vector (0, 0, 1.0);

		//	make entity face self->goalentity
		hook->dflags -= (hook->dflags & DFL_FACEENEMY);
	}
	else if (move_dir == MOVE_DOWN)
	{
		forward = Vector (0, 0, -1.0);

		//	make entity face self->goalentity
		hook->dflags -= (hook->dflags & DFL_FACEENEMY);
	}
	else
	{
		ang = Vector (self->s.angles);
		ang.x = ang.z = 0;

		ang.AngleVectors (forward, right, up);

		//	make entity face self->goalentity
		hook->dflags -= (hook->dflags & DFL_FACEENEMY);
	}

	org = Vector (self->s.origin);
	move_dist = hook->last_origin.vdist (org);

	/////////////////////////////////////////////////////////////////
	//	find the height of the ground
	/////////////////////////////////////////////////////////////////

	temp = Vector (self->s.origin);
	temp.z -= 1000;

	//	Q2KILL
	//	gstate->TraceBox (self->s.origin, temp.vec (), self->mins, self->maxs, FALSE, self, &trace);
	tr = gstate->trace_q2 (self->s.origin, self->s.mins, self->s.maxs, temp.vec (), self, CHOOSE_CLIP(self));

	hook->ground_org = Vector (tr.endpos);
//	com->SpawnMarker (self, Vector (trace.endpos), "models/debug/jumptarg.mdl", 0.05);

	/////////////////////////////////////////////////////////////////
	//	determine if there is a clear path to our goal
	/////////////////////////////////////////////////////////////////

	dir = forward;

	if (move_dir == MOVE_BACKWARD)
		dir = dir * -1;

	ter = ai_terrain_type (self, dir, 32.0);

	/////////////////////////////////////////////////////////////////
	//	if stairs, go up
	/////////////////////////////////////////////////////////////////
	if (terrain.wall_type == TER_STEPSUP)
	{
//		gstate->Con_Printf ("ai_flytogoal: TER_STEPSUP\n");
		dir = up;
	}
	/////////////////////////////////////////////////////////////////
	//	if a high obstruction, go down
	/////////////////////////////////////////////////////////////////
	if (terrain.wall_type == TER_HIGH_OBS)
	{
//		gstate->Con_Printf ("ai_flytogoal: TER_HIGH_OBS\n");
		dir = up * -1;
	}
	/////////////////////////////////////////////////////////////////
	//	if a grade up, follow it
	/////////////////////////////////////////////////////////////////
	else if (terrain.wall_type == TER_GRADEUP)
	{
//		gstate->Con_Printf ("ai_flytogoal: TER_GRADEUP\n");
		dir = Vector (0, 0, terrain.wall_normal.z);
	}
	/////////////////////////////////////////////////////////////////
	//	no clear path, look for a door at ground level
	/////////////////////////////////////////////////////////////////

	/////////////////////////////////////////////////////////////////
	//	no clear path, so attempt move around obstruction
	/////////////////////////////////////////////////////////////////
	else if (terrain.wall_type > TER_FAR_WALL)//if (trace.fraction < 1.0)
	{
		wall_normal = Vector (trace.planeNormal);
/*		
		if (wall_normal.z != 0)
		{
			////////////////////////////////////////////////////////////
			//	fly up or down along slanted walls
			////////////////////////////////////////////////////////////
			
			if (self->goalentity->s.origin [2] - self->s.origin [2] < 0 && move_dir != MOVE_DOWN)
				ai_flytogoal (self, speed, MOVE_DOWN);
			else if (move_dir != MOVE_UP)
				ai_flytogoal (self, speed, MOVE_UP);

		}
*/
		if (move_dir == MOVE_FORWARD && terrain.wall_type == TER_WALL_RIGHT)
		{
			hook->strafe_dir = STRAFE_LEFT;
			hook->strafe_time = gstate->time + hook->think_time;

			yaw = self->s.angles [1];
			temp_speed = hook->attack_speed;
			hook->attack_speed = speed;
			ai_strafetogoal (self, MOVE_STRAFE);
			self->s.angles [1] = yaw;
			hook->attack_speed = temp_speed;
			
			return;
		}
		else if (move_dir == MOVE_FORWARD && terrain.wall_type == TER_WALL_LEFT)
		{
			hook->strafe_dir = STRAFE_RIGHT;
			hook->strafe_time = gstate->time + hook->think_time;

			yaw = self->s.angles [1];
			temp_speed = hook->attack_speed;
			hook->attack_speed = speed;
			ai_strafetogoal (self, MOVE_STRAFE);
			self->s.angles [1] = yaw;
			hook->attack_speed = temp_speed;

			return;
		}

		else if (!hook->turn_dir)
		{
			////////////////////////////////////////////////////////////
			//	choose a direction to go around obstruction
			////////////////////////////////////////////////////////////

			hook->turn_yaw = ai_choose_turn (self, self->goalentity->s.origin, wall_normal);
			dir = Vector (0, hook->turn_yaw, 0);
			dir.AngleVectors (dir, right, up);
		}
		else
		{
			////////////////////////////////////////////////////////////
			//	last turn didn't get us unstuck, so turn again in the
			//	same direction
			////////////////////////////////////////////////////////////

			if (hook->turn_yaw > 0)
			{
				self->ideal_ang [1] = self->s.angles [1] + 90;
				ai_debug_print (self, "wall: stuck, turning 90, md = %.2f\n", move_dist);
			}
			else
			{
				self->ideal_ang [1] = self->s.angles [1] - 90;
				ai_debug_print (self, "wall: stuck, turning -90, md = %.2f\n", move_dist);
			}

			hook->begin_turn (self);

			return;
		}
	}
	else
	{
		hook->dflags -= (hook->dflags & DFL_TURNED);
		hook->turn_dir = 0;
	}

	// face the goal entity
/*
	if (move_dir == MOVE_STRAFE && self->enemy)
		ai_face_enemy (self);
	else 
		ai_face_goal (self);
*/
	//	support for old, think-base turning
	if (hook->dflags & DFL_FACENONE)
	{
		// face the goal entity
		if (move_dir == MOVE_STRAFE && self->enemy)
			ai_face_enemy (self);
		else
			ai_face_goal (self);
	}

	temp = dir * speed;
	temp.set (self->velocity);

	///////////////////////////////////////////////////////////////////
	//	turn off the FRICKIN FL_ONGROUND flag so we can FRICKIN move
	///////////////////////////////////////////////////////////////////

	self->flags -= (self->flags & FL_ONGROUND);

	//	FIXME: should flying entities drop nodes?
	node_place_node (self, hook->node_list, hook->last_origin.vec ());
	hook->last_origin = Vector (self->s.origin);
}


