//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================

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

class CChoreoEvent;
class CChoreoChannel;
class CChoreoActor;
class IChoreoEventCallback;
class CEventRelativeTag;
class CUtlBuffer;
class CFlexAnimationTrack;

#include "utlvector.h"
#include "expressionsample.h"

class ISceneTokenProcessor;

#define DEFAULT_SCENE_FPS	60
#define MIN_SCENE_FPS		10
#define MAX_SCENE_FPS		240

//-----------------------------------------------------------------------------
// Purpose: Container for chereographed scene of events for actors
//-----------------------------------------------------------------------------
class CChoreoScene
{
public:
	// Construction
					CChoreoScene( IChoreoEventCallback *callback );
					~CChoreoScene( void );

	// Assignment
	CChoreoScene&	operator=(const CChoreoScene& src );

	// Event callback handler
	void			SetEventCallbackInterface( IChoreoEventCallback *callback );

	// Loading
	bool			ParseFromBuffer( ISceneTokenProcessor *tokenizer );
	void			SetPrintFunc( void ( *pfn )( const char *fmt, ... ) );

	// Saving
	bool			SaveToFile( const char *filename );

	static void		FileSaveFlexAnimationTrack( CUtlBuffer& buf, int level, CFlexAnimationTrack *track );
	static void		FileSaveFlexAnimations( CUtlBuffer& buf, int level, CChoreoEvent *e );
	static void		FileSaveRamp( CUtlBuffer& buf, int level, CChoreoEvent *e );
	static void		FileSaveSceneRamp( CUtlBuffer& buf, int level, CChoreoScene *scene );

	static void		ParseFlexAnimations( ISceneTokenProcessor *tokenizer, CChoreoEvent *e, bool removeold = true );
	static void		ParseRamp( ISceneTokenProcessor *tokenizer, CChoreoEvent *e );
	static void		ParseSceneRamp( ISceneTokenProcessor *tokenizer, CChoreoScene *scene );

	// Debugging
	void			Print( void );

	// Sound system needs to have sounds pre-queued by this much time
	void			SetSoundFileStartupLatency( float time );

	// Simulation
	void			Think( float curtime );
	// Retrieves time in simulation
	float			GetTime( void );
	// Retrieves start/stop time for looped/debug scene
	void			GetSceneTimes( float& start, float& end );

	void			LoopToTime( float t );

	// Has simulation finished
	bool			SimulationFinished( void );
	// Reset simulation
	void			ResetSimulation( bool forward = true, float starttime = 0.0f, float endtime = 0.0f );
	// Find time at which last simulation event is triggered
	float			FindStopTime( void );

	void			ResumeSimulation( void );

	// Have all the pause events happened
	bool			CheckEventCompletion( void );

	// Find named actor in scene data
	CChoreoActor	*FindActor( const char *name );
	// Remove actor from scene
	void			RemoveActor( CChoreoActor *actor );
	// Find index for actor
	int				FindActorIndex( CChoreoActor *actor );

	// Swap actors in the data
	void			SwapActors( int a1, int a2 );

	// General data access
	int				GetNumEvents( void );
	CChoreoEvent	*GetEvent( int event );

	int				GetNumActors( void );
	CChoreoActor	*GetActor( int actor );
	
	int				GetNumChannels( void );
	CChoreoChannel	*GetChannel( int channel );
	int				GetChannelIndex( CChoreoChannel	*pChannel );

	// Object allocation/destruction
	void			DeleteReferencedObjects( CChoreoActor *actor );
	void			DeleteReferencedObjects( CChoreoChannel *channel );
	void			DeleteReferencedObjects( CChoreoEvent *event );

	CChoreoActor	*AllocActor( void );
	CChoreoChannel	*AllocChannel( void );
	CChoreoEvent	*AllocEvent( void );

	void			AddEventToScene( CChoreoEvent *event );
	void			AddActorToScene( CChoreoActor *actor );
	void			AddChannelToScene( CChoreoChannel *channel );

	// Go through all elements and update relative tags, removing any orphaned
	// tags and updating the timestamp of normal tags
	void			ReconcileTags( void );
	CEventRelativeTag	*FindTagByName( const char *wavname, const char *name );
	CChoreoEvent	*FindTargetingEvent( const char *wavname, const char *name );

	// Used by UI to provide target actor names
	char const		*GetMapname( void );
	void			SetMapname( const char *name );

	void			ExportEvents( const char *filename, CUtlVector< CChoreoEvent * >& events );
	void			ImportEvents( ISceneTokenProcessor *tokenizer, CChoreoActor *actor, CChoreoChannel *channel, float starttime );

	// Subscene support
	void			SetSubScene( bool sub );
	bool			IsSubScene( void ) const;

	int				GetSceneFPS( void ) const;
	void			SetSceneFPS( int fps );
	bool			IsUsingFrameSnap( void ) const;
	void			SetUsingFrameSnap( bool snap );

	float			SnapTime( float t );

