//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//

#ifndef AI_BLENDED_MOVEMENT_H
#define AI_BLENDED_MOVEMENT_H

#include "ai_basenpc.h"
#include "ai_motor.h"
#include "ai_navigator.h"

struct AI_Waypoint_t;

//-----------------------------------------------------------------------------
// CLASS: CAI_BlendedMotor
//
// Purpose: Home of fancy human animation transition code
//
//-----------------------------------------------------------------------------

class CAI_BlendedMotor : public CAI_Motor
{
	typedef CAI_Motor BaseClass;
public:
	CAI_BlendedMotor( CAI_BaseNPC *pOuter )
	 :	BaseClass( pOuter )
	{
		m_iPrimaryLayer = -1;
		m_nPrimarySequence = ACT_INVALID;

		m_iSecondaryLayer = -1;
		m_nSecondarySequence =  ACT_INVALID;
		m_flSecondaryWeight = 0.0f;

		m_nSavedGoalActivity = ACT_INVALID;
		m_nSavedTranslatedGoalActivity = ACT_INVALID;
		m_nGoalSequence = ACT_INVALID;

		m_nPrevMovementSequence = ACT_INVALID;
		m_nInteriorSequence = ACT_INVALID;

		m_bDeceleratingToGoal = false;

		m_flStartCycle = 0.0f;

		m_flPredictiveSpeedAdjust = 1.0f;
		m_flReactiveSpeedAdjust = 1.0f;
		m_vecPrevOrigin1.Init();
		m_vecPrevOrigin2.Init();

		m_prevYaw = 0.0f;
		m_doTurn = 0.0f;
		m_doLeft = 0.0f;
		m_doRight = 0.0f;
		m_flNextTurnAct = 0.0f;
	}

	void 	MoveClimbStart( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw );
	void 	MoveJumpStart( const Vector &velocity );

	void	ResetMoveCalculations();
	void	MoveStart();
	void	ResetGoalSequence();
	void	MoveStop();
	void	MovePaused();
	void	MoveContinue();

	float	OverrideMaxYawSpeed( Activity activity );
	void	UpdateYaw( int speed );
	void	RecalculateYawSpeed(); 

	bool	IsDeceleratingToGoal() const	{ return m_bDeceleratingToGoal; }
	float	GetMoveScriptTotalTime();

