#ifndef C_STUDIOFLEX_H
#define C_STUDIOFLEX_H
#pragma once


#include "c_baseanimating.h"
#include "c_baseanimatingoverlay.h"
#include "sceneentity_shared.h"

#include "utlvector.h"

class CFlexSceneFile
{
public:
	enum
	{
		MAX_FLEX_FILENAME = 128,
	};

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

struct Emphasized_Phoneme;
class CSentence;

enum
{
	PHONEME_CLASS_WEAK = 0,
	PHONEME_CLASS_NORMAL,
	PHONEME_CLASS_STRONG,

	NUM_PHONEME_CLASSES
};

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 int[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 int[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;
	int* m_Mapping;
};

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

class IHasLocalToGlobalFlexSettings
{
public:
	virtual void		EnsureTranslations(const flexsettinghdr_t* pSettinghdr) = 0;
};

struct Emphasized_Phoneme
{
	char			classname[64];
	bool			required;
	bool			basechecked;
	const flexsettinghdr_t* base;
	const flexsetting_t* exp;

	bool			valid;
	float			amount;
};

class C_BaseFlex : public C_BaseAnimatingOverlay, public IHasLocalToGlobalFlexSettings
{
	DECLARE_CLASS(C_BaseFlex, C_BaseAnimatingOverlay);
public:
	DECLARE_CLIENTCLASS();
	DECLARE_PREDICTABLE();
	DECLARE_INTERPOLATION();

	C_BaseFlex();
	virtual			~C_BaseFlex();

	virtual void Spawn();

	virtual void InitPhonemeMappings();

	void		SetupMappings(char const* pchFileRoot);

	virtual CStudioHdr* OnNewModel(void);

	virtual void	StandardBlendingRules(CStudioHdr* hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask);

	virtual void OnThreadedDrawSetup();

	virtual void BuildTransformations(CStudioHdr* pStudioHdr, Vector* pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList& boneComputed);
	static void		LinkToGlobalFlexControllers(CStudioHdr* hdr);
	virtual	void	SetupWeights(const matrix3x4_t* pBoneToWorld, int nFlexWeightCount, float* pFlexWeights, float* pFlexDelayedWeights);
	virtual	bool	SetupGlobalWeights(const matrix3x4_t* pBoneToWorld, int nFlexWeightCount, float* pFlexWeights, float* pFlexDelayedWeights);
	static void		RunFlexDelay(int nFlexWeightCount, float* pFlexWeights, float* pFlexDelayedWeights, float& flFlexDelayTime);
	virtual	void	SetupLocalWeights(const matrix3x4_t* pBoneToWorld, int nFlexWeightCount, float* pFlexWeights, float* pFlexDelayedWeights);
	virtual bool	UsesFlexDelayedWeights();

	static	void	RunFlexRules(CStudioHdr* pStudioHdr, float* dest);

	virtual Vector	SetViewTarget(CStudioHdr* pStudioHdr);

	virtual bool	GetSoundSpatialization(SpatializationInfo_t& info);

	virtual void	GetToolRecordingState(KeyValues* msg);

	void				AddFlexAnimation(CSceneEventInfo* info);

	void			SetFlexWeight(LocalFlexController_t index, float value);
	float			GetFlexWeight(LocalFlexController_t index);

	LocalFlexController_t				FindFlexController(const char* szName);

public:
	Vector			m_viewtarget;
	CInterpolatedVar< Vector >	m_iv_viewtarget;
	float			m_flexWeight[MAXSTUDIOFLEXCTRL];
	CInterpolatedVarArray< float, MAXSTUDIOFLEXCTRL >	m_iv_flexWeight;

	int				m_blinktoggle;

	static int		AddGlobalFlexController(const char* szName);
	static char const* GetGlobalFlexControllerName(int idx);

public:

	void				StartChoreoScene(CChoreoScene* scene);
	void				RemoveChoreoScene(CChoreoScene* scene);

	virtual bool		StartSceneEvent(CSceneEventInfo* info, CChoreoScene* scene, CChoreoEvent* event, CChoreoActor* actor, C_BaseEntity* pTarget);

	virtual void		ProcessSceneEvents(bool bFlexEvents);

	virtual	bool		ProcessSceneEvent(bool bFlexEvents, CSceneEventInfo* info, CChoreoScene* scene, CChoreoEvent* event);

	virtual bool		ProcessSequenceSceneEvent(CSceneEventInfo* info, CChoreoScene* scene, CChoreoEvent* event);

	void				ClearSceneEvents(CChoreoScene* scene, bool canceled);

	virtual	bool		ClearSceneEvent(CSceneEventInfo* info, bool fastKill, bool canceled);

	void				AddSceneEvent(CChoreoScene* scene, CChoreoEvent* event, C_BaseEntity* pTarget = NULL, bool bClientSide = false);

	void				RemoveSceneEvent(CChoreoScene* scene, CChoreoEvent* event, bool fastKill);

	bool				CheckSceneEvent(float currenttime, CChoreoScene* scene, CChoreoEvent* event);

	virtual bool		CheckSceneEventCompletion(CSceneEventInfo* info, float currenttime, CChoreoScene* scene, CChoreoEvent* event);

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

	virtual void		EnsureTranslations(const flexsettinghdr_t* pSettinghdr);

	void* FindSceneFile(const char* filename);

private:

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

	bool ProcessFlexAnimationSceneEvent(CSceneEventInfo* info, CChoreoScene* scene, CChoreoEvent* event);
	bool ProcessFlexSettingSceneEvent(CSceneEventInfo* info, CChoreoScene* scene, CChoreoEvent* event);
	void AddFlexSetting(const char* expr, float scale,
		const flexsettinghdr_t* pSettinghdr, bool newexpression);

	CUtlVector < CSceneEventInfo >		m_SceneEvents;
	CUtlVector < CChoreoScene* >		m_ActiveChoreoScenes;

	bool				HasSceneEvents() const;

private:
	CUtlRBTree< FS_LocalToGlobal_t, unsigned short > m_LocalToGlobal;

	float			m_blinktime;
	int				m_prevblinktoggle;

	int				m_iBlink;
	LocalFlexController_t				m_iEyeUpdown;
	LocalFlexController_t				m_iEyeRightleft;
	bool			m_bSearchedForEyeFlexes;
	int				m_iMouthAttachment;

	float			m_flFlexDelayTime;
	float* m_flFlexDelayedWeight;
	int				m_cFlexDelayedWeight;

	static int		g_numflexcontrollers;
	static char* g_flexcontroller[MAXSTUDIOFLEXCTRL * 4];       
	static float	g_flexweight[MAXSTUDIOFLEXDESC];

protected:

	Emphasized_Phoneme m_PhonemeClasses[NUM_PHONEME_CLASSES];

private:

	C_BaseFlex(const C_BaseFlex&);     

	const flexsetting_t* FindNamedSetting(const flexsettinghdr_t* pSettinghdr, const char* expr);

	void			ProcessVisemes(Emphasized_Phoneme* classes);
	void			AddVisemesForSentence(Emphasized_Phoneme* classes, float emphasis_intensity, CSentence* sentence, float t, float dt, bool juststarted);
	void			AddViseme(Emphasized_Phoneme* classes, float emphasis_intensity, int phoneme, float scale, bool newexpression);
	bool			SetupEmphasisBlend(Emphasized_Phoneme* classes, int phoneme);
	void			ComputeBlendedSetting(Emphasized_Phoneme* classes, float emphasis_intensity);

#ifdef HL2_CLIENT_DLL
public:

	Vector			m_vecLean;
	CInterpolatedVar< Vector >	m_iv_vecLean;
	Vector			m_vecShift;
	CInterpolatedVar< Vector >	m_iv_vecShift;
#endif
};


class CFlexSceneFileManager : CAutoGameSystem
{
public:

	CFlexSceneFileManager() : CAutoGameSystem("CFlexSceneFileManager")
	{
	}

	virtual bool Init();
	virtual void Shutdown();

	void EnsureTranslations(IHasLocalToGlobalFlexSettings* instance, const flexsettinghdr_t* pSettinghdr);
	void* FindSceneFile(IHasLocalToGlobalFlexSettings* instance, const char* filename, bool allowBlockingIO);

private:
	void DeleteSceneFiles();

	CUtlVector< CFlexSceneFile* > m_FileList;
};


inline bool C_BaseFlex::HasSceneEvents() const
{
	return m_SceneEvents.Count() != 0;
}


EXTERN_RECV_TABLE(DT_BaseFlex);

float* GetVisemeWeights(int phoneme);


#endif  
