//////////////////////////////////////////////////////////////////////////////
//
// Asura_Audio_System.h
//
// Copyright (c) 2008 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

#if defined ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#if !defined __ASURA_AUDIO_SYSTEM_H__
#define __ASURA_AUDIO_SYSTEM_H__

#if defined ASURA_USE_AUDIO_SYSTEM

/******************************************************************************/
/* Included Files                                                             */
/******************************************************************************/

// Asura Platform-independent
#include "Camera/Asura_Camera.h"
#include "Core/Asura_FixedArray.h"
#include "Core/Asura_String.h"
#include "ChunkSystem/Asura_Chunk_Loaders.h" // Needed for ASURA_STANDARD_FILE_EXTENSION

/******************************************************************************/
/* Macros                                                                     */
/******************************************************************************/

#if defined _DEBUG || defined ASURA_TOOLS
	#define ASURA_AUDIO_STORE_FILENAMES
#endif

/******************************************************************************/
/* Constants                                                                  */
/******************************************************************************/

const char* const szASURA_AUDIO_STREAMING_FILENAME =
	"Sounds\\StreamingSounds." ASURA_STANDARD_FILE_EXTENSION;

#define ASURA_MAX_NO_OF_LISTENERS 4

/******************************************************************************/
/* Classes                                                                    */
/******************************************************************************/

class Asura_Audio_DSPChain;
class Asura_Audio_Reverb;
class Asura_Audio_Voice;
class Asura_Chunk_Stream;
class Asura_LOS_OutputData;
class Asura_Vector_3;
class Asura_Vector_2;

template< typename T > class Asura_ResourceSet;
template< typename T > class Asura_TemplateHashTree;

/******************************************************************************/
/* Typedefs                                                                   */
/******************************************************************************/

typedef float(*Asura_Audio_OcclusionFactor_Callback) (const Asura_LOS_OutputData* pxLoSData);

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Audio_Reverb
//
// Holds settings for a reverb preset. Now pulled out of generic DSP chains
//
////////////////////////////////////////////////////////////////////////////////

class Asura_Audio_Reverb
{
public:

	Asura_Audio_Reverb();
	Asura_Audio_Reverb( const Asura_Audio_Reverb* pxReverb1, const Asura_Audio_Reverb* pxReverb2, const float fLerpFactor );

	bool operator==(const Asura_Audio_Reverb& xReverb);

	#ifdef ASURA_TOOLS
		void WriteToChunkStream( Asura_Chunk_Stream& xStream ) const;
	#endif

	#if defined ASURA_AUDIO_STORE_FILENAMES
		Asura_String m_xName;
	#endif

	int   m_iRoom;
	int   m_iRoomHF;
	float m_fRoomRolloffFactor;
	float m_fDecayTime;
	float m_fDecayHFRatio;
	int   m_iReflections;
	float m_fReflectionsDelay;
	int   m_iReverb;
	float m_fReverbDelay;
	float m_fDiffusion;
	float m_fDensity;
	float m_fHFReference;
};

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Audio_StreamInfo
//
// Holds information about where a streaming sound is located with the
// streaming sounds package file
//
////////////////////////////////////////////////////////////////////////////////

class Asura_Audio_StreamInfo
{
public:

	Asura_Audio_StreamInfo();
	~Asura_Audio_StreamInfo();

	#if defined ASURA_AUDIO_STORE_FILENAMES
		const char* m_szFilename;
	#endif

	// Which export file contains this. Filenames will be duplicated here; if memory becomes an
	// issue, could point all these at one copy of the filename
	const char* m_szPackage;

	// Byte offset within StreamingSounds.asr[BE] where this file starts
	u_int m_uLocation;

	bool m_bLooping;

	#if defined ASURA_TOOLS
		// Need to set this in Tools to prevent dialogue getting exported in the main package
		bool m_bDialogue;
	#endif

private:

