/*
===========================================================================
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors

This file is part of the OpenJK source code.

OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/
#include "g_headers.h"

#include "b_local.h"

//static void R5D2_LookAround( void );
float NPC_GetPainChance( gentity_t *self, int damage );
extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );

#define TURN_OFF   0x00000100

//Local state enums
enum
{
	LSTATE_NONE = 0,
	LSTATE_BACKINGUP,
	LSTATE_SPINNING,
	LSTATE_PAIN,
	LSTATE_DROP
};

/*
-------------------------
R2D2_PartsMove
-------------------------
*/
void R2D2_PartsMove(void)
{
	// Front 'eye' lense
	if ( TIMER_Done(NPC,"eyeDelay") )
	{
		NPC->pos1[1] = AngleNormalize360( NPC->pos1[1]);

		NPC->pos1[0]+=Q_irand( -20, 20 );	// Roll
		NPC->pos1[1]=Q_irand( -20, 20 );
		NPC->pos1[2]=Q_irand( -20, 20 );

		if (NPC->genericBone1)
		{
			gi.G2API_SetBoneAnglesIndex( &NPC->ghoul2[NPC->playerModel], NPC->genericBone1, NPC->pos1, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
		}
		TIMER_Set( NPC, "eyeDelay", Q_irand( 100, 1000 ) );
	}
}

/*
-------------------------
NPC_BSDroid_Idle
-------------------------
*/
void Droid_Idle( void )
{
//	VectorCopy( NPCInfo->investigateGoal, lookPos );

//	NPC_FacePosition( lookPos );
}

/*
-------------------------
R2D2_TurnAnims
-------------------------
*/
void R2D2_TurnAnims ( void )
{
	float turndelta;
	int		anim;

	turndelta = AngleDelta(NPC->currentAngles[YAW], NPCInfo->desiredYaw);

	if ((fabs(turndelta) > 20) && ((NPC->client->NPC_class == CLASS_R2D2) || (NPC->client->NPC_class == CLASS_R5D2)))
	{
		anim = NPC->client->ps.legsAnim;
		if (turndelta<0)
		{
			if (anim != BOTH_TURN_LEFT1)
			{
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TURN_LEFT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
			}
		}
		else
		{
			if (anim != BOTH_TURN_RIGHT1)
			{
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TURN_RIGHT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
			}
		}
	}
	else
	{
			NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
	}

}

/*
-------------------------
Droid_Patrol
-------------------------
*/
void Droid_Patrol( void )
{

	NPC->pos1[1] = AngleNormalize360( NPC->pos1[1]);

	if ( NPC->client && NPC->client->NPC_class != CLASS_GONK )
	{
		R2D2_PartsMove();		// Get his eye moving.
		R2D2_TurnAnims();
	}

	//If we have somewhere to go, then do that
	if ( UpdateGoal() )
	{
		ucmd.buttons |= BUTTON_WALKING;
		NPC_MoveToGoal( qtrue );

		if( NPC->client && NPC->client->NPC_class == CLASS_MOUSE )
		{
			NPCInfo->desiredYaw += sin(level.time*.5) * 25; // Weaves side to side a little

			if (TIMER_Done(NPC,"patrolNoise"))
			{
				G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/mouse/misc/mousego%d.wav", Q_irand(1, 3)) );

				TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
			}
		}
		else if( NPC->client && NPC->client->NPC_class == CLASS_R2D2 )
		{
			if (TIMER_Done(NPC,"patrolNoise"))
			{
				G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/r2d2/misc/r2d2talk0%d.wav",	Q_irand(1, 3)) );

				TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
			}
		}
		else if( NPC->client && NPC->client->NPC_class == CLASS_R5D2 )
		{
			if (TIMER_Done(NPC,"patrolNoise"))
			{
				G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/r5d2/misc/r5talk%d.wav", Q_irand(1, 4)) );

				TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
			}
		}
		if( NPC->client && NPC->client->NPC_class == CLASS_GONK )
		{
			if (TIMER_Done(NPC,"patrolNoise"))
			{
				G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/gonk/misc/gonktalk%d.wav", Q_irand(1, 2)) );

				TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
			}
		}
//		else
//		{
//			R5D2_LookAround();
//		}
	}

	NPC_UpdateAngles( qtrue, qtrue );

}

/*
-------------------------
Droid_Run
-------------------------
*/
void Droid_Run( void )
{
	R2D2_PartsMove();

	if ( NPCInfo->localState == LSTATE_BACKINGUP )
	{
		ucmd.forwardmove = -127;
		NPCInfo->desiredYaw += 5;

		NPCInfo->localState = LSTATE_NONE;	// So he doesn't constantly backup.
	}
	else
	{
		ucmd.forwardmove = 64;
		//If we have somewhere to go, then do that
		if ( UpdateGoal() )
		{
			if (NPC_MoveToGoal( qfalse ))
			{
				NPCInfo->desiredYaw += sin(level.time*.5) * 5; // Weaves side to side a little
			}
		}
	}

	NPC_UpdateAngles( qtrue, qtrue );
}

/*
-------------------------
void Droid_Spin( void )
-------------------------
*/
void Droid_Spin( void )
{
	vec3_t dir = {0,0,1};

	R2D2_TurnAnims();


	// Head is gone, spin and spark
	if ( NPC->client->NPC_class == CLASS_R5D2 )
	{
		// No head?
		if (gi.G2API_GetSurfaceRenderStatus( &NPC->ghoul2[NPC->playerModel], "head" ))
		{
			if (TIMER_Done(NPC,"smoke") && !TIMER_Done(NPC,"droidsmoketotal"))
			{
				TIMER_Set( NPC, "smoke", 100);
				G_PlayEffect( "droid_smoke" , NPC->currentOrigin,dir);
			}

			if (TIMER_Done(NPC,"droidspark"))
			{
				TIMER_Set( NPC, "droidspark", Q_irand(100,500));
				G_PlayEffect( "spark", NPC->currentOrigin,dir);
			}

			ucmd.forwardmove = Q_irand( -64, 64);

			if (TIMER_Done(NPC,"roam"))
			{
				TIMER_Set( NPC, "roam", Q_irand( 250, 1000 ) );
				NPCInfo->desiredYaw = Q_irand( 0, 360 ); // Go in random directions
			}
		}
		else
		{
			if (TIMER_Done(NPC,"roam"))
			{
				NPCInfo->localState = LSTATE_NONE;
			}
			else
			{
				NPCInfo->desiredYaw = AngleNormalize360(NPCInfo->desiredYaw + 40); // Spin around
			}
		}
	}
	else
	{
		if (TIMER_Done(NPC,"roam"))
		{
			NPCInfo->localState = LSTATE_NONE;
		}
		else
		{
			NPCInfo->desiredYaw = AngleNormalize360(NPCInfo->desiredYaw + 40); // Spin around
		}
	}

	NPC_UpdateAngles( qtrue, qtrue );
}

/*
-------------------------
NPC_BSDroid_Pain
-------------------------
*/
void NPC_Droid_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, vec3_t point, int damage, int mod,int hitLoc )
{
	int		anim;
	float	pain_chance;

	VectorCopy( self->NPC->lastPathAngles, self->s.angles );

	if ( self->client->NPC_class == CLASS_R5D2 )
	{
		pain_chance = NPC_GetPainChance( self, damage );

		// Put it in pain
		if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || Q_flrand(0.0f, 1.0f) < pain_chance )	// Spin around in pain? Demp2 always does this
		{
			// Health is between 0-30 or was hit by a DEMP2 so pop his head
			if ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )
			{
				if (!(self->spawnflags & 2))	// Doesn't have to ALWAYSDIE
				{
					if ((self->NPC->localState != LSTATE_SPINNING) &&
						(!gi.G2API_GetSurfaceRenderStatus( &self->ghoul2[self->playerModel], "head" )))
					{
						gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "head", TURN_OFF );

//						G_PlayEffect( "small_chunks" , self->currentOrigin );
						G_PlayEffect( "r5d2head", self->currentOrigin );

						self->s.powerups |= ( 1 << PW_SHOCKED );
						self->client->ps.powerups[PW_SHOCKED] = level.time + 3000;

						TIMER_Set( self, "droidsmoketotal", 5000);
						TIMER_Set( self, "droidspark", 100);
						self->NPC->localState = LSTATE_SPINNING;
					}
				}
			}
			// Just give him normal pain for a little while
			else
			{
				anim = self->client->ps.legsAnim;

				if ( anim == BOTH_STAND2 )	// On two legs?
				{
					anim = BOTH_PAIN1;
				}
				else						// On three legs
				{
					anim = BOTH_PAIN2;
				}

				NPC_SetAnim( self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );

				// Spin around in pain
				self->NPC->localState = LSTATE_SPINNING;
				TIMER_Set( self, "roam", Q_irand(1000,2000));
			}
		}
	}
	else if (self->client->NPC_class == CLASS_MOUSE)
	{
		if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )
		{
			self->NPC->localState = LSTATE_SPINNING;
			self->s.powerups |= ( 1 << PW_SHOCKED );
			self->client->ps.powerups[PW_SHOCKED] = level.time + 3000;
		}
		else
		{
			self->NPC->localState = LSTATE_BACKINGUP;
		}

		self->NPC->scriptFlags &= ~SCF_LOOK_FOR_ENEMIES;
	}
	else if (self->client->NPC_class == CLASS_R2D2)
	{

		pain_chance = NPC_GetPainChance( self, damage );

		if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || Q_flrand(0.0f, 1.0f) < pain_chance )	// Spin around in pain? Demp2 always does this
		{
			anim = self->client->ps.legsAnim;

			if ( anim == BOTH_STAND2 )	// On two legs?
			{
				anim = BOTH_PAIN1;
			}
			else						// On three legs
			{
				anim = BOTH_PAIN2;
			}

			NPC_SetAnim( self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );

			// Spin around in pain
			self->NPC->localState = LSTATE_SPINNING;
			TIMER_Set( self, "roam", Q_irand(1000,2000));
		}
	}
	else if ( self->client->NPC_class == CLASS_INTERROGATOR && ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) && other )
	{
		vec3_t dir;

		VectorSubtract( self->currentOrigin, other->currentOrigin, dir );
		VectorNormalize( dir );

		VectorMA( self->client->ps.velocity, 550, dir, self->client->ps.velocity );
		self->client->ps.velocity[2] -= 127;
	}

	NPC_Pain( self, inflictor, other, point, damage, mod);
}


