//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======//
//
// Purpose: 
//
//=============================================================================//
#ifndef MULTIPLAYERANIMSTATE_H
#define MULTIPLAYERANIMSTATE_H
#ifdef _WIN32
#pragma once
#endif

#include "convar.h"
#include "basecombatweapon_shared.h"

#if defined( CLIENT_DLL )
class C_BasePlayer;
#define CPlayer C_BasePlayer
#else
class CBasePlayer;
#endif

enum PlayerAnimEvent_t
{
	PLAYERANIMEVENT_ATTACK_PRIMARY,
	PLAYERANIMEVENT_ATTACK_SECONDARY,
	PLAYERANIMEVENT_ATTACK_GRENADE,
	PLAYERANIMEVENT_RELOAD,
	PLAYERANIMEVENT_RELOAD_LOOP,
	PLAYERANIMEVENT_RELOAD_END,
	PLAYERANIMEVENT_JUMP,
	PLAYERANIMEVENT_SWIM,
	PLAYERANIMEVENT_DIE,
	PLAYERANIMEVENT_FLINCH_CHEST,
	PLAYERANIMEVENT_FLINCH_HEAD,
	PLAYERANIMEVENT_FLINCH_LEFTARM,
	PLAYERANIMEVENT_FLINCH_RIGHTARM,
	PLAYERANIMEVENT_FLINCH_LEFTLEG,
	PLAYERANIMEVENT_FLINCH_RIGHTLEG,
	PLAYERANIMEVENT_DOUBLEJUMP,

	// Cancel.
	PLAYERANIMEVENT_CANCEL,
	PLAYERANIMEVENT_SPAWN,

	// Snap to current yaw exactly
	PLAYERANIMEVENT_SNAP_YAW,

	PLAYERANIMEVENT_CUSTOM,				// Used to play specific activities
	PLAYERANIMEVENT_CUSTOM_GESTURE,
	PLAYERANIMEVENT_CUSTOM_SEQUENCE,	// Used to play specific sequences
	PLAYERANIMEVENT_CUSTOM_GESTURE_SEQUENCE,

	// TF Specific. Here until there's a derived game solution to this.
	PLAYERANIMEVENT_ATTACK_PRE,
	PLAYERANIMEVENT_ATTACK_POST,
	PLAYERANIMEVENT_GRENADE1_DRAW,
	PLAYERANIMEVENT_GRENADE2_DRAW,
	PLAYERANIMEVENT_GRENADE1_THROW,
	PLAYERANIMEVENT_GRENADE2_THROW,
	PLAYERANIMEVENT_VOICE_COMMAND_GESTURE,

	PLAYERANIMEVENT_COUNT
};

// Gesture Slots.
enum
{
	GESTURE_SLOT_ATTACK_AND_RELOAD,
	GESTURE_SLOT_GRENADE,
	GESTURE_SLOT_JUMP,
	GESTURE_SLOT_SWIM,
	GESTURE_SLOT_FLINCH,
	GESTURE_SLOT_VCD,
	GESTURE_SLOT_CUSTOM,

	GESTURE_SLOT_COUNT,
};

#define GESTURE_SLOT_INVALID	-1

struct GestureSlot_t
{
	int					m_iGestureSlot;
	Activity			m_iActivity;
	bool				m_bAutoKill;
	bool				m_bActive;
	CAnimationLayer		*m_pAnimLayer;
};

inline bool IsCustomPlayerAnimEvent( PlayerAnimEvent_t event )
{
	return ( event == PLAYERANIMEVENT_CUSTOM ) || ( event == PLAYERANIMEVENT_CUSTOM_GESTURE ) ||
		( event == PLAYERANIMEVENT_CUSTOM_SEQUENCE ) || ( event == PLAYERANIMEVENT_CUSTOM_GESTURE_SEQUENCE );
}

struct MultiPlayerPoseData_t
{
	int			m_iMoveX;
	int			m_iMoveY;
	int			m_iAimYaw;
	int			m_iAimPitch;
	int			m_iBodyHeight;