	Asura_Audio_StreamInfo( const Asura_Audio_StreamInfo& );
	Asura_Audio_StreamInfo& operator=( const Asura_Audio_StreamInfo& );
};

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Audio_System
//
// Base level of the audio system
//
////////////////////////////////////////////////////////////////////////////////

class Asura_Audio_System
{
	friend class Asura_Audio_Voice;
	friend class Asura_Chunk_ReverbPresets;

public:

	enum Speakers
	{
		ASURA_SPEAKER_FRONT_LEFT		= 0,
		ASURA_SPEAKER_FRONT_RIGHT		,
		ASURA_SPEAKER_FRONT_CENTRE		,
		
		ASURA_SPEAKER_LOW_FREQ			,
		
		ASURA_SPEAKER_REAR_LEFT			,
		ASURA_SPEAKER_REAR_RIGHT		,
		ASURA_SPEAKER_REAR_CENTRE		,
		
		ASURA_SPEAKER_SIDE_LEFT			,
		ASURA_SPEAKER_SIDE_RIGHT		,

		ASURA_SPEAKER_FRONT_CENTRE_LEFT	,
		ASURA_SPEAKER_FRONT_CENTRE_RIGHT,

		//Add more speakers here if desired (change SetSpeakerActive as well)
		
		ASURA_NUM_SPEAKERS
	};

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

	static float CalcVolFalloff( const float fInnerRadius, const float fOuterRadius,
		const float fDistance );

	static float CalcVolFalloff( const Asura_Vector_3& xInnerRange,
		const Asura_Vector_3& xOuterRange, const Asura_Vector_3& xPos );

	static bool LoadWave( Asura_Chunk_Stream& xStream, const u_int uFileSize, const u_int uSubType,
		const u_int uFlags, const char* const szFilename );

	#if defined ASURA_TOOLS
		static bool LoadWave( const char* const szFilename, const bool bLooping );
		static void ForceWaveToBeLoaded( const Asura_Hash_ID uWave );
		static void ReloadWave( const Asura_Hash_ID uWave, const bool bForceReloadNow );
		static void SetWaveLooping( const Asura_Hash_ID uWave, const bool bLoop );
	#endif

	#if defined ASURA_AUDIO_STORE_FILENAMES
		static const char* GetWaveFilename( const Asura_Hash_ID uWave );
	#endif

	static void						AddDSPChain			( const Asura_Hash_ID uHash, const Asura_Audio_DSPChain* pxChain );
	static void						RemoveDSPChain		( const Asura_Hash_ID uHash );
	static int						GetNumDSPChains		( );
	static Asura_Audio_DSPChain*	GetDSPChainByHash	( const Asura_Hash_ID uHash );
	static void						SetMasterDSP		( const Asura_Hash_ID uDSPChain, const float fFadeTime );
	#ifdef ASURA_TOOLS
	static Asura_Audio_DSPChain*	GetDSPChainByIndex	( const int iIndex );
	#endif

	static Asura_Handle CreateVoice(	const Asura_Hash_ID uWave,
										const bool bFilter, 
										const bool bMusic,
										const Asura_Hash_ID uDSPChain = ASURA_HASH_ID_UNSET );

	static Asura_Handle CreateStreamingVoice(	const Asura_Hash_ID uStreamHash, 
												const bool bFilter,
												const bool bMusic,
												const float fTimeOffset = 0.0f,
												const Asura_Hash_ID uDSPChain = ASURA_HASH_ID_UNSET );

	static Asura_Handle CreateSubmixVoice(	const bool bFilter, 
											const bool bMusic, 
											const Asura_Hash_ID uDSPChain = ASURA_HASH_ID_UNSET );

	static void DestroyVoice( Asura_Handle& iVoiceHandle );
	static Asura_Audio_Voice* GetVoice( const Asura_Handle iVoiceHandle );

	static bool				IsSpeakerActive( Speakers eSpeaker );
	static void				SetSpeakerActive( Speakers eSpeaker );
	static int				GetNumActiveSpeakers( ) { return s_iNumActiveSpeakers; }
	static Asura_Vector_2	GetSpeakerPosition( Speakers eSpeaker );
	static float			GetMaxAngleBetweenSpeakers()				{ return s_fMaxAngleBetweenSpeakers; }