	int				GetSceneRampCount( void );
	CExpressionSample *GetSceneRamp( int index );
	void			AddSceneRamp( float time, float value, bool selected );
	void			DeleteSceneRamp( int index );
	void			ClearSceneRamp( void );
	void			ResortSceneRamp( void );
	// remove any samples after endtime
	void			RemoveOutOfRangeSceneRampSamples( void );

	// Puts in dummy start/end samples to spline to zero ( or 0.5 for
	//  left/right data) at the origins
	CExpressionSample	*GetBoundedSceneRamp( int number );

	// Global intensity for scene
	float			GetSceneRampIntensity( float time );

private:
	// Simulation stuff
	enum
	{
		IN_RANGE = 0,
		BEFORE_RANGE,
		AFTER_RANGE
	};

	int				IsTimeInRange( float t, float starttime, float endtime );

	typedef enum 
	{
		PROCESSING_TYPE_IGNORE = 0,
		PROCESSING_TYPE_START,
		PROCESSING_TYPE_START_RESUMECONDITION,
		PROCESSING_TYPE_CONTINUE,
		PROCESSING_TYPE_STOP,
	} PROCESSING_TYPE;

	struct ActiveList
	{
		PROCESSING_TYPE		pt;
		CChoreoEvent		*e;
	};

	static bool EventLess( const CChoreoScene::ActiveList &al0, const CChoreoScene::ActiveList &al1 );

	int				EventThink( CChoreoEvent *e, 
						float frame_start_time, 
						float frame_end_time,
						bool playing_forward, PROCESSING_TYPE& disposition );

	// Prints to debug console, etc
	void			choreoprintf( int level, const char *fmt, ... );

	// Initialize scene
	void			Init( IChoreoEventCallback *callback );

	float			FindAdjustedStartTime( void );
	float			FindAdjustedEndTime( void );

	bool			FindPauseBetweenTimes( float starttime, float endtime );

	// Parse scenes from token buffer
	CChoreoEvent	*ParseEvent( CChoreoActor *actor, CChoreoChannel *channel, float timeoffset = 0.0f );
	CChoreoChannel	*ParseChannel( CChoreoActor *actor );
	CChoreoActor	*ParseActor( void );

	void			ParseFPS( void );
	void			ParseSnap( void );

	// Map file for retrieving named objects
	void			ParseMapname( void );
	// When previewing actor in hlfaceposer, this is the model to associate
	void			ParseFacePoserModel( CChoreoActor *actor );

	// Print to printfunc
	void			PrintEvent( int level, CChoreoEvent *e );
	void			PrintChannel( int level, CChoreoChannel *c );
	void			PrintActor( int level, CChoreoActor *a );

	// File I/O
	static void		FilePrintf( CUtlBuffer& buf, int level, const char *fmt, ... );
	void			FileSaveEvent( CUtlBuffer& buf, int level, CChoreoEvent *e, float starttimeoffset = 0.0f );
	void			FileSaveChannel( CUtlBuffer& buf, int level, CChoreoChannel *c );
	void			FileSaveActor( CUtlBuffer& buf, int level, CChoreoActor *a );
	void			FileSaveHeader( CUtlBuffer& buf );

	// Object destruction
	void			DestroyActor( CChoreoActor *actor );
	void			DestroyChannel( CChoreoChannel *channel );
	void			DestroyEvent( CChoreoEvent *event );

	// Global object storage
	CUtlVector < CChoreoEvent * >	m_Events;
	CUtlVector < CChoreoActor * >	m_Actors;
	CUtlVector < CChoreoChannel	* >	m_Channels;

	// These are just pointers, the actual objects are in m_Events
	CUtlVector < CChoreoEvent * >	m_ResumeConditions;
	// These are just pointers, the actual objects are in m_Events
	CUtlVector < CChoreoEvent * >	m_ActiveResumeConditions;
	// These are just pointers, the actual objects are in m_Events
	CUtlVector < CChoreoEvent * >	m_PauseEvents;

	// Current simulation time
	float			m_flCurrentTime;

	float			m_flStartTime;
	float			m_flEndTime;

	float			m_flEarliestTime;
	float			m_flLatestTime;
	int				m_nActiveEvents;

	// .wav file playback needs to issue play commands a bit ahead of time
	//  in order to hit exact marks
	float			m_flSoundSystemLatency;

	// Scene's linger a bit after finishing to let blends reset themselves
	float			m_flLastActiveTime;

	// Print callback function
	void ( *m_pfnPrint )( const char *fmt, ... );

	IChoreoEventCallback			*m_pIChoreoEventCallback;

	ISceneTokenProcessor			*m_pTokenizer;

	enum
	{
		MAX_MAPNAME = 128
	};

	char			m_szMapname[ MAX_MAPNAME ];

	bool			m_bSubScene;

	bool			m_bUseFrameSnap;
	int				m_nSceneFPS;

	// Global scene ramp/envelope
	CUtlVector< CExpressionSample > m_SceneRamp;
};

CChoreoScene *ChoreoLoadScene( 
	IChoreoEventCallback *callback, 
	ISceneTokenProcessor *tokenizer,
	void ( *pfn ) ( const char *fmt, ... ) );

#endif // CHOREOSCENE_H
