//////////////////////////////////////////////////////////////////////////////
//
// Asura_Dialogue_System.h
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
// Description:
//
//     provides the playback interface and manages access to event storage
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef ___ASURA_DIALOGUE_SYSTEM_H___
#define ___ASURA_DIALOGUE_SYSTEM_H___ 1

#ifdef ASURA_USE_DIALOGUE_SYSTEM

// I N C L U D E S ///////////////////////////////////////////////////////////
#include "Core/Asura_Maths.h"
#include "Core/Asura_Handle.h"
#include "Core/Asura_Hash.h"
#include "Core/Asura_String.h"
#include "Network/Asura_Guid.h"

// D E F I N E S /////////////////////////////////////////////////////////////
#ifndef ASURA_DIALOGUE_MAX_ACTIVE_EVENTS
#define ASURA_DIALOGUE_MAX_ACTIVE_EVENTS 2
#endif

#ifndef ASURA_DIALOGUE_MIN_SUBTITLE_DURATION
#define ASURA_DIALOGUE_MIN_SUBTITLE_DURATION 2.5f
#endif

#define ASURA_DIALOGUE_STATIC_MESSAGE_QUEUE_SIZE 8
#define ASURA_DIALOGUE_MAX_SUBTITLE_LENGTH 512

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////
class Asura_Dialogue_Event;
class Asura_Dialogue_Handler;
class Asura_Dialogue_Line;
class Asura_Dialogue_PlaybackObject;
class Asura_Dialogue_CharacterMappingList;
class Asura_Message;
class Asura_Dialogue_StaticMessage_QueueEntry;

// C L A S S E S /////////////////////////////////////////////////////////////



// Asura_Dialogue_System - interface class for dialogue playback calls
class Asura_Dialogue_System
{
public:

	static void Initialise();
	static void Reset();
	static void Shutdown();
	static void Update();

	// Checks to see if the provided guid is currently playing dialogue
	static bool IsGuidCurrentlyPlayingDialogue( Asura_Guid uGuid );

	// FindEvent() - lookup event by hashID
	static const Asura_Dialogue_Event* FindEvent(Asura_Hash_ID uHashID);

	// PlayEvent()
	// if pointer to Asura_Handle is passed in, the external handle is returned and must be 
	// released when the calling code is finised with it (by calling Stop() or FinishedWithHandle()
	// The version with a GUID parameter is meant for code-driven one-shot AI speech and the other
	// version has the mappings structure to cope with more complex conversations.
	static void PlayEvent( const Asura_Dialogue_Event* pxEvent, Asura_Guid uGuid, Asura_Handle* piHandleOut );
	static void PlayEvent( const Asura_Dialogue_Event* pxEvent, 
		const Asura_Dialogue_CharacterMappingList* pxMappings, Asura_Handle* piHandleOut );

	//Plays the dialogue event at a specified position , instead of on entities.
	//The guid isn't required , but if available it will be used for messages , subtitles etc
	static void PlayEvent( const Asura_Dialogue_Event* pxEvent, const Asura_Vector_3& xPosition, Asura_Guid uGuid,Asura_Handle* piHandleOut );

	// PlayLine()
	// Plays a single dialogue line at the position of the entity referred to by uGuid.
	static void PlayLine( const Asura_Dialogue_Line* pxLine, Asura_Guid uGuid, Asura_Handle* piHandleOut, Asura_Guid uInstance = ASURA_GUID_UNREGISTERED, int iNotifyFinishedMessageData = 0 );

	// Stop() destroys the dialogue playback object and releases the handle, so it uses the reference
	// to set the handle to be invalid automatically.
	static void Stop( Asura_Handle& iHandleRef );

	// Stops destroys the dialogue playback object associated with this guid. If there are two active dialogue playback objects assigned to this guid it will destriyed the first.
	// you shiuld be checking if anything is playing on this guid before playing a second item anyway!
	static void StopByGuid( Asura_Guid uGuid );

	//Stop all currently playing dialogue. RAR 06/11/2005
	static void StopAll();

	//Stop All Currently playing dialogue with internally handled handles.
	static void StopAllWithNoExternalHandle();


	// SkipLine() causes any dialogue lines that are currently playing to stop , causing events to move
	// onto the next line. RAR 18/12/2006
	static void SkipLine();


	// FinishedWithHandle() allows the dialogue system code to free up the event handle when the 
	// event has finished playing. The reference is used to set the handle to be invalid automatically.
	static void FinishedWithHandle( Asura_Handle& iHandleRef );

	// IsPlaying() returns whether the given playback object is playing
	static bool IsPlaying( Asura_Handle iHandle );

