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

#ifndef BASEFLEX_H
#define BASEFLEX_H
#ifdef _WIN32
#pragma once
#endif


#include "BaseAnimatingOverlay.h"
#include "utlvector.h"
#include "utlrbtree.h"
#include "sceneentity_shared.h"

struct flexsettinghdr_t;
struct flexsetting_t;
class AI_Response;

//-----------------------------------------------------------------------------
// Purpose:  A .vfe referenced by a scene during .vcd playback
//-----------------------------------------------------------------------------
class CFlexSceneFile
{
public:
	enum
	{
		MAX_FLEX_FILENAME = 128,
	};

	char			filename[ MAX_FLEX_FILENAME ];
	void			*buffer;
};

//-----------------------------------------------------------------------------
// Purpose: Animated characters who have vertex flex capability (e.g., facial expressions)
//-----------------------------------------------------------------------------
class CBaseFlex : public CBaseAnimatingOverlay
{
	DECLARE_CLASS( CBaseFlex, CBaseAnimatingOverlay );
public:
	DECLARE_SERVERCLASS();
	DECLARE_DATADESC();
	DECLARE_PREDICTABLE();

	// Construction
						CBaseFlex( void );
						~CBaseFlex( void );

	virtual void		SetModel( const char *szModelName );

	void Blink( );

	virtual	void		SetViewtarget( const Vector &viewtarget );
	const Vector		&GetViewtarget( void ) const;

	void				SetFlexWeight( char *szName, float value );
	void				SetFlexWeight( LocalFlexController_t index, float value );
	float				GetFlexWeight( char *szName );
	float				GetFlexWeight( LocalFlexController_t index );

	// Look up flex controller index by global name
	LocalFlexController_t	FindFlexController( const char *szName );
	void				EnsureTranslations( const flexsettinghdr_t *pSettinghdr );

	// Keep track of what scenes are being played
	void				StartChoreoScene( CChoreoScene *scene );
	void				RemoveChoreoScene( CChoreoScene *scene, bool canceled = false );

	// Start the specifics of an scene event
	virtual bool		StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget );

	// Manipulation of events for the object
	// Should be called by think function to process all scene events
	// The default implementation resets m_flexWeight array and calls
	//  AddSceneEvents
	virtual void		ProcessSceneEvents( void );

	// Assumes m_flexWeight array has been set up, this adds the actual currently playing
	//  expressions to the flex weights and adds other scene events as needed
	virtual	bool		ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );

	// Remove all playing events
	void				ClearSceneEvents( CChoreoScene *scene, bool canceled );

	// Stop specifics of event
	virtual	bool		ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled );

	// Add the event to the queue for this actor
	void				AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget = NULL );

	// Remove the event from the queue for this actor
	void				RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill );

	// Checks to see if the event should be considered "completed"
	bool				CheckSceneEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );

	// Checks to see if a event should be considered "completed"
	virtual bool		CheckSceneEventCompletion( CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event );

	// Finds the layer priority of the current scene
	int					GetScenePriority( CChoreoScene *scene );

	// Returns true if the actor is not currently in a scene OR if the actor
	//  is in a scene, but a PERMIT_RESPONSES event is active and the permit time
	//  period has enough time remaining to handle the response in full.
	bool				PermitResponse( float response_length );

	// Set response end time (0 to clear response blocking)
	void				SetPermitResponse( float endtime );

	void				SentenceStop( void ) { EmitSound( "AI_BaseNPC.SentenceStop" ); }

	virtual float		PlayScene( const char *pszScene, float flDelay = 0.0f, AI_Response *response = NULL, IRecipientFilter *filter = NULL );
	virtual float		PlayAutoGeneratedSoundScene( const char *soundname );

	virtual int			GetSpecialDSP( void ) { return 0; }

protected:
	// For handling .vfe files
	// Search list, or add if not in list
	const void			*FindSceneFile( const char *filename );

	// Find setting by name
	const flexsetting_t *FindNamedSetting( const flexsettinghdr_t *pSettinghdr, const char *expr );

	// Called at the lowest level to actually apply an expression
	void				AddFlexSetting( const char *expr, float scale, const flexsettinghdr_t *pSettinghdr, bool newexpression );

	// Called at the lowest level to actually apply a flex animation
	void				AddFlexAnimation( CSceneEventInfo *info );

	bool				HasSceneEvents() const;
	bool				IsRunningSceneMoveToEvent();

	LocalFlexController_t	FlexControllerLocalToGlobal( const flexsettinghdr_t *pSettinghdr, int key );