/*
-------------------------
Droid_Pain
-------------------------
*/
void Droid_Pain(void)
{
	if (TIMER_Done(NPC,"droidpain"))	//He's done jumping around
	{
		NPCInfo->localState = LSTATE_NONE;
	}
}

/*
-------------------------
NPC_Mouse_Precache
-------------------------
*/
void NPC_Mouse_Precache( void )
{
	int	i;

	for (i = 1; i < 4; i++)
	{
		G_SoundIndex( va( "sound/chars/mouse/misc/mousego%d.wav", i ) );
	}

	G_EffectIndex( "env/small_explode" );
	G_SoundIndex( "sound/chars/mouse/misc/death1" );
	G_SoundIndex( "sound/chars/mouse/misc/mouse_lp" );
}

/*
-------------------------
NPC_R5D2_Precache
-------------------------
*/
void NPC_R5D2_Precache(void)
{
	for ( int i = 1; i < 5; i++)
	{
		G_SoundIndex( va( "sound/chars/r5d2/misc/r5talk%d.wav", i ) );
	}
	G_SoundIndex( "sound/chars/mark2/misc/mark2_explo" ); // ??
	G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp2.wav" );
	G_EffectIndex( "env/med_explode");
	G_EffectIndex( "droid_smoke" );
	G_EffectIndex( "r5d2head");
}

