/***
*
*	Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*	
*	This product contains software technology licensed from Id 
*	Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc. 
*	All Rights Reserved.
*
*   This source code contains proprietary and confidential information of
*   Valve LLC and its suppliers.  Access to this code is restricted to
*   persons who have executed a written SDK license with Valve.  Any access,
*   use or distribution of this code by or to any unlicensed person is illegal.
*
****/

#include	"extdll.h"
#include	"util.h"
#include	"cbase.h"
#include	"monsters.h"
#include	"schedule.h"
#include	"flyingmonster.h"

#define FLYING_AE_FLAP		(8)
#define FLYING_AE_FLAPSOUND	(9)

extern DLL_GLOBAL edict_t *g_pBodyQueueHead;

int CFlyingMonster::CheckLocalMove( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist )
{
	// UNDONE: need to check more than the endpoint
	if( FBitSet( pev->flags, FL_SWIM ) && ( UTIL_PointContents( vecEnd ) != CONTENTS_WATER ) )
	{
		// ALERT( at_aiconsole, "can't swim out of water\n" );
		return FALSE;
	}

	TraceResult tr;

	UTIL_TraceHull( vecStart + Vector( 0.0f, 0.0f, 32.0f ), vecEnd + Vector( 0.0f, 0.0f, 32.0f ), dont_ignore_monsters, large_hull, edict(), &tr );

	// ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z );
	// ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z );

	if( pflDist )
	{
		*pflDist = ( ( tr.vecEndPos - Vector( 0.0f, 0.0f, 32.0f ) ) - vecStart ).Length();// get the distance.
	}

	// ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction );
	if( tr.fStartSolid || tr.flFraction < 1.0f )
	{
		if( pTarget && pTarget->edict() == gpGlobals->trace_ent )
			return LOCALMOVE_VALID;
		return LOCALMOVE_INVALID;
	}

	return LOCALMOVE_VALID;
}

BOOL CFlyingMonster::FTriangulate( const Vector &vecStart, const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex )
{
	return CBaseMonster::FTriangulate( vecStart, vecEnd, flDist, pTargetEnt, pApex );
}

Activity CFlyingMonster::GetStoppedActivity( void )
{ 
	if( pev->movetype != MOVETYPE_FLY )		// UNDONE: Ground idle here, IDLE may be something else
		return ACT_IDLE;

	return ACT_HOVER; 
}

void CFlyingMonster::Stop( void ) 
{
	Activity stopped = GetStoppedActivity();
	if( m_IdealActivity != stopped )
	{
		m_flightSpeed = 0;
		m_IdealActivity = stopped;
	}
	pev->angles.z = 0.0f;
	pev->angles.x = 0.0f;
	m_vecTravel = g_vecZero;
}

float CFlyingMonster::ChangeYaw( int speed )
{
	if( pev->movetype == MOVETYPE_FLY )
	{
		float diff = FlYawDiff();
		float target = 0.0f;

		if( m_IdealActivity != GetStoppedActivity() )
		{
			if( diff < -20.0f )
				target = 90.0f;
			else if( diff > 20.0f )
				target = -90.0f;
		}
		pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0f * gpGlobals->frametime );
	}
	return CBaseMonster::ChangeYaw( speed );
}

void CFlyingMonster::Killed( entvars_t *pevAttacker, int iGib )
{
	pev->movetype = MOVETYPE_STEP;
	ClearBits( pev->flags, FL_ONGROUND );
	pev->angles.z = 0;
	pev->angles.x = 0;
	CBaseMonster::Killed( pevAttacker, iGib );
}

void CFlyingMonster::HandleAnimEvent( MonsterEvent_t *pEvent )
{
	switch( pEvent->event )
	{
	case FLYING_AE_FLAP:
		m_flightSpeed = 400;
		break;
	case FLYING_AE_FLAPSOUND:
		if( m_pFlapSound )
			EMIT_SOUND( edict(), CHAN_BODY, m_pFlapSound, 1, ATTN_NORM );	
		break;
	default:
		CBaseMonster::HandleAnimEvent( pEvent );
		break;
	}
}

void CFlyingMonster::Move( float flInterval )
{
	if( pev->movetype == MOVETYPE_FLY )
		m_flGroundSpeed = m_flightSpeed;
	CBaseMonster::Move( flInterval );
}