private:
	// Starting various expression types 

	bool RequestStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget );
	bool RequestStartGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget );

	bool HandleStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor );
	bool HandleStartGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor );
	bool StartFacingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget );
	bool StartMoveToSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget );

	// Processing various expression types
	bool ProcessFlexAnimationSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );
	bool ProcessFlexSettingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );
	bool ProcessSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );
	bool ProcessGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );
	bool ProcessFacingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );
	bool ProcessMoveToSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );
	bool ProcessLookAtSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );

	// Set playing the scene sequence
public:
	bool EnterSceneSequence( CChoreoScene *scene, CChoreoEvent *event, bool bRestart = false );
private:
	bool ExitSceneSequence( void );

private:
	CNetworkArray( float, m_flexWeight, MAXSTUDIOFLEXCTRL );	// indexed by model local flexcontroller

	// Vector from actor to eye target
	CNetworkVector( m_viewtarget );

	// Blink state
	CNetworkVar( int, m_blinktoggle );

	// Array of active SceneEvents, in order oldest to newest
	CUtlVector < CSceneEventInfo >		m_SceneEvents;

	// Mapping for each loaded scene file used by this actor
	struct FS_LocalToGlobal_t
	{
		explicit FS_LocalToGlobal_t() :
			m_Key( 0 ),
			m_nCount( 0 ),
			m_Mapping( 0 )
		{
		}

		explicit FS_LocalToGlobal_t( const flexsettinghdr_t *key ) :
			m_Key( key ),
			m_nCount( 0 ),
			m_Mapping( 0 )
		{
		}		

		void SetCount( int count )
		{
			Assert( !m_Mapping );
			Assert( count > 0 );
			m_nCount = count;
			m_Mapping = new LocalFlexController_t[ m_nCount ];
			Q_memset( m_Mapping, 0, m_nCount * sizeof( int ) );
		}

		FS_LocalToGlobal_t( const FS_LocalToGlobal_t& src )
		{
			m_Key = src.m_Key;
			delete m_Mapping;
			m_Mapping = new LocalFlexController_t[ src.m_nCount ];
			Q_memcpy( m_Mapping, src.m_Mapping, src.m_nCount * sizeof( int ) );

			m_nCount = src.m_nCount;
		}

		~FS_LocalToGlobal_t()
		{
			delete m_Mapping;
			m_nCount = 0;
			m_Mapping = 0;
		}

		const flexsettinghdr_t	*m_Key;
		int						m_nCount;
		LocalFlexController_t	*m_Mapping;	
	};

	static bool FlexSettingLessFunc( const FS_LocalToGlobal_t& lhs, const FS_LocalToGlobal_t& rhs );

	CUtlRBTree< FS_LocalToGlobal_t, unsigned short > m_LocalToGlobal;

	// The NPC is in a scene, but another .vcd (such as a short wave to say in response to the player doing something )
	//  can be layered on top of this actor (assuming duration matches, etc.
	float				m_flAllowResponsesEndTime;

	// List of actively playing scenes
	CUtlVector < CChoreoScene * >		m_ActiveChoreoScenes;
	bool				m_bUpdateLayerPriorities;

public:
	bool				IsSuppressedFlexAnimation( CSceneEventInfo *info );

private:
	// last time a foreground flex animation was played
	float				m_flLastFlexAnimationTime;


public:
	void DoBodyLean( void );

	virtual void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity );


#ifdef HL2_DLL
	Vector m_vecPrevOrigin;
	Vector m_vecPrevVelocity;
	CNetworkVector( m_vecLean );
	CNetworkVector( m_vecShift );
#endif
};


//-----------------------------------------------------------------------------
// For toggling blinking
//-----------------------------------------------------------------------------
inline void CBaseFlex::Blink()
{
	m_blinktoggle = !m_blinktoggle;
}

//-----------------------------------------------------------------------------
// Do we have active expressions?
//-----------------------------------------------------------------------------
inline bool CBaseFlex::HasSceneEvents() const
{
	return m_SceneEvents.Count() != 0;
}


//-----------------------------------------------------------------------------
// Other inlines
//-----------------------------------------------------------------------------
inline const Vector &CBaseFlex::GetViewtarget( ) const
{
	return m_viewtarget.Get();	// bah
}

inline void CBaseFlex::SetFlexWeight( char *szName, float value )
{
	SetFlexWeight( FindFlexController( szName ), value );
}

inline float CBaseFlex::GetFlexWeight( char *szName )
{
	return GetFlexWeight( FindFlexController( szName ) );
}


EXTERN_SEND_TABLE(DT_BaseFlex);


#endif // BASEFLEX_H