	static Asura_Audio_StreamInfo* AddStreamInfo( const char* const szFilename,
		const char* const szPackage, const u_int uLocation, const bool bLooping );

	static void DeleteStreamInfo( const Asura_Hash_ID uHash );
	static Asura_TemplateHashTree< Asura_Audio_StreamInfo >& GetStreamInfoTree();
	static Asura_Audio_StreamInfo* GetStreamInfo( const Asura_Hash_ID uHash );

	static float		GetDistanceFilterMinRange();
	static float		GetDistanceFilterMaxRange();

	static void	SetEntityOcclusionFactorCallback		(Asura_Audio_OcclusionFactor_Callback pfnCallback)	{ s_pfnGetEntityOcclusionFactor			= pfnCallback; }
	static void	SetEnvironmentOcclusionFactorCallback	(Asura_Audio_OcclusionFactor_Callback pfnCallback)	{ s_pfnGetEnvironmentOcclusionFactor	= pfnCallback; }

	static Asura_Audio_OcclusionFactor_Callback	GetEntityOcclusionFactorCallback		()	{ return s_pfnGetEntityOcclusionFactor;			}
	static Asura_Audio_OcclusionFactor_Callback	GetEnvironmentOcclusionFactorCallback	()	{ return s_pfnGetEnvironmentOcclusionFactor;	}

	static void		SetOcclusionCPUTimePerFrame	( const float fSeconds )	{ s_fOcclusionCPUTimePerFrame = fSeconds;	}
	static float	GetOcclusionCPUTimePerFrame	( )							{ return s_fOcclusionCPUTimePerFrame;		}

	static void			SetGUIDToIgnoreForOcclusion	( const Asura_Guid uIgnore, const u_int uListenerIdx = 0 );
	static Asura_Guid	GetGUIDToIgnoreForOcclusion	( const u_int uListenerIdx = 0 );

	static void	SetShouldUpdateOcclusion	( const bool bUpdate )	{ s_bShouldUpdateOcclusion = bUpdate;	}
	static bool	IsOcclusionUpdateEnabled	( )						{ return s_bShouldUpdateOcclusion;		}

	static Asura_Audio_Reverb* AddReverb( const char* szName, bool& bAlreadyExists );
	static Asura_Audio_Reverb* GetReverb( const Asura_Hash_ID uHash,
		const bool bAssertOnFail = true );

	static void SetCurrentReverb( const Asura_Hash_ID uHash, const float fAmount );
	static void GetCurrentReverb( Asura_Audio_Reverb& xReverb, float& fAmount );

	#if defined ASURA_TOOLS
		static void DeleteReverb( const Asura_Hash_ID uHash );
		static u_int GetNumReverbs();
		static Asura_Audio_Reverb* GetReverbByIndex( const u_int uIndex );
		static void GetReverbNames( const char** const pszNames );
	#endif

	static void SetNumberOfListeners( const u_int uNum );
	static void SetListenerActive( const bool bActive, const u_int uListenerIdx = 0 );
	static void SetListenerUsesCurrentCamera( const u_int uListenerIdx = 0 );
	static void SetListenerUsesCamera( const int iCamera, const u_int uListenerIdx = 0 );
	static void SetListenerPosition( const Asura_Vector_3 xPos, const Asura_Matrix_3x3 xOrient, const u_int uListenerIdx = 0 );
	static u_int GetNumberOfListeners() { return s_uNumListeners; }
	static void GetListenerPosition( Asura_Vector_3* pxPos, Asura_Matrix_3x3* pxOrient, const u_int uListenerIdx = 0 );
	static bool IsListenerActive( const u_int uListenerIdx = 0 );

protected:

	static void Platform_Initialise();
	static void Platform_Reset();
	static void Platform_Shutdown();
	static void Platform_Update();

