#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif

#ifndef __ASURA_CUTSCENE_SYSTEM_H__
#define __ASURA_CUTSCENE_SYSTEM_H__

#include "Core/Asura_HashTree.h"
#include "Debugging/Asura_Assert.h"
#include "Network/Asura_Guid.h"

class Asura_Cutscene;
class Asura_Cutscene_Actor;
class Asura_Stream;
class Asura_Vector_3;

class Asura_Cutscene_Handler
{
	public:
		Asura_Cutscene_Handler();
		~Asura_Cutscene_Handler();

		void Initialise(void);
		void Shutdown(void);
		void Reset(void);

		void Update();   // call update on all CurrentCutscenes which play on server.

		Asura_Cutscene* GetCutsceneByHash( const Asura_Hash_ID uHash );
		bool AddCutscene(Asura_Cutscene* pxCutscene);

#ifdef ASURA_TOOLS
		//returns the index of the cutscene , or -1 if it doesn't exist
		int GetCutsceneIndex( const char* szName );

		inline Asura_Cutscene* GetCutsceneByIndex(int iID)
		{
			Asura_Assert((0 <= iID) && (iID < m_xCutsceneStorage.GetNumberOfNodes()), "invalid current cutscene index");
			Asura_Cutscene** ppxCutscene = m_xCutsceneStorage.GetDataByIndex(iID);
			if (ppxCutscene)
			{
				return *ppxCutscene;
			}
			return NULL;
		}
#endif //#ifdef ASURA_TOOLS

		inline int GetNumberOfCutscenes(void)
		{
			return m_xCutsceneStorage.GetNumberOfNodes();
		}

		//removes the cutscene from the list of stored cutscenes (but doesn't delete)
		void DeleteCutscene( const char* szName ) { DeleteCutsceneByHash(Asura_GetHashID(szName)); }
		void DeleteCutsceneByHash( const Asura_Hash_ID uHash );

		// (MH - 09.08.2002) added system for getting currently-playing cutscenes.
		void CutsceneStarted(Asura_Cutscene* pxCutscene);
		void CutsceneFinished(Asura_Cutscene* pxCutscene);
	    

#ifdef ASURA_TOOLS
		// AJG - 28/03/06 - As with the overall cutscenes, this is now tools-only
		inline const Asura_Cutscene* GetCurrentCutsceneByIndex(int iID)
		{
			Asura_Assert((0 <= iID) && (iID < m_xCurrentCutscenes.GetNumberOfNodes()), "invalid current cutscene index");
			Asura_Cutscene** ppxCutscene = m_xCurrentCutscenes.GetDataByIndex(iID);
			if (ppxCutscene)
			{
				return *ppxCutscene;
			}
			return NULL;
		}
#endif //#ifdef ASURA_TOOLS

		inline const Asura_Cutscene* GetCurrentCutsceneByHash(Asura_Hash_ID uHash)
		{
			Asura_Cutscene** ppxCutscene =  m_xCurrentCutscenes.GetDataByHash(uHash);
			if (ppxCutscene)
			{
				return *ppxCutscene;
			}
			return NULL;
		}

		inline int GetNumberOfCurrentCutscenes(void)
		{
			return m_xCurrentCutscenes.GetNumberOfNodes();
		}
	    
		void StopAllCutscenes(void);

		void CutsceneProfileRequestedByClient( Asura_Hash_ID uCutsceneHash );

		bool ShouldRenderWidescreenBars();

		void RenderSubtitles();

		bool RenameCutscene(const char* szOldName,const char* szNewName);

		#ifdef ASURA_TOOLS
		//at the moment for getting the corona textures used. RAR 24/01/03
		void BuildUsedTextureTree(class Asura_NameTree* pxNameTree);
		#endif

		// Returns true iff there's a cutscene that starts active and controls
		// the camera. If the return value is true then the vector and
		// cutscene ptr specified will return the relevant information. They
		// not be modified if the function returns false.
		bool GetCutsceneStartPosition( Asura_Vector_3* pxPos = NULL, const Asura_Cutscene** ppxCutscene = NULL );

		// Returns true iff there's a cutscene that starts active and controls
		// the camera.
		bool AnyOpeningCutscenes( void );

		// (AAM - 29.04.2004) Find the cutscene actor with the given guid
		Asura_Cutscene_Actor* FindCutsceneActor(Asura_Guid uGuid);

		//Resets the enabled events flag for all cutscenes.
		//Mainly for tools use to make sure all the cutscene get set back to their default state
		void ResetEnabledEventFlags();

		bool GetCanSkipCutscenes();
		void SkipCutscenes();

		// Generic traversals. We'll assume that exactly one of the static function pointers is non-null to decide which sort of traversal we're doing.
		void TraverseAllCutscenes();
		void TraverseCurrentCutscenes();