/*
-------------------------
NPC_R2D2_Precache
-------------------------
*/
void NPC_R2D2_Precache(void)
{
	for ( int i = 1; i < 4; i++)
	{
		G_SoundIndex( va( "sound/chars/r2d2/misc/r2d2talk0%d.wav", i ) );
	}
	G_SoundIndex( "sound/chars/mark2/misc/mark2_explo" ); // ??
	G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp.wav" );
	G_EffectIndex( "env/med_explode");
}

/*
-------------------------
NPC_Gonk_Precache
-------------------------
*/
void NPC_Gonk_Precache( void )
{
	G_SoundIndex("sound/chars/gonk/misc/gonktalk1.wav");
	G_SoundIndex("sound/chars/gonk/misc/gonktalk2.wav");

	G_SoundIndex("sound/chars/gonk/misc/death1.wav");
	G_SoundIndex("sound/chars/gonk/misc/death2.wav");
	G_SoundIndex("sound/chars/gonk/misc/death3.wav");

	G_EffectIndex( "env/med_explode");
}

/*
-------------------------
NPC_Protocol_Precache
-------------------------
*/
void NPC_Protocol_Precache( void )
{
	G_SoundIndex( "sound/chars/mark2/misc/mark2_explo" );
	G_EffectIndex( "env/med_explode");
}

/*
static void R5D2_OffsetLook( float offset, vec3_t out )
{
	vec3_t	angles, forward, temp;

	GetAnglesForDirection( NPC->currentOrigin, NPCInfo->investigateGoal, angles );
	angles[YAW] += offset;
	AngleVectors( angles, forward, NULL, NULL );
	VectorMA( NPC->currentOrigin, 64, forward, out );

	CalcEntitySpot( NPC, SPOT_HEAD, temp );
	out[2] = temp[2];
}
*/

/*
-------------------------
R5D2_LookAround
-------------------------
*/
/*
static void R5D2_LookAround( void )
{
	vec3_t	lookPos;
	float	perc = (float) ( level.time - NPCInfo->pauseTime ) / (float) NPCInfo->investigateDebounceTime;

	//Keep looking at the spot
	if ( perc < 0.25 )
	{
		VectorCopy( NPCInfo->investigateGoal, lookPos );
	}
	else if ( perc < 0.5f )		//Look up but straight ahead
	{
		R5D2_OffsetLook( 0.0f, lookPos );
	}
	else if ( perc < 0.75f )	//Look right
	{
		R5D2_OffsetLook( 45.0f, lookPos );
	}
	else	//Look left
	{
		R5D2_OffsetLook( -45.0f, lookPos );
	}

	NPC_FacePosition( lookPos );
}

*/

/*
-------------------------
NPC_BSDroid_Default
-------------------------
*/
void NPC_BSDroid_Default( void )
{

	if ( NPCInfo->localState == LSTATE_SPINNING )
	{
		Droid_Spin();
	}
	else if ( NPCInfo->localState == LSTATE_PAIN )
	{
		Droid_Pain();
	}
	else if ( NPCInfo->localState == LSTATE_DROP )
	{
		NPC_UpdateAngles( qtrue, qtrue );
		ucmd.upmove = Q_flrand(-1.0f, 1.0f) * 64;
	}
	else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
	{
		Droid_Patrol();
	}
	else
	{
		Droid_Run();
	}
}