	float		m_flEstimateYaw;
	float		m_flLastAimTurnTime;

	void Init()
	{
		m_iMoveX = 0;
		m_iMoveY = 0;
		m_iAimYaw = 0;
		m_iAimPitch = 0;
		m_iBodyHeight = 0;
		m_flEstimateYaw = 0.0f;
		m_flLastAimTurnTime = 0.0f;
	}
};

struct DebugPlayerAnimData_t
{
	float		m_flSpeed;
	float		m_flAimPitch;
	float		m_flAimYaw;
	float		m_flBodyHeight;
	Vector2D	m_vecMoveYaw;

	void Init()
	{
		m_flSpeed = 0.0f;
		m_flAimPitch = 0.0f;
		m_flAimYaw = 0.0f;
		m_flBodyHeight = 0.0f;
		m_vecMoveYaw.Init();
	}
};

struct MultiPlayerMovementData_t
{
	// Set speeds to -1 if they are not used.
	float		m_flWalkSpeed;
	float		m_flRunSpeed;
	float		m_flSprintSpeed;	
	float		m_flBodyYawRate;
};


//=============================================================================
//
// Multi-Player Animation State
//
class CMultiPlayerAnimState
{
public:

	DECLARE_CLASS_NOBASE( CMultiPlayerAnimState );

	// Creation/Destruction
	CMultiPlayerAnimState() {}
	CMultiPlayerAnimState( CBasePlayer *pPlayer, MultiPlayerMovementData_t &movementData );
	~CMultiPlayerAnimState();

	// This is called by both the client and the server in the same way to trigger events for
	// players firing, jumping, throwing grenades, etc.
	virtual void ClearAnimationState();
	virtual void DoAnimationEvent( PlayerAnimEvent_t event, int nData = 0 );
	virtual Activity CalcMainActivity();	
	virtual void Update( float eyeYaw, float eyePitch );
	virtual void Release( void );

	const QAngle &GetRenderAngles();

	virtual Activity TranslateActivity( Activity actDesired );

	virtual void SetRunSpeed( float flSpeed ) { m_MovementData.m_flRunSpeed = flSpeed; }
	virtual void SetWalkSpeed( float flSpeed ) { m_MovementData.m_flWalkSpeed = flSpeed; }
	virtual void SetSprintSpeed( float flSpeed ) { m_MovementData.m_flSprintSpeed = flSpeed; }

	// Debug
	virtual void ShowDebugInfo( void );
	virtual void DebugShowAnimState( int iStartLine );

	Activity GetCurrentMainActivity( void ) { return m_eCurrentMainSequenceActivity; }

	void OnNewModel( void );

	// Gestures.
	void	ResetGestureSlots( void );
	void	ResetGestureSlot( int iGestureSlot );
	void AddVCDSequenceToGestureSlot( int iGestureSlot, int iGestureSequence, bool bAutoKill = true );

	// Feet.
	bool	m_bForceAimYaw;

protected:

	virtual void Init( CBasePlayer *pPlayer, MultiPlayerMovementData_t &movementData ); 
	CBasePlayer *GetBasePlayer( void )				{ return m_pPlayer; }

	// Allow inheriting classes to override SelectWeightedSequence
	virtual int SelectWeightedSequence( Activity activity ) { return GetBasePlayer()->SelectWeightedSequence( activity ); }
	virtual void RestartMainSequence();

	void GetOuterAbsVelocity( Vector& vel );
	float GetOuterXYSpeed();

	virtual bool HandleJumping( Activity &idealActivity );
	virtual bool HandleDucking( Activity &idealActivity );
	virtual bool HandleMoving( Activity &idealActivity );
	virtual bool HandleSwimming( Activity &idealActivity );
	virtual bool HandleDying( Activity &idealActivity );

