//
// Asura_Sound_DialogueController.h
//
// Created by Richard Rice - 11/11/2004
// 
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_SOUND_DIALOGUECONTROLLER_H__
#define __ASURA_SOUND_DIALOGUECONTROLLER_H__

//////////////////////////////////////////////////////////////////////////////
//
// includes
//
//////////////////////////////////////////////////////////////////////////////

// asura
#include "Core/Asura.h"
#include "Core/Asura_Hash.h"
#include "Core/Asura_Maths.h"
#include "Core/Asura_Handle.h"
#include "Sound/Asura_ProjectSoundIDs.h"

//////////////////////////////////////////////////////////////////////////////
//
// support classes for the dialogue controller
//
//////////////////////////////////////////////////////////////////////////////

// NJD - 10/05/2005 - I've added these for now to allow projects to reduce the
//                    memory usage if necessary. I've got a bunch of code ready
//                    to improve the design of this sub-system and sort out the
//                    bonkers memory usage, but I'm not allowed to put it in yet
//                    for scheduling reasons. This should be getting done soon.
#ifndef ASURA_DIALOGUE_CONTROLLER_MAX_STREAMS
    #define ASURA_DIALOGUE_CONTROLLER_MAX_STREAMS           (16)
#endif
#ifndef ASURA_DIALOGUE_CONTROLLER_MEMORY_TYPE_COUNT
    #define ASURA_DIALOGUE_CONTROLLER_MEMORY_TYPE_COUNT     (32)
#endif

//Define that can be overriden to stop tannoy pitch being affected by time scale. RAR 07/07/2006
#ifndef ASURA_TANNOY_USEGAMETIMESCALE
	#define ASURA_TANNOY_USEGAMETIMESCALE (1)
#endif

enum DialogueSystemConstants
{
    // number of dialogue memories per memory type
    DSC_PER_MEMORY_TYPE_MEMORY_SIZE = ASURA_DIALOGUE_CONTROLLER_MAX_STREAMS,
    // this is an estimate of the number of dialogue memory types (derivations of DialogueMemory)
    // that we are going to have. if any project has more then this value should be increased.
    DSC_MEMORY_TYPE_COUNT           = ASURA_DIALOGUE_CONTROLLER_MEMORY_TYPE_COUNT,
    // total number of dialogue memories allowed
    DSC_MEMORY_SIZE                 = DSC_MEMORY_TYPE_COUNT * DSC_PER_MEMORY_TYPE_MEMORY_SIZE
};

class DialogueMemory
{
protected:
    enum SoundMemoryState
    {
        SMS_STATE_UNDEFINED,
        SMS_SOUND_INITIALISING,         // sound is having its data setup by us
        SMS_SOUND_PLAYING,              // sound is playing
        SMS_SOUND_FINISHED,             // sound is finished but dialogue handle is still valid
        SMS_SOUND_READY_FOR_DELETION    // sound is finished and dialogue handle is invalid
    };

public:
    DialogueMemory()
        : m_fWorthiness( 0.0f ), m_iSoundHandle( ASURA_INVALID_HANDLE ), m_uProjectFlags( 0 ), m_eState( SMS_STATE_UNDEFINED )
    {}
    virtual ~DialogueMemory() {}
    virtual void Clear() { m_fWorthiness = 0.0f; m_eState = SMS_STATE_UNDEFINED; }

    virtual Asura_Sound_ID  GetSoundToPlay() const                                 = 0;
    virtual Asura_Sound_ID  GetSoundToLoad() const                                 = 0;
    virtual void            ShuffleSoundIndexData() const                          = 0;
    virtual void            UpdateSoundWorthiness()                                = 0;
    virtual void            UpdatePosition( const Asura_Vector_3& xPosition )      = 0;
    virtual unsigned int    GetTextIdToShow( bool& bTextDataSet ) const            = 0;
    virtual Asura_Hash_ID   GetTextPageHashId() const                              = 0;
    virtual Asura_Handle    CreateSound( const Asura_Sound_ID eSoundToPlay ) const = 0;

public:
    void DestroyAndReplaceDialogue();

public:
    bool IsFree()             const { return SMS_STATE_UNDEFINED == m_eState; }
    bool IsPlaying()          const { return SMS_SOUND_PLAYING == m_eState; }
    bool IsFinished()         const { return SMS_SOUND_FINISHED == m_eState; }
    bool IsReadyForDeletion() const { return SMS_SOUND_READY_FOR_DELETION == m_eState; }

    void SoundInitialising()     { m_eState = SMS_SOUND_INITIALISING; }
    void SoundPlaying()          { m_eState = SMS_SOUND_PLAYING; }
    void SoundFinished()         { Clear(); m_eState = SMS_SOUND_FINISHED; }
    void SoundReadyForDeletion() { Clear(); m_eState = SMS_SOUND_READY_FOR_DELETION; }

