/*
 */

// gs_players.c	-	player model animations

#include "../game/q_shared.h"
#include "gs_public.h"


#define MOVEDIREPSILON	0.3f
#define WALKEPSILON	5.0f
#define RUNEPSILON	220.0f

// movement flags for animation control
#define ANIMMOVE_FRONT		0x00000001  //	Player is pressing fordward
#define ANIMMOVE_BACK		0x00000002  //	Player is pressing backpedal
#define ANIMMOVE_LEFT		0x00000004  //	Player is pressing sideleft
#define ANIMMOVE_RIGHT		0x00000008  //	Player is pressing sideright
#define ANIMMOVE_WALK		0x00000010  //	Player is pressing the walk key
#define ANIMMOVE_RUN		0x00000020  //	Player is running
#define ANIMMOVE_DUCK		0x00000040  //	Player is crouching
#define ANIMMOVE_SWIM		0x00000080  //	Player is swimming
#define ANIMMOVE_AIR		0x00000100  //	Player is at air, but not jumping

typedef struct
{
	int moveflags;              //moving direction
	int anim[PMODEL_PARTS];
} pm_anim_t;


//=================
//GS_SetBaseAnimUpper
//=================
static void GS_SetBaseAnimUpper( pm_anim_t *pmanim )
{
	//SWIMMING
	if( pmanim->moveflags & ANIMMOVE_SWIM )
	{
		pmanim->anim[UPPER] = TORSO_SWIM;
	}
	// FALLING
	else if( pmanim->moveflags & ANIMMOVE_AIR )
	{
		pmanim->anim[UPPER] = TORSO_STAND;
	}
	//CROUCH
	else if( pmanim->moveflags & ANIMMOVE_DUCK )
	{
		if( pmanim->moveflags & ( ANIMMOVE_WALK|ANIMMOVE_RUN ) )
		{
			pmanim->anim[UPPER] = TORSO_RUN;
		}
		else
		{
			pmanim->anim[UPPER] = TORSO_STAND;
		}
	}
	// RUN
	else if( pmanim->moveflags & ANIMMOVE_RUN )
	{
		pmanim->anim[UPPER] = TORSO_RUN;
	}
	//WALK
	else if( pmanim->moveflags & ANIMMOVE_WALK )
	{
		pmanim->anim[UPPER] = TORSO_STAND;
	}
	// STAND
	else
	{
		pmanim->anim[UPPER] = TORSO_STAND;
	}
}

//=================
//GS_SetBaseAnimLower
//=================
static void GS_SetBaseAnimLower( pm_anim_t *pmanim )
{
	//SWIMMING
	if( pmanim->moveflags & ANIMMOVE_SWIM )
	{
		if( pmanim->moveflags & ANIMMOVE_FRONT )
		{
			pmanim->anim[LOWER] = LEGS_SWIMFWD;
		}
		else
			pmanim->anim[LOWER] = LEGS_SWIM;
	}
	//FALLING
	else if( pmanim->moveflags & ANIMMOVE_AIR )
	{
		pmanim->anim[LOWER] = LEGS_JUMP1ST;
	}
	//CROUCH
	else if( pmanim->moveflags & ANIMMOVE_DUCK )
	{
		if( pmanim->moveflags & ( ANIMMOVE_WALK|ANIMMOVE_RUN ) )
		{
			pmanim->anim[LOWER] = LEGS_CRWALK;
		}
		else
		{
			pmanim->anim[LOWER] = LEGS_IDLECR;
		}
	}
	// RUN
	else if( pmanim->moveflags & ANIMMOVE_RUN )
	{
		//front/backward has priority over side movements
		if( pmanim->moveflags & ANIMMOVE_FRONT )
		{
			pmanim->anim[LOWER] = LEGS_RUNFWD;

		}
		else if( pmanim->moveflags & ANIMMOVE_BACK )
		{
			pmanim->anim[LOWER] = LEGS_RUNBACK;

		}
		else if( pmanim->moveflags & ANIMMOVE_RIGHT )
		{
			pmanim->anim[LOWER] = LEGS_RUNRIGHT;

		}
		else if( pmanim->moveflags & ANIMMOVE_LEFT )
		{
			pmanim->anim[LOWER] = LEGS_RUNLEFT;

		}
		else  //is moving by inertia
			pmanim->anim[LOWER] = LEGS_WALKFWD;
	}
	//WALK
	else if( pmanim->moveflags & ANIMMOVE_WALK )
	{
		//front/backward has priority over side movements
		if( pmanim->moveflags & ANIMMOVE_FRONT )
		{
			pmanim->anim[LOWER] = LEGS_WALKFWD;

		}
		else if( pmanim->moveflags & ANIMMOVE_BACK )
		{
			pmanim->anim[LOWER] = LEGS_WALKBACK;

		}
		else if( pmanim->moveflags & ANIMMOVE_RIGHT )
		{
			pmanim->anim[LOWER] = LEGS_WALKRIGHT;

		}
		else if( pmanim->moveflags & ANIMMOVE_LEFT )
		{
			pmanim->anim[LOWER] = LEGS_WALKLEFT;

		}
		else  //is moving by inertia
			pmanim->anim[LOWER] = LEGS_WALKFWD;
	}
	else
	{   // STAND
		pmanim->anim[LOWER] = LEGS_IDLE;
	}
}