	private:
		// CALLBACKS
		static void DestroyCutscene_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene);

		static void Update_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene);

		static void StopAllCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene);

		// The user data here is a bool, set to true if we find a cutscene that needs the bars
		static bool ShouldRenderWidescreenBars_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene, void* pUserData);

		static void RenderSubtitles_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene);

#ifdef ASURA_TOOLS
		// The user data here is the name list needed by BuildUsedTextureTree
		static bool BuildUsedTextureTree_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene, void* pUserData);
#endif //#ifdef ASURA_TOOLS

		// The user data is pointer to the pointer of the cutscene we've found. NULL until successfully set.
		static bool GetCutsceneStartPosition_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene, void* pUserData);

		static Asura_Guid s_uFindCutsceneActorGuid;
		// The user data is a pointer to the pointer of the actor we're looking for. NULL until set be the successful FindCutsceneActor call.
		static bool FindCutsceneActor_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene, void* pUserData);

		static void ResetEnabledEventFlags_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene);

		// The user data is a pointer to a bool set to false if we find a relevant cutscene that can't be skipped at this time.
		static bool GetCanSkipCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene, void* pUserData);

		static void SkipCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene);

		static void AddToCurrentCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene);
		void AddToCurrentCutscenes(Asura_Hash_ID uHash, class Asura_Cutscene* pxCutscene);
		static void RemoveFromCurrentCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene);
		void RemoveFromCurrentCutscenes(Asura_Hash_ID uHash);

		// Hash trees store cutscene pointers since we may have subclassed cutscenes here.
		// This complicates deletion slightly, since the cutscenes themselves won't be automatically deleted when the tree is cleared, but this shouldn't cause a problem if people are aware of this.
		Asura_TemplateHashTree<Asura_Cutscene*> m_xCutsceneStorage;

		// The pointers may also cause issues here, since we won't be keeping track of whether they've been deleted from the main storage.
		// Be sure to remove nodes from here appropriately when removing nodes from main storage.
		Asura_TemplateHashTree<Asura_Cutscene*> m_xCurrentCutscenes;

		// Pair of hash trees for initialising and finishing. Traversed both before and after update unfortuantely (although I'm not sure the post-update initialise traversal is needed), but normally empty
		Asura_TemplateHashTree<Asura_Cutscene*> m_xInitialisingCutscenes;
		Asura_TemplateHashTree<Asura_Cutscene*> m_xFinishingCutscenes;

		// Functions for generic callbacks
		static void (*s_pfnGenericCutsceneCallbackWithoutData)(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene);
		static bool (*s_pfnGenericCutsceneCallbackWithData)(Asura_Hash_ID uHash, class Asura_Cutscene** ppxCutscene, void* pUserData);
		static void* s_pGenericCutsceneCallbackUserData;

		friend class Asura_Cutscene_System;
};

class Asura_Cutscene_System
{
	public:
		static void Initialise(void);
		static void Shutdown(void);
		static void Reset(void);

		static void Update();   // call update on all s_pxCurrentCutscenes which play on server.

		static Asura_Cutscene* GetCutsceneByHash( const Asura_Hash_ID uHash ) { return GetCurrentHandler().GetCutsceneByHash(uHash); }
		// Deprecated method to smooth transition to hash tree.
		static Asura_Cutscene* GetCutscene( const char* szName )
		{
			return GetCutsceneByHash(Asura_GetHashID(szName));
		}
		static bool AddCutscene(Asura_Cutscene* pxCutscene) { return GetCurrentHandler().AddCutscene(pxCutscene); }

#ifdef ASURA_TOOLS
		//returns the index of the cutscene , or -1 if it doesn't exist
		// AJG - 28/03/06 - Tools-only now, because we're changing to using a hash tree which doesn't know any indexes outside of tools.
		static int GetCutsceneIndex( const char* szName ) { return GetCurrentHandler().GetCutsceneIndex(szName); }

		static inline Asura_Cutscene* GetCutsceneByIndex(int iID) { return GetCurrentHandler().GetCutsceneByIndex(iID); }
#endif //#ifdef ASURA_TOOLS

		static inline int GetNumberOfCutscenes(void) { return GetCurrentHandler().GetNumberOfCutscenes(); }

		// Set/Get subtitles font
		static inline u_int GetSubtitleFontIndex()				{ return s_uSubtitleFontIndex;	}
		static inline void  SetSubtitleFontIndex( u_int uFI	)	{ s_uSubtitleFontIndex = uFI;	}

		//removes the cutscene from the list of stored cutscenes (but doesn't delete)
		static void DeleteCutscene( const char* szName ) { GetCurrentHandler().DeleteCutscene(szName); }
		static void DeleteCutsceneByHash( const Asura_Hash_ID uHash ) { GetCurrentHandler().DeleteCutsceneByHash(uHash); }

