#pragma once

// Base types
#include "../Core/Type/RuType_Object.h"
#include "../Core/Math/RuMathVector.h"
#include "../Core/Thread/RuThread_CriticalSection.h"

// Miles Sound System
#include "../../Tools/MilesWin/include/mss.h"

// Import Miles Sound System library
#pragma comment(lib, "mss32.lib")

#pragma managed(push, off)

// ************************************************************************************************************************************************************

enum RuAudioLoopMode
{
	ruAUDIOLOOPMODE_NONE					= 0,
	ruAUDIOLOOPMODE_LOOP_250MS				= 1,									//!< Loop cross-fade 250 MS
	ruAUDIOLOOPMODE_LOOP_500MS				= 2,									//!< Loop cross-fade 250 MS
	ruAUDIOLOOPMODE_LOOP_1000MS				= 3,									//!< Loop cross-fade 1000 MS
	ruAUDIOLOOPMODE_LOOP_RANDOM_1000MS		= 4,									//!< Random loop cross-fade 250 - 1000 MS
	ruAUDIOLOOPMODE_LOOP_RANDOM_2000MS		= 5,									//!< Random loop cross-fade 500 - 2000 MS
	ruAUDIOLOOPMODE_LOOP_RANDOM_PGM_0		= 6,

	ruAUDIOLOOPMODE_FORCE_DWORD				= 0x7FFFFFFF
};

// ************************************************************************************************************************************************************

enum RuSoundType
{
	ruSOUNDTYPE_SFX							= 0,									//!< Sound FX
	ruSOUNDTYPE_AMBIENCE					= 1,									//!< Ambience
	ruSOUNDTYPE_MUSIC						= 2,									//!< Music
	ruSOUNDTYPE_INTERFACE					= 3										//!< Interface
};

// ************************************************************************************************************************************************************

class RuSymphonyEvent_PlaybackFinished_Args : public RuEventArgs
{
public:
	PTRVALUE									m_soundHandle;						//!< Sound handle which finished playback

	RuSymphonyEvent_PlaybackFinished_Args(PTRVALUE soundHandle)
	:	m_soundHandle(soundHandle)
	{
	}
};

// ************************************************************************************************************************************************************

class CRuSymphony : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	struct SoundDescriptor
	{
		char*									m_resourceName;						//!< Resource name for the sound
		INT32									m_soundDataSize;					//!< Size of the sound data
		BYTE*									m_soundData;						//!< Raw sound data
		REAL									m_timeSinceLastUse;					//!< Time since last use

												SoundDescriptor();
												~SoundDescriptor();
	};

	struct SampleDescriptor
	{
		HSAMPLE									m_sampleHandle;						//!< Handle to the sample
		BYTE*									m_sampleData;						//!< Pointer to the sample data
		INT32									m_sampleDataSize;					//!< Sample data size
		BOOL									m_fadingOut;
		RuAudioLoopMode							m_loop;								//!< Sample loop mode
		SampleDescriptor*						m_slaveSampleDesc;

		SoundDescriptor*						m_soundDescriptor;

		// Sound type-specific state
		INT32									m_soundType;						//!< Sound type of the currently playing sample
		REAL									m_volumeModifier;

		// 3D sound state
		BOOL									m_enable3DPositioning;				//!< Is 3D positioning enabled?
		BOOL									m_pinToListener;
		CRuVector3								m_3dPosition;						//!< Position of the 3D sound
	};

	// Events
	CRuEvent									m_event_PlaybackFinished;			//!< Sample playback finished

	// MSS driver
	HDIGDRIVER									m_digDriver;						//!< Digital driver handle

	// Speaker configuration
	INT32										m_numPhysicalChannels;
	INT32										m_numLogicalChannels;
	REAL										m_falloffPower;
	MSS_MC_SPEC									m_channelSpec;

	// Filters
	HPROVIDER									m_volumeRampFilter;					//!< Volume ramp filter

	// Sample descriptors
	INT32										m_numSampleHandles;					//!< Total number of sample handles
	SampleDescriptor*							m_sampleDescriptors;				//!< Sample descriptors

	// Settings
	INT32										m_soundDescriptorCacheSize;			//!< Maximum cache size allowed for the sound descriptor cache

	REAL										m_defaults_3D_MinDist;
	REAL										m_defaults_3D_MaxDist;
	REAL										m_defaults_3D_Rolloff;				//!< Default rolloff factor

	CRuVector3									m_listenerPosition;					//!< Position of the listener
	CRuVector3									m_listenerForward;					//!< Forward direction of the listener
	CRuVector3									m_listenerUp;						//!< Upward direction of the listener

	// Sound dictionary
	CRuTernaryStringTree<SoundDescriptor *>		m_soundDescriptors;
	REAL										m_gcElapsedTime;
	CRuArrayList<SoundDescriptor *>				m_gcSoundDescriptors;