	// GetNumActiveHandles() returns the number of active dialogue handles - PJC 11/04/07
	static u_int GetNumActiveHandles();

	// JAT 08/02/07 - set & get VolumeScale
	static void SetVolumeScale( Asura_Handle iHandle, const float fVolumeScale );
	static float GetVolumeScale( Asura_Handle iHandle );

	//Overrides the position at which the dialogue is being played
	static void UpdatePosition( Asura_Handle iHandle, const Asura_Vector_3& xPos);


#if defined(ASURA_TOOLS)
	//Removes any references to the deleted event from any playing instances.
	static void DialogueEventDeleted(const Asura_Dialogue_Event* pxEvent);
#endif

	static void HandleMessage(const Asura_Message* pxMessage);

	//called by special fx system to do the subtitle rendering.
	static void RenderSubtitles();

	//default implementation of subtitle rendering
	static void RenderSubtitles(const Asura_UnicodeChar* wszText);

	//sets the function to call when rendering subtitles.
	static void SetProjectRenderSubtitles(void (*pfnProjectRenderSubtitles)(const Asura_UnicodeChar* wszText))
	{s_pfnProjectRenderSubtitles = pfnProjectRenderSubtitles;}

	//Sets project callback for triggering animations from dialogue.
	//The function gets called at the start of the line with the ID of an animation to play
	//When the line is finished , the function is called again with a value of zero for the animation.
	static void SetProjectTriggerAnimation(void pfnProjectTriggerAnimation(Asura_Guid uSpeakerGuid,u_int uAnimation))
	{s_pfnProjectTriggerAnimation = pfnProjectTriggerAnimation;}


	//Returns true if there is a subtitle active
	static bool IsSubtitle() {return s_pxCurrentDialogueLine != 0;}
	
	//Gets the text for the subtitle
	static const Asura_UnicodeChar* GetSubtitleText();
	
	//Gets the ID of the character that is speaking
	static Asura_Hash_ID GetSubtitleCharacterID();

	//Gets the guid of the character that is speaking
	static Asura_Guid GetSubtitleCharacterGuid(){return s_uSpeakerGuid;}

	//Used by dialogue playaback code to set the current subtitles (default behaviour)
	static void SetCurrentDialogueLine(const Asura_Dialogue_Line* pxLine,Asura_Guid uSpeakerGuid,float fDuration);
	static void RemoveCurrentDialogueLine(const Asura_Dialogue_Line* pxLine);

	// AJE - 13Feb2007 - sets the functions to call post-setting / post-removing of the current dialogue line
	static void SetProjectPostSetCurrentDialogueLine(void (*pfnProjectPostSetCurrentDialogueLine)(const Asura_Dialogue_Line* pxLine, Asura_Guid uSpeakerGuid))
	{
		s_pfnProjectPostSetCurrentDialogueLine = pfnProjectPostSetCurrentDialogueLine;
	}

	static void SetProjectPostRemoveCurrentDialogueLine(void (*pfnProjectPostRemoveCurrentDialogueLine)(const Asura_Dialogue_Line* pxLine))
	{
		s_pfnProjectPostRemoveCurrentDialogueLine = pfnProjectPostRemoveCurrentDialogueLine;
	}

	static void SetProjectPlayEvent(bool (*pfnProjectPlayEvent)(const Asura_Dialogue_Event *pxEvent, const Asura_Dialogue_CharacterMappingList* pxMappings, Asura_Guid uSpeakerGuid))
	{
		s_pfnProjectPlayEvent = pfnProjectPlayEvent;
	}


	static void SetProjectVoiceCreateEvent(Asura_Handle (*pfnProjectVoiceCreateEvent)(const Asura_Dialogue_Line& xLine, Asura_Guid xSpeaker, float fVolumeScale))
	{
		s_pfnProjectVoiceCreateEvent = pfnProjectVoiceCreateEvent;
	}
	static Asura_Handle (*GetProjectVoiceCreateEvent(void)) (const Asura_Dialogue_Line& xLine, Asura_Guid xSpeaker, float fVolumeScale)
	{
		return s_pfnProjectVoiceCreateEvent;
	}


	//cancels the current subtitle
	static void CancelSubtitle() {s_pxCurrentDialogueLine = NULL;}

	//called by the dialogue playback system to trigger animations when dialogue starts on a character
	static void TriggerAnimation(Asura_Guid uSpeakerGuid,u_int uAnimation);

	//used by dialogue playback to queue up any static messages so they don't get triggered in the middle of an update
	static void QueueStaticMessage(const Asura_Dialogue_Event* pxEvent,Asura_Hash_ID uMessageID, Asura_Guid uSpeakerGuid, const Asura_Dialogue_PlaybackObject* pxPlaybackObject);