		// (MH - 09.08.2002) added system for getting currently-playing cutscenes.
		static void CutsceneStarted(Asura_Cutscene* pxCutscene) { GetCurrentHandler().CutsceneStarted(pxCutscene); }
		static void CutsceneFinished(Asura_Cutscene* pxCutscene) { GetCurrentHandler().CutsceneFinished(pxCutscene); }

#ifdef ASURA_TOOLS
		// AJG - 28/03/06 - As with the overall cutscenes, this is now tools-only
		static inline const Asura_Cutscene* GetCurrentCutsceneByIndex(int iID) { return GetCurrentHandler().GetCurrentCutsceneByIndex(iID); }
#endif //#ifdef ASURA_TOOLS

		static inline const Asura_Cutscene* GetCurrentCutsceneByHash(Asura_Hash_ID uHash) { return GetCurrentHandler().GetCurrentCutsceneByHash(uHash); }

		static inline int GetNumberOfCurrentCutscenes(void) { return GetCurrentHandler().GetNumberOfCurrentCutscenes(); }

		// AJG - 28/03/06 - First version: stop all cutscenes in all handlers, making the assumption that when this is called we're wanting to kill off everything in all zones.
		static void StopAllCutscenes(void);

		static void CutsceneProfileRequestedByClient( Asura_Hash_ID uCutsceneHash ) { GetCurrentHandler().CutsceneProfileRequestedByClient(uCutsceneHash); }

		static void SetWidescreenBarColour(u_int uColour)		{ s_uWidescreenBarColour = uColour; }
		static u_int GetWidescreenBarColour()					{ return s_uWidescreenBarColour; }

		static void SetWidescreenRatio(float fX, float fY);
		static float GetWidescreenDisplayedXOverYRatio();	// to set viewport size in camera FOV setting
		static float GetWidescreenBarHeight();
		// In streaming builds, this checks all zones
		static bool ShouldRenderWidescreenBars();
		static void RenderWidescreenBars();

		static void SetRenderWidescreenBarsFunction(void (*pfnRenderWidescreenBars)()){s_pfnRenderWidescreenBars = pfnRenderWidescreenBars;}

		static void RenderSubtitles() { TraverseCurrentCutscenes(RenderSubtitles_Callback, true); }
		static void RenderSubtitles_Callback(Asura_Hash_ID uHash, Asura_Cutscene** ppxCutscene);

		static bool RenameCutscene(const char* szOldName,const char* szNewName) { return GetCurrentHandler().RenameCutscene(szOldName, szNewName); }

		#ifdef ASURA_TOOLS
		//at the moment for getting the corona textures used. RAR 24/01/03
		static void BuildUsedTextureTree(class Asura_NameTree* pxNameTree) { return GetCurrentHandler().BuildUsedTextureTree(pxNameTree); }
		#endif


		// Returns true iff there's a cutscene that starts active and controls
		// the camera. If the return value is true then the vector and
		// cutscene ptr specified will return the relevant information. They
		// not be modified if the function returns false.
		// AJG - 28/03/06 - TODO: Should this check all handlers?
		static bool GetCutsceneStartPosition( Asura_Vector_3* pxPos = NULL, const Asura_Cutscene** ppxCutscene = NULL )
		{
			return GetCurrentHandler().GetCutsceneStartPosition(pxPos, ppxCutscene);
		}

		// Returns true iff there's a cutscene that starts active and controls
		// the camera.
		static bool AnyOpeningCutscenes( void );

		//Is a cutscene currently playing that uses a soundtrack
		static bool IsPlayingSoundTrack() { return s_pxCutscenePlayingSoundTrack!=0; }

		//Returns true if there is a cutscene playing a soundtrack , and it doesn't have the don't pause streams flags
		static bool ShouldPauseStreamsForCutsceneSoundTrack();

		//used when cutscene starts/stops to set the playing soundtrack flag
		static void SetCutscenePlayingSoundTrack(Asura_Cutscene* pxCutscene, bool bSet);

		// (AAM - 29.04.2004) Find the cutscene actor with the given guid
		static Asura_Cutscene_Actor* FindCutsceneActor(Asura_Guid uGuid) { return GetCurrentHandler().FindCutsceneActor(uGuid); }

		//Resets the enabled events flag for all cutscenes.
		//Mainly for tools use to make sure all the cutscene get set back to their default state
		// TODO - is it good enough just to go through the current handler?
		static void ResetEnabledEventFlags() { GetCurrentHandler().ResetEnabledEventFlags(); }