//=================
//GS_SetBaseAnims
//=================
static void GS_SetBaseAnims( pm_anim_t *pmanim )
{
	int part;

	for( part = 0; part < PMODEL_PARTS; part++ )
	{
		switch( part )
		{
		case LOWER:
			GS_SetBaseAnimLower( pmanim );
			break;

		case UPPER:
			GS_SetBaseAnimUpper( pmanim );
			break;

		case HEAD:
		default:
			pmanim->anim[part] = 0;
			break;
		}
	}
}

//=================
//GS_UpdateBaseAnims
//=================
int GS_UpdateBaseAnims( entity_state_t *state, vec3_t velocity )
{
	pm_anim_t pmanim;
	vec3_t movedir;
	vec3_t hvel;
	vec3_t viewaxis[3];
	float xyspeedcheck;
	int waterlevel;
	vec3_t mins, maxs;
	vec3_t point;
	trace_t	trace;

	if( !state )
		GS_Error( "GS_UpdateBaseAnims: NULL state\n" );

	GS_BBoxForEntityState( state, mins, maxs );

	memset( &pmanim, 0, sizeof( pm_anim_t ) );

	// determine if player is at ground, for walking or falling
	// this is not like having groundEntity, we are more generous with
	// the tracing size here to include small steps
	point[0] = state->origin[0];
	point[1] = state->origin[1];
	point[2] = state->origin[2] - ( 1.6*STEPSIZE );
	GS_Trace( &trace, state->origin, mins, maxs, point, state->number, MASK_PLAYERSOLID );
	if( trace.ent == -1 || ( trace.fraction < 1.0f && trace.plane.normal[2] < 0.7 ) )
	{
		pmanim.moveflags |= ANIMMOVE_AIR;
	}

	// crouching : fixme? : it assumes the entity is using the player box sizes
	if( VectorCompare( maxs, playerbox_crouch_maxs ) )
	{
		pmanim.moveflags |= ANIMMOVE_DUCK;
	}

	// find out the water level
	waterlevel = GS_WaterLevel( state, mins, maxs );
	if( waterlevel >= 2 || ( waterlevel && ( pmanim.moveflags & ANIMMOVE_AIR ) ) )
	{
		pmanim.moveflags |= ANIMMOVE_SWIM;
	}

	//find out what are the base movements the model is doing

	hvel[0] = velocity[0];
	hvel[1] = velocity[1];
	hvel[2] = 0;
	xyspeedcheck = VectorNormalize2( hvel, movedir );
	if( xyspeedcheck > WALKEPSILON )
	{
		VectorNormalizeFast( movedir );
		AngleVectors( tv( 0, state->angles[YAW], 0 ), viewaxis[FORWARD], viewaxis[RIGHT], viewaxis[UP] );

		// if it's moving to where is looking, it's moving forward
		if( DotProduct( movedir, viewaxis[RIGHT] ) > MOVEDIREPSILON )
		{
			pmanim.moveflags |= ANIMMOVE_RIGHT;
		}
		else if( -DotProduct( movedir, viewaxis[RIGHT] ) > MOVEDIREPSILON )
		{
			pmanim.moveflags |= ANIMMOVE_LEFT;
		}
		if( DotProduct( movedir, viewaxis[FORWARD] ) > MOVEDIREPSILON )
		{
			pmanim.moveflags |= ANIMMOVE_FRONT;
		}
		else if( -DotProduct( movedir, viewaxis[FORWARD] ) > MOVEDIREPSILON )
		{
			pmanim.moveflags |= ANIMMOVE_BACK;
		}

		if( xyspeedcheck > RUNEPSILON )
			pmanim.moveflags |= ANIMMOVE_RUN;
		else if( xyspeedcheck > WALKEPSILON )
			pmanim.moveflags |= ANIMMOVE_WALK;
	}

	GS_SetBaseAnims( &pmanim );
	return ( ( pmanim.anim[LOWER] &0x3F ) | ( pmanim.anim[UPPER] &0x3F )<<6 | ( pmanim.anim[HEAD] &0xF )<<12 );
}

#undef MOVEDIREPSILON
#undef WALKEPSILON
#undef RUNEPSILON