	static bool GetShowSubtitles(void)					{	return s_bShowSubtitles;		}
	static void SetShowSubtitles(const bool bOnOff)		{	s_bShowSubtitles = bOnOff;		}

private:
	static void PlayEvent( const Asura_Dialogue_Event* pxEvent, 
		const Asura_Dialogue_CharacterMappingList* pxMappings, Asura_Guid uSpeakerGuid, const Asura_Vector_3* pxPosition,
		Asura_Handle* piHandleOut, unsigned short usMessageBlock, Asura_Guid uInstanceGuid, Asura_Hash_ID uInstanceHandlerID );

	// private functions
	static void UpdatePlaybackObject(Asura_Handle iHandle);
	static void StopAllWithNoExternalHandleCallback(Asura_Handle iHandle);



	static void StopAllCallback(Asura_Handle iHandle);
	static void SkipLineCallback(Asura_Handle iHandle);


#if defined(ASURA_TOOLS)
	static void DialogueEventDeletedCallback(Asura_Handle iHandle);
#endif

	static void TriggerQueuedMessages();

#if defined( ASURA_USE_DEBUGOVERLAY )
	static void DoDebugOverlayListSounds( void* const pxIgnore );
#endif

	static void SendNotifyFinishedMessageIfRequired(Asura_Guid uGuid, int iNotifyFinishedMessageData);

	// private data
    static Asura_Dialogue_PlaybackObject* s_pxPlaybackObjectList;
    static Asura_Tracking_Handle_List* s_pxPlaybackObjectHandleList;

	static const Asura_Dialogue_Line* s_pxCurrentDialogueLine;
	static Asura_Guid s_uSpeakerGuid;
	static float s_fSubtitleDuration;
	static float s_fSubtitleTimer;
	static int s_iSubtitleLength;
	static Asura_UnicodeChar s_wszSubtitleLine[ASURA_DIALOGUE_MAX_SUBTITLE_LENGTH];
	static bool s_bSplitSubtitle;
	static bool s_bShowSubtitles;
	
	static Asura_Dialogue_StaticMessage_QueueEntry s_axStaticMessageQueue[ASURA_DIALOGUE_STATIC_MESSAGE_QUEUE_SIZE];
	static u_int s_uStaticMessageQueue_FirstEntry;
	static u_int s_uStaticMessageQueue_NumEntries;

	static void (*s_pfnProjectRenderSubtitles)(const Asura_UnicodeChar* wszText);
	static void (*s_pfnProjectTriggerAnimation)(Asura_Guid uSpeaker,u_int uAnimation);

	// AJE - 13Feb2007 - hooks for project specific function 'overrides'
	static void (*s_pfnProjectPostSetCurrentDialogueLine)(const Asura_Dialogue_Line* pxLine, Asura_Guid uSpeakerGuid);
	static void (*s_pfnProjectPostRemoveCurrentDialogueLine)(const Asura_Dialogue_Line* pxLine);
	static bool (*s_pfnProjectPlayEvent)(const Asura_Dialogue_Event *pxEvent, const Asura_Dialogue_CharacterMappingList* pxMappings, Asura_Guid uSpeakerGuid);
	static Asura_Handle (*s_pfnProjectVoiceCreateEvent)(const Asura_Dialogue_Line& xLine, Asura_Guid xSpeaker, float fVolumeScale);
};

class Asura_Dialogue_StaticMessage_QueueEntry
{
public :
	const Asura_Dialogue_Event* m_pxEvent;
	Asura_Hash_ID m_uMessageID;
	Asura_Guid m_uSpeakerGuid;
	u_short m_usMessageBlockFromInstance;
	Asura_Hash_ID m_uStaticMessageHandler;
};


//static message for playing a single dialogue line
class Asura_Message_Dialogue_PlayLine
{
public :
	Asura_Hash_ID m_uLineID;

	//ID of dialogue storage handler to look in for the dialogue line.
	//This is only used in streaming builds , but in the interests of not having the messages invalidated
	//as a result of the defines changing , I'm going to leave it in for all builds
	//RAR 28/11/2006
	Asura_Hash_ID m_uHandlerID;
};

//Message sent back to a dialogue controller/trigger when the dialogue has finished
class Asura_Message_Dialogue_NotifyFinished
{
public :
	int m_iMessageData;
};

#endif // ASURA_USE_DIALOGUE_SYSTEM

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

#endif // #ifndef ___ASURA_DIALOGUE_SYSTEM_H___