	void	MaintainTurnActivity( void );
	bool	AddTurnGesture( float flYD );


private:
	AIMotorMoveResult_t MoveGroundExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult );
	AIMotorMoveResult_t MoveFlyExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult );


	// --------------------------------

	void	BuildMoveScript(  const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult );

	void	BuildVelocityScript( const AILocalMoveGoal_t &move );
	void	InsertSlowdown( float distToObstruction, float idealAccel, bool bAlwaysSlowdown );

	int		BuildTurnScript( int i, int j );
	void	BuildTurnScript( const AILocalMoveGoal_t &move );
	int 	BuildInsertNode( int i, float flTime );

	Activity GetTransitionActivity( void );
	
	// --------------------------------

	// helpers to simplify code
	float	GetCycle()														{ return GetOuter()->GetCycle();								}
	int		AddLayeredSequence( int sequence, int iPriority )				{ return GetOuter()->AddLayeredSequence( sequence, iPriority ); }
	void	SetLayerWeight( int iLayer, float flWeight )					{ GetOuter()->SetLayerWeight( iLayer, flWeight );				}
	void	SetLayerPlaybackRate( int iLayer, float flPlaybackRate )		{ GetOuter()->SetLayerPlaybackRate( iLayer, flPlaybackRate );	}
	void	SetLayerNoRestore( int iLayer, bool bNoRestore )				{ GetOuter()->SetLayerNoRestore( iLayer, bNoRestore );			}
	void	SetLayerCycle( int iLayer, float flCycle )						{ GetOuter()->SetLayerCycle( iLayer, flCycle );					}
	void	SetLayerCycle( int iLayer, float flCycle, float flPrevCycle )	{ GetOuter()->SetLayerCycle( iLayer, flCycle, flPrevCycle );	}
	void	RemoveLayer( int iLayer, float flKillRate, float flKillDelay )	{ GetOuter()->RemoveLayer( iLayer, flKillRate, flKillDelay );	}

	// --------------------------------

	struct AI_Movementscript_t
	{
	public:
		AI_Movementscript_t( )
		{
			Init( );
		};

		void Init( void )
		{
			memset( this, 0, sizeof(*this) );
		};

		float	flTime;			// time till next entry
		float	flElapsedTime;	// time since first entry

		float	flDist;			// distance to next entry

		float	flMaxVelocity;

		// float	flVelocity;

		float	flYaw;
		float	flAngularVelocity;

		bool	bLooping;
		int		nFlags;

		AI_Waypoint_t *pWaypoint;

	public:
		AI_Movementscript_t *pNext;
		AI_Movementscript_t *pPrev;

		Vector	vecLocation;

	};
	
	//---------------------------------

	CUtlVector<AI_Movementscript_t>	m_scriptMove;
	CUtlVector<AI_Movementscript_t>	m_scriptTurn;

	//---------------------------------

	bool			m_bDeceleratingToGoal;

	int				m_iPrimaryLayer;
	int				m_iSecondaryLayer;

	int				m_nPrimarySequence;
	int				m_nSecondarySequence;
	float			m_flSecondaryWeight;

	Activity		m_nSavedGoalActivity;
	Activity		m_nSavedTranslatedGoalActivity;
	int				m_nGoalSequence;

	int				m_nPrevMovementSequence;
	int				m_nInteriorSequence;

	float			m_flStartCycle;

	float			m_flCurrRate;

	float			m_flPredictiveSpeedAdjust;		// predictive speed adjust from probing slope 
	float			m_flReactiveSpeedAdjust;		// reactive speed adjust when slope movement detected
	Vector			m_vecPrevOrigin1;
	Vector			m_vecPrevOrigin2;

	//---------------------------------

	float			m_flNextTurnGesture;	// next time for large turn gesture

	//---------------------------------
	float			m_prevYaw;
	float			m_doTurn;
	float			m_doLeft;
	float			m_doRight;
	float			m_flNextTurnAct;		// next time for small turn gesture

	
	float	GetMoveScriptDist( float &flNewSpeed );
	float	GetMoveScriptYaw( void );
	void	SetMoveScriptAnim( float flNewSpeed );

	int		GetInteriorSequence( int fromSequence );

	DECLARE_SIMPLE_DATADESC();
};

//-----------------------------------------------------------------------------
// CLASS: CAI_BlendingHost
//
// Purpose: Bridge to the home of fancy human animation transition code
//
//-----------------------------------------------------------------------------

template <class BASE_NPC>
class CAI_BlendingHost : public BASE_NPC
{
	DECLARE_CLASS_NOFRIEND( CAI_BlendingHost, BASE_NPC );
public:
	const CAI_BlendedMotor *GetBlendedMotor() const { return assert_cast<const CAI_BlendedMotor *>(this->GetMotor()); }
	CAI_BlendedMotor *		GetBlendedMotor()		{ return assert_cast<CAI_BlendedMotor *>(this->GetMotor()); }

	CAI_Motor *CreateMotor()
	{
		MEM_ALLOC_CREDIT();
		return new CAI_BlendedMotor( this );
	}

	CAI_Navigator *CreateNavigator()
	{
		CAI_Navigator *pNavigator = BaseClass::CreateNavigator();
		pNavigator->SetValidateActivitySpeed( false );
		return pNavigator;
	}

	float MaxYawSpeed( void )
	{
		float override = GetBlendedMotor()->OverrideMaxYawSpeed( this->GetActivity() );
		if ( override != -1 )
			return override;
		return BaseClass::MaxYawSpeed();
	}

	float GetTimeToNavGoal()
	{
		float result = GetBlendedMotor()->GetMoveScriptTotalTime();
		if ( result != -1 )
			return result;
		return BaseClass::GetTimeToNavGoal();
	}

};

//-------------------------------------
// to simplify basic usage:
class CAI_BlendedNPC : public CAI_BlendingHost<CAI_BaseNPC>
{
	DECLARE_CLASS( CAI_BlendedNPC, CAI_BlendingHost<CAI_BaseNPC> );
};

//-----------------------------------------------------------------------------	

#endif