	static bool Platform_LoadWave( Asura_Chunk_Stream& xStream, const u_int uFileSize,
		const u_int uSubType, const u_int uFlags, const char* const szFilename );

	#if defined ASURA_TOOLS
		static bool Platform_LoadWave( const char* const szFilename, const bool bLooping );
		static void Platform_ForceWaveToBeLoaded( const Asura_Hash_ID uWave );
		static void Platform_ReloadWave( const Asura_Hash_ID uWave, const bool bForceReloadNow );
		static void Platform_SetWaveLooping( const Asura_Hash_ID uWave, const bool bLoop );
	#endif

	#if defined ASURA_AUDIO_STORE_FILENAMES
		static const char* Platform_GetWaveFilename( const Asura_Hash_ID uWave );
	#endif

	static Asura_Handle Platform_CreateVoice(	const Asura_Hash_ID uWave,
												const bool bFilter, 
												const bool bMusic,
												const Asura_Hash_ID uDSPChain = ASURA_HASH_ID_UNSET );

	static Asura_Handle Platform_CreateStreamingVoice(	const Asura_Hash_ID uStreamHash,
														const bool bFilter,
														const bool bMusic, 
														const float fTimeOffset,
														const Asura_Hash_ID uDSPChain = ASURA_HASH_ID_UNSET );

	static Asura_Handle Platform_CreateSubmixVoice(	const bool bFilter, 
													const bool bMusic, 
													const Asura_Hash_ID uDSPChain = ASURA_HASH_ID_UNSET );

	static void Platform_DestroyVoice( const Asura_Handle iVoiceHandle );
	static Asura_Audio_Voice* Platform_GetVoice( const Asura_Handle iVoiceHandle );

	static void Platform_CreateDSPChain( const Asura_Hash_ID uHash,	const Asura_Audio_DSPChain& xChain );
	static void Platform_SetMasterDSP( const Asura_Hash_ID uDSPChain, const float fFadeTime );

private:

	static u_int s_uActiveSpeakers;
	static int s_iNumActiveSpeakers;
	static float s_fMaxAngleBetweenSpeakers;

	static Asura_TemplateHashTree< Asura_Audio_DSPChain > s_xDSPChains;
	static Asura_TemplateHashTree< Asura_Audio_StreamInfo >	s_xStreamInfoTree;
	static Asura_ResourceSet< Asura_Audio_Reverb > s_xReverbs;

	static Asura_Audio_Reverb s_xCurrReverb;
	static float s_fCurrReverbAmount;
	static Asura_Audio_Reverb s_xReverbBuffers[2];
	static float s_fReverbAmountBuffers[2];
	static float s_fReverbTransition;
	static int s_iCurrentReverbBuffer;
	
	static u_int s_uNumListeners;
	static bool s_abListenerActive[ ASURA_MAX_NO_OF_LISTENERS ];
	static bool s_abListenerUsesCurrentCamera[ ASURA_MAX_NO_OF_LISTENERS ];
	static int s_aiListenerCameraIndex[ ASURA_MAX_NO_OF_LISTENERS ];
	static Asura_Vector_3 s_axListenerPosition[ ASURA_MAX_NO_OF_LISTENERS ];
	static Asura_Matrix_3x3 s_axListenerOrientation[ ASURA_MAX_NO_OF_LISTENERS ];

	//occlusions
	static Asura_Audio_OcclusionFactor_Callback s_pfnGetEntityOcclusionFactor;
	static Asura_Audio_OcclusionFactor_Callback s_pfnGetEnvironmentOcclusionFactor;

	static bool s_bShouldUpdateOcclusion;
	static float s_fOcclusionCPUTimePerFrame;

	static Asura_FixedArray<Asura_Guid, ASURA_MAX_NO_OF_LISTENERS> s_auOcclusionGUIDsToIgnore;

};

#endif // defined ASURA_USE_AUDIO_SYSTEM

#endif // !defined __ASURA_AUDIO_SYSTEM_H__

//eof