    void ReturnThis() { Clear(); }

public:
    float        m_fWorthiness;
    Asura_Handle m_iSoundHandle;
    u_int        m_uProjectFlags;

protected:
    SoundMemoryState m_eState;
};

class TannoyDialogueMemory : public DialogueMemory
{
public:
    TannoyDialogueMemory();
    virtual ~TannoyDialogueMemory();
    virtual void Clear();

    virtual Asura_Sound_ID  GetSoundToPlay() const;
    virtual Asura_Sound_ID  GetSoundToLoad() const;
    virtual void            ShuffleSoundIndexData() const {}
    virtual void            UpdateSoundWorthiness() {}
    virtual void            UpdatePosition( const Asura_Vector_3& ) {}
    virtual unsigned int    GetTextIdToShow( bool& bTextDataSet ) const;
    virtual Asura_Hash_ID   GetTextPageHashId() const;
    virtual Asura_Handle    CreateSound( const Asura_Sound_ID eSoundToPlay ) const;

    static TannoyDialogueMemory* GetFreePointer();
    static bool                  AnyPlaying();
    static bool                  AnyPlayingWithFlag( const u_int uFlags );
    // fills in the flags of any tannoy playing with the Asura_Sound_System sound handle and returns
    // whether it found any
    static bool                  AnyPlayingWithHandle( u_int& uFlags, const Asura_Handle iAsuraSoundHandle );

    // for preventing player sounds from being played when dead, etc    
    bool Project_ShouldPlayThisSound();

public:
    Asura_Sound_ID m_eSoundID;
    bool           m_bPositionValid;
    Asura_Vector_3 m_xPosition;
    float          m_fInnerRadius;
    float          m_fOuterRadius;

protected:
    static TannoyDialogueMemory s_axDialogueMemory[ DSC_PER_MEMORY_TYPE_MEMORY_SIZE ];
};

//////////////////////////////////////////////////////////////////////////////
//
// Asura_Sound_DialogueController
// 
// All dialogue passes through here. Culls requests based on priority,
// distance from cameras, and platform dependent sound count limits.
// Handles the sequencing and loading/unloading of dialogue with variations.
//
//////////////////////////////////////////////////////////////////////////////

class Asura_Sound_DialogueController
{
public:
    static void Initialise();
    static void Shutdown();
    static void Update();

    static Asura_Handle PlayTannoyDialogue( const Asura_Sound_ID& eSoundID,
                                            const float&          fInnerRadius,
                                            const float&          fOuterRadius,
                                            const float&          fVolume,
                                            const u_int&          uProjectFlags,
                                            const Asura_Vector_3* pxPosition = NULL );
    static void         UpdateDialoguePosition( const Asura_Handle&   iDialogueHandle,
                                                const Asura_Vector_3& xPosition );
    static void         UpdateDialogueHandle( Asura_Handle& iDialogueHandle );
    static void         DestroyDialogue( Asura_Handle& iDialogueHandle );
    static void         DestroyAllDialogue();
    // this to get a list of all sound handles currently in use
    static Asura_Handle GetNextAsuraSoundHandle( const bool bReset );

	static void SetDialoguePitchUsesTimeScale(bool bUseTimeScale, float fTimeScaleToUse = 1.0f);

protected:
    static bool         PlatformSoundLimitReached();
    static bool         ExpelWorseSound( const float fWorthiness );
    static Asura_Handle PlaySound( DialogueMemory* pxDialogueMemory, float fVolume = 1.0f );
    static Asura_Handle AddSoundToMemory( const Asura_Handle& iSoundHandle, DialogueMemory* pxDialogueMemory );
    static void         LoadTannoySounds();

    static bool RoomInMemory() { return ( s_ucMemorySize < DSC_MEMORY_SIZE ); }
    static bool GetIndexFromHandle( const Asura_Handle& iDialogueHandle, int& iIndex ) {
        iIndex = static_cast< int >( iDialogueHandle );
        if ( 0 <= iIndex && iIndex < DSC_MEMORY_SIZE ) {
            return true;
        }
        else {
            Asura_Assert( 0, "Asura_Sound_DialogueController::GetIndexFromHandle - invalid dialogue handle" );
            return false;
        }
    }

    // project function for displaying any text associated with a sound that is being played
    static void Project_SendShowTextMessage( const Asura_Hash_ID uTextPageHashId, const unsigned int uTextId, const u_int uProjectFlags );

protected:
    static int             s_iPlayingSounds;
    static unsigned char   s_ucMemorySize;
    static DialogueMemory* s_apxDialogueMemory[ DSC_MEMORY_SIZE ];

    // stuff for iterating through the dialogue handles using the public interface
    static unsigned char   s_ucMemoryIndex;

	// Values for changing the use of time scale to affect pitch
	static bool s_bPitchUsesTimeScale;
	static float s_fTimeScaleToUse;
};

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////

#endif //__ASURA_SOUND_DIALOGUECONTROLLER_H__