public:
												CRuSymphony();
	virtual										~CRuSymphony();

	// Events
	CRuEvent&									Event_PlaybackFinished();

	// Startup & Shutdown
	BOOL										Startup();
	BOOL										Shutdown();

	// Listener positioning and falloff
	const CRuVector3&							GetListenerPosition();
	BOOL										SetListenerPosition(const CRuVector3 &listenerPosition);
	BOOL										SetListenerOrientation(const CRuVector3 &listenerForward, const CRuVector3 &listenerUp);
	BOOL										SetDefault3DSoundDistances(REAL minDist, REAL maxDist);
	BOOL										SetDefault3DSoundRolloff(REAL rolloff);
	REAL										GetDefault3DMaxDist();

	// Playback
	PTRVALUE									PlaySound(const char *soundPath, INT32 soundType, REAL volumeLevel, BOOL loop);
	PTRVALUE									Play3DSound(const char *soundPath, INT32 soundType, REAL volumeLevel, BOOL loop, const CRuVector3 &position, BOOL pinToListener);

	CRuVector3									Get3DSoundPosition(PTRVALUE soundHandle);
	BOOL										Set3DSoundPosition(PTRVALUE soundHandle, const CRuVector3 &position);

	// Playback (Deprecated)
	PTRVALUE									PlaySoundByPath(const char *soundPath, BOOL loop, REAL volumeLevel = 0.881f, INT32 soundType = ruSOUNDTYPE_SFX);
	PTRVALUE									Play3DSoundByPath(const char *soundPath, REAL x, REAL y, REAL z, RuAudioLoopMode loop, BOOL pinToListener = FALSE, INT32 soundType = ruSOUNDTYPE_SFX, REAL volumeLevel = 0.881f);

	// Playback alteration
	BOOL										SetMasterSoundVolume(REAL volumeLevel);
	BOOL										AdjustSoundVolumeByType(INT32 soundType, REAL volumeLevel);

	// Termination
	BOOL										FadeOutSound(PTRVALUE soundHandle, REAL fadeOutTimeMS = 250.0f);
	BOOL										StopSound(PTRVALUE soundHandle);

	// Debug
	void										EnumeratePlayingSounds();

	// Update
	BOOL										Update(REAL elapsedTime);

	// Cache management
	BOOL										ClearUnusedSoundDescriptors();

protected:
	// Resource retrieval
	SampleDescriptor*							GetAvailableSampleDescriptor();
	SoundDescriptor*							GetSoundDescriptor(const char *soundPath);
	BOOL										IsSoundDescriptorInUse(SoundDescriptor *soundDesc);

	BOOL										VolumeRamp(HSAMPLE sampleHandle, REAL level0, REAL level1, REAL duration);
	BOOL										SoundVolumeRampIsZero(PTRVALUE soundHandle);

	BOOL										Update3DSoundChannelLevels(PTRVALUE soundHandle);

	BOOL										ClearUnusedSoundDescriptorsCallback(const void *key, void *data);

	BOOL										GCSoundDescriptors();
	BOOL										GCSoundDescriptorsCallback(const void *key, void *data);

	BOOL										ClearSoundDescriptors();
	BOOL										ClearSoundDescriptorsCallback(const void *key, void *data);
};

// ************************************************************************************************************************************************************

extern CRuSymphony*								g_ruSymphony;

// ************************************************************************************************************************************************************

#pragma managed(pop)