		// Standardised cutscene skipping - one version which checks if it's possible and carries out out if it is (including passing the information across the network)...
		static bool CheckAndRequestSkipCutscenes(float fMinPlayingTime);
		// ...The other version separates the check and the request, and doesn't do the messaging, allowing projects to change the interaction between server and client
		// (also allows the message from the above version to just call the skipping stage).
		static bool GetCanSkipCutscenes(float fMinPlayingTime);
		static void RequestSkipCutscenes();

		// Generic traversals
		static void TraverseAllCutscenes(bool (*pfnCallback)(Asura_Hash_ID, class Asura_Cutscene**, void*), void* pUserData, bool bAllZones = false);
		static void TraverseAllCutscenes(void (*pfnCallback)(Asura_Hash_ID, class Asura_Cutscene**), bool bAllZones = false);
		static void TraverseCurrentCutscenes(bool (*pfnCallback)(Asura_Hash_ID, class Asura_Cutscene**, void*), void* pUserData, bool bAllZones = false);
		static void TraverseCurrentCutscenes(void (*pfnCallback)(Asura_Hash_ID, class Asura_Cutscene**), bool bAllZones = false);

#if defined(ASURA_USE_MULTIPLE_HANDLERS)
		// Handler functions...
		static	void					ReparentHandler(const Asura_Hash_ID uSourceHash, const Asura_Hash_ID uNewParentHash);
		static	void					ReparentHandler(Asura_Cutscene_Handler& xSourceHandler, const Asura_Hash_ID uNewParentHash);

		static	Asura_Cutscene_Handler*	CreateHandler(const Asura_Hash_ID uHash, const Asura_Hash_ID uParentHandlerHash);
		static	bool					DestroyHandler(const Asura_Hash_ID uHash);
		static	void					WriteHandlerReport(u_int uHandler, Asura_Stream& xLog);

		static	void					CreateAndSetHandler(const Asura_Hash_ID uHash, const Asura_Hash_ID uParentHandlerHash)	{ s_pxCurrentHandler = CreateHandler(uHash, uParentHandlerHash); }
		static	void					SetCurrentHandler(Asura_Cutscene_Handler* pxHandler)	{ s_pxCurrentHandler = pxHandler; }
		static	void					SetCurrentHandler(const Asura_Hash_ID uHash)			{ s_pxCurrentHandler = s_xHandlerTree.GetDataByHash(uHash); } // AJG - 31/03/06 - shouldn't we check this is actually in the tree?
		static	Asura_Cutscene_Handler&	GetCurrentHandler(void)									{ return *s_pxCurrentHandler; }
		static	Asura_Cutscene_Handler*	GetHandler(const Asura_Hash_ID uHash)					{ return s_xHandlerTree.GetDataByHash(uHash); }
#else
		// Handler functions...
		static	Asura_Cutscene_Handler&	GetCurrentHandler(void) { return s_xHandler; }
#endif

	private:


#if defined(ASURA_USE_MULTIPLE_HANDLERS)
		// Callbacks for tree traversals
		static void Reset_Callback(Asura_Hash_ID uHash, class Asura_Cutscene_Handler* pxData);
		static void StopAllCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene_Handler* pxData);
		static void SkipCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene_Handler* pxData);
		// The user data is a pointer to a bool set to false if we find a relevant cutscene that can't be skipped at this time.
		static bool GetCanSkipCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene_Handler* pxData, void* pUserData);
		// The user data here is a bool, set to true if we find a cutscene that needs the bars
		static bool ShouldRenderWidescreenBars_Callback(Asura_Hash_ID uHash, class Asura_Cutscene_Handler* pxData, void* pUserData);
		// Generic traversals across all zones
		// Note that the way these are done for now means that traversals which can exit early but search all zones won't be prevented from searching all remaining zones if they hit their early exit.
		static void TraverseAllCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene_Handler* pxData);
		static void TraverseCurrentCutscenes_Callback(Asura_Hash_ID uHash, class Asura_Cutscene_Handler* pxData);

        static void Update_Callback(Asura_Hash_ID uHash, class Asura_Cutscene_Handler* pxData);

		static Asura_Cutscene_Handler*							s_pxCurrentHandler;		// Currently active handler.
		static Asura_TemplateHashTree<Asura_Cutscene_Handler>	s_xHandlerTree;			// Tree of all of the handlers available.
#else
		static Asura_Cutscene_Handler s_xHandler;				// Single static-instance of a zone.
#endif

		// These variables remain static members of the overall system
		static u_int s_uWidescreenBarColour;
		static float s_fWidescreenRatio;
		static u_int s_uSubtitleFontIndex;
		static Asura_Cutscene* s_pxCutscenePlayingSoundTrack;

		static void (*s_pfnRenderWidescreenBars)();

		static float s_fCutsceneSkipTime;

		friend class Asura_Cutscene_Handler;
};

#endif//__ASURA_MESH_SYSTEM_H__