	// Gesture Slots
	CUtlVector<GestureSlot_t>		m_aGestureSlots;
	bool	InitGestureSlots( void );
	void	ShutdownGestureSlots( void );
	bool	IsGestureSlotActive( int iGestureSlot );
	bool	IsGestureSlotPlaying( int iGestureSlot, Activity iGestureActivity );
	void	AddToGestureSlot( int iGestureSlot, Activity iGestureActivity, bool bAutoKill );
	void	RestartGesture( int iGestureSlot, Activity iGestureActivity, bool bAutoKill = true );
	void	ComputeGestureSequence( CStudioHdr *pStudioHdr );
	void	UpdateGestureLayer( CStudioHdr *pStudioHdr, GestureSlot_t *pGesture );
	void	DebugGestureInfo( void );

#ifdef CLIENT_DLL
	void	RunGestureSlotAnimEventsToCompletion( GestureSlot_t *pGesture );
#endif

	virtual void PlayFlinchGesture( Activity iActivity );

	virtual float CalcMovementPlaybackRate( bool *bIsMoving );

	// Pose paramters.
	bool				SetupPoseParameters( CStudioHdr *pStudioHdr );
	virtual void		ComputePoseParam_MoveYaw( CStudioHdr *pStudioHdr );
	virtual void		ComputePoseParam_AimPitch( CStudioHdr *pStudioHdr );
	virtual void		ComputePoseParam_AimYaw( CStudioHdr *pStudioHdr );
	void				ComputePoseParam_BodyHeight( CStudioHdr *pStudioHdr );
	virtual void		EstimateYaw( void );
	void				ConvergeYawAngles( float flGoalYaw, float flYawRate, float flDeltaTime, float &flCurrentYaw );

	virtual float GetCurrentMaxGroundSpeed();
	virtual void ComputeSequences( CStudioHdr *pStudioHdr );
	void ComputeMainSequence();
	void UpdateInterpolators();
	void ResetGroundSpeed( void );
	float GetInterpolatedGroundSpeed( void );

	void ComputeFireSequence();
	void ComputeDeployedSequence();

	bool ShouldUpdateAnimState();

	void				DebugShowAnimStateForPlayer( bool bIsServer );
	void				DebugShowEyeYaw( void );

// Client specific.
#ifdef CLIENT_DLL

	// Debug.
	void				DebugShowActivity( Activity activity );

#endif

protected:

	CBasePlayer	*m_pPlayer;

	QAngle				m_angRender;

	// Pose parameters.
	bool						m_bPoseParameterInit;
	MultiPlayerPoseData_t		m_PoseParameterData;
	DebugPlayerAnimData_t		m_DebugAnimData;

	bool						m_bCurrentFeetYawInitialized;
	float						m_flLastAnimationStateClearTime;
	
	float m_flEyeYaw;
	float m_flEyePitch;
	float m_flGoalFeetYaw;
	float m_flCurrentFeetYaw;
	float m_flLastAimTurnTime;

	MultiPlayerMovementData_t	m_MovementData;

	// Jumping.
	bool	m_bJumping;
	float	m_flJumpStartTime;	
	bool	m_bFirstJumpFrame;

	// Swimming.
	bool	m_bInSwim;
	bool	m_bFirstSwimFrame;

	// Dying
	bool	m_bDying;
	bool	m_bFirstDyingFrame;

	// Last activity we've used on the lower body. Used to determine if animations should restart.
	Activity m_eCurrentMainSequenceActivity;	

	// Specific full-body sequence to play
	int		m_nSpecificMainSequence;

	// Weapon data.
	CHandle<CBaseCombatWeapon>	m_hActiveWeapon;

	// Ground speed interpolators.
#ifdef CLIENT_DLL
	float m_flLastGroundSpeedUpdateTime;
	CInterpolatedVar<float> m_iv_flMaxGroundSpeed;
#endif
	float m_flMaxGroundSpeed;
};

// If this is set, then the game code needs to make sure to send player animation events
// to the local player if he's the one being watched.
extern ConVar cl_showanimstate;

#endif // DOD_PLAYERANIMSTATE_H