BOOL CFlyingMonster::ShouldAdvanceRoute( float flWaypointDist )
{
	// Get true 3D distance to the goal so we actually reach the correct height
	if( m_Route[m_iRouteIndex].iType & bits_MF_IS_GOAL )
		flWaypointDist = ( m_Route[m_iRouteIndex].vecLocation - pev->origin ).Length();

	if( flWaypointDist <= 64.0f + ( m_flGroundSpeed * gpGlobals->frametime ) )
		return TRUE;

	return FALSE;
}

void CFlyingMonster::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval )
{
	if( pev->movetype == MOVETYPE_FLY )
	{
		if( gpGlobals->time - m_stopTime > 1.0f )
		{
			if( m_IdealActivity != m_movementActivity )
			{
				m_IdealActivity = m_movementActivity;
				m_flGroundSpeed = m_flightSpeed = 200;
			}
		}
		Vector vecMove = pev->origin + ( ( vecDir + ( m_vecTravel * m_momentum ) ).Normalize() * (m_flGroundSpeed * flInterval ) );

		if( m_IdealActivity != m_movementActivity )
		{
			m_flightSpeed = UTIL_Approach( 100, m_flightSpeed, 75.0f * gpGlobals->frametime );
			if( m_flightSpeed < 100 )
				m_stopTime = gpGlobals->time;
		}
		else
			m_flightSpeed = UTIL_Approach( 20, m_flightSpeed, 300.0f * gpGlobals->frametime );

		if( CheckLocalMove( pev->origin, vecMove, pTargetEnt, NULL ) )
		{
			m_vecTravel = vecMove - pev->origin;
			m_vecTravel = m_vecTravel.Normalize();
			UTIL_MoveToOrigin( ENT( pev ), vecMove, ( m_flGroundSpeed * flInterval ), MOVE_STRAFE );
		}
		else
		{
			m_IdealActivity = GetStoppedActivity();
			m_stopTime = gpGlobals->time;
			m_vecTravel = g_vecZero;
		}
	}
	else
		CBaseMonster::MoveExecute( pTargetEnt, vecDir, flInterval );
}

float CFlyingMonster::CeilingZ( const Vector &position )
{
	TraceResult tr;

	Vector minUp = position;
	Vector maxUp = position;
	maxUp.z += 4096.0f;

	UTIL_TraceLine( position, maxUp, ignore_monsters, NULL, &tr );
	if( tr.flFraction != 1.0f )
		maxUp.z = tr.vecEndPos.z;

	if( ( pev->flags ) & FL_SWIM )
	{
		return UTIL_WaterLevel( position, minUp.z, maxUp.z );
	}
	return maxUp.z;
}

BOOL CFlyingMonster::ProbeZ( const Vector &position, const Vector &probe, float *pFraction )
{
	int conPosition = UTIL_PointContents( position );
	if( ( ( ( pev->flags ) & FL_SWIM ) == FL_SWIM ) ^ ( conPosition == CONTENTS_WATER ) )
	{
		//    SWIMING & !WATER
		// or FLYING  & WATER
		//
		*pFraction = 0.0f;
		return TRUE; // We hit a water boundary because we are where we don't belong.
	}
	int conProbe = UTIL_PointContents( probe );
	if( conProbe == conPosition )
	{
		// The probe is either entirely inside the water (for fish) or entirely
		// outside the water (for birds).
		//
		*pFraction = 1.0f;
		return FALSE;
	}

	Vector ProbeUnit = ( probe - position ).Normalize();
	float ProbeLength = ( probe - position ).Length();
	float maxProbeLength = ProbeLength;
	float minProbeLength = 0;

	float diff = maxProbeLength - minProbeLength;
	while( diff > 1.0f )
	{
		float midProbeLength = minProbeLength + diff / 2.0f;
		Vector midProbeVec = midProbeLength * ProbeUnit;
		if( UTIL_PointContents( position + midProbeVec ) == conPosition )
		{
			minProbeLength = midProbeLength;
		}
		else
		{
			maxProbeLength = midProbeLength;
		}
		diff = maxProbeLength - minProbeLength;
	}
	*pFraction = minProbeLength/ProbeLength;

	return TRUE;
}

float CFlyingMonster::FloorZ( const Vector &position )
{
	TraceResult tr;

	Vector down = position;
	down.z -= 2048.0f;

	UTIL_TraceLine( position, down, ignore_monsters, NULL, &tr );

	if( tr.flFraction != 1.0f )
		return tr.vecEndPos.z;

	return down.z;
}