//////////////////////////////////////////////////////////////////////////////
//
// Asura_Sound_Event.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
// Description:
//
//     implementation of Asura_Sound_Event classes
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Sound/Asura_Sound_Event.h"

// Asura includes
#include "Core/Asura_Maths.h"
#include "Core/Asura_PrimeSearch.h"
#include "Sound/Asura_Sound_Event_Storage.h"
#include "Sound/Asura_Sound_Event_System.h"
#include "Sound/Asura_Sound_Mixer.h"
#include "Sound/Asura_Sound.h"

//Tools includes
#ifdef ASURA_TOOLS
#include "Misc/Tools_Platform.h"
#endif

// A S U R A _ S O U N D _ E V E N T /////////////////////////////////////////
Asura_Sound_Event::Asura_Sound_Event(const char* szName)
: m_uLastPlayedItem(0)
, m_pxPrimeSearch(NULL)
, m_fMinVol(1.0f)
, m_fMaxVol(1.0f)
, m_fMinPitch(1.0f)
, m_fMaxPitch(1.0f)
, m_fMinDelay(0.0f)
, m_fMaxDelay(0.0f)
, m_fMinDuration(0.0f)
, m_fMaxDuration(0.0f)
, m_fPriority(1.0f)
, m_ePlaybackType(ASURA_PT_CDPLAYERRANDOM)
, m_pxMixingChannel(NULL)
, m_pxVariableParams(NULL)
, m_ucPlayProbability(100)
, m_e3dSoundType(ASURA_SOUND_POINT)
, m_fInnerRadius(5.0f)
, m_fOuterRadius(30.0f)
, m_uMaxInstances(0)
, m_uMinFreeVoices(0)
, m_uInstanceGroupID(0)
, m_fMaxOuterRadius(Asura_Maths::LargeFloat)
#ifdef ASURA_SOUND_INCLUDE_TOOLS_DATA
, m_xEventName(szName)
, m_xEventType("Default")
, m_xEventSubType("Default")
, m_xMixerChannelName("Master")
, m_xFilename("Default")
, m_bGlobalSound(false)
#endif
#ifdef ASURA_TOOLS
, m_bEventMapping(false)
#endif
, m_uMappingCount(0)
, m_uSoundType(0)
, m_uDSPChainHash( ASURA_HASH_ID_UNSET )
{
	m_uFlags = 0;
	m_bUseOcclusion = true;
	m_bUseDistanceFilter = true;
	m_bUseDoppler = true;
	m_bUseEnvironmentalReverb = true;
	m_bUseMasterDSP = true;

	m_uHashID = Asura_GetHashID(szName);
}

Asura_Sound_Event::~Asura_Sound_Event()
{
#if defined(ASURA_USE_MULTIPLE_HANDLERS) || defined(ASURA_TOOLS)
	// Check for any references to this event in other objects.
	Asura_Sound_Event_System::SoundEventDeleted(this);
#endif	// defined(ASURA_USE_MULTIPLE_HANDLERS)

	Asura_Assert(!m_uMappingCount,"Event should have all entries in sound storage system removed before being deleted");

	delete m_pxPrimeSearch;
	delete m_pxVariableParams;
}

// virtual function override
void Asura_Sound_Event::FillInPlayParams( Asura_Sound_PlayParams& xParams ) const
{
	//Asura_Assert(!IsMetaEvent(), "only valid for events with samples");
	xParams.m_fVolume *= Asura_Random::GetFloat( m_fMinVol, m_fMaxVol );
	xParams.m_fPitch *= Asura_Random::GetFloat( m_fMinPitch, m_fMaxPitch );
	xParams.m_fDelay += Asura_Random::GetFloat( m_fMinDelay, m_fMaxDelay );
	if (IsPanOverride())
	{
		if (xParams.m_bForcedPan)
		{
			xParams.m_xSurroundPan += m_xSurroundPan;
		}
		else
		{
			xParams.m_bForcedPan = true;
			xParams.m_xSurroundPan = m_xSurroundPan;
		}
	}
	
	xParams.m_bUseOcclusion			= xParams.m_bUseOcclusion && m_bUseOcclusion;
	xParams.m_bUseDistanceFilter	= xParams.m_bUseDistanceFilter && m_bUseDistanceFilter;
	xParams.m_bUseDoppler			= xParams.m_bUseDoppler && m_bUseDoppler;
	xParams.m_bUseEnvReverb			= xParams.m_bUseEnvReverb && m_bUseEnvironmentalReverb;
	xParams.m_bUseMasterDSP			= xParams.m_bUseMasterDSP && m_bUseMasterDSP;

	// set mixing channel to the first non-default in the event chain.
	if (!xParams.m_pxMixingChannel)
	{
		xParams.m_pxMixingChannel = m_pxMixingChannel;
	}

	xParams.m_uDSPChain = m_uDSPChainHash;
}


const Asura_Sound_Playable* Asura_Sound_Event::GetPlayableSound() const
{
	// not valid for ASURA_PT_SIMULTANEOUS, GetPlayableSound() returns an entry
	// from the list as appropriate and sets m_uLastPlayedItem.

	u_int uNumPlayables = m_xPlayableList.Size();
#ifdef ASURA_SOUND_INCLUDE_TOOLS_DATA
	Asura_Assertf( uNumPlayables, ("Sound event '%s': no items to play",GetName()) );
#else
	Asura_Assertf( uNumPlayables, ("Sound event id: %x: no items to play",GetHashID()) );
#endif 
	if (!uNumPlayables)
	{
		return NULL;
	}


	switch (m_ePlaybackType)
	{
		case ASURA_PT_ALLSEQUENTIAL:
		{
			// one at a time triggering next one when finished
			m_uLastPlayedItem = 0;
			break;
		}
		case ASURA_PT_ONEBYONE:
		{
			// current one in list, stopping and advancing in list when finished 
			m_uLastPlayedItem = (m_uLastPlayedItem + 1) % uNumPlayables;
			break;
		}
		case ASURA_PT_RANDOM:
		{
			// pick at random
			m_uLastPlayedItem = Asura_Random::GetIntU(0, uNumPlayables - 1);
			break;
		}
		case ASURA_PT_CDPLAYERRANDOM:
		{
			// like a CD random function (plays all in random order)
			Asura_Assert(m_pxPrimeSearch, "no object to choose cdplayer-style random item");
			if(uNumPlayables == 1)
			{
				//If there is only one playable , just go for it.
				//(Prevents crash caused by attempting to find a playable different from the previous one).
				//RAR 16/10/2006
				m_uLastPlayedItem = 0;
			}
			else
			{
				if (m_pxPrimeSearch->Done())
				{
					m_pxPrimeSearch->Regenerate();
				}
				u_int uNextItem = m_pxPrimeSearch->GetNext();
				if (m_uLastPlayedItem == uNextItem)
				{
					// this can happen after regenerating.
					uNextItem = m_pxPrimeSearch->GetNext();
				}
				m_uLastPlayedItem = uNextItem;
			}
			break;
		}
		default:
		{
			Asura_Assert( false, "function not valid for this playback type");
			m_uLastPlayedItem = 0;
			break;
		}
	}

	return m_xPlayableList[m_uLastPlayedItem];
}

const Asura_Sound_Playable* Asura_Sound_Event::GetPlayableSound(u_int uIndex) const
{
	u_int uNumPlayables = m_xPlayableList.Size();
	Asura_Assert(uIndex < uNumPlayables, "invalid playable sound index");
	if (uIndex >= uNumPlayables)
	{
		return NULL;
	}
	return m_xPlayableList[uIndex];
}

void Asura_Sound_Event::SetPlaybackType( ASURA_SOUND_PLAYBACK_TYPE eType )
{
	if (m_ePlaybackType == eType)
	{
		// nothing to do
		return;
	}
	if (m_ePlaybackType == ASURA_PT_CDPLAYERRANDOM)
	{
		// free the random search object
		delete m_pxPrimeSearch;
		m_pxPrimeSearch = NULL;
	}
	else if (m_ePlaybackType == ASURA_PT_VARIABLE)
	{
		// free the variable param data
		delete m_pxVariableParams;
		m_pxVariableParams = NULL;
	}

	if (eType == ASURA_PT_CDPLAYERRANDOM)
	{
		// allocate a random search object
		Asura_Assert(!m_pxPrimeSearch, "overwriting random search object");
		if(GetNumPlayables())
		{
			m_pxPrimeSearch = new Asura_PrimeSearch(GetNumPlayables());
		}
	}
	else if(eType == ASURA_PT_VARIABLE)
	{
		// allocate storage for variable params
		Asura_Assert(!m_pxVariableParams,"");
		m_pxVariableParams = new Asura_Sound_VariableParams;
		m_pxVariableParams->Initialise();
	}
	m_ePlaybackType = eType;
}

#ifdef ASURA_TOOLS
// Set() accessors
void Asura_Sound_Event::SetVolume( float fMin, float fMax )
{
	Asura_Assert(fMin <= fMax, "invalid settings");
	Asura_Assert(fMin >= 0.0f && fMin <= fASURA_SOUND_EVENT_VOLUME_MAX, "invalid settings");
	Asura_Assert(fMax >= 0.0f && fMax <= fASURA_SOUND_EVENT_VOLUME_MAX, "invalid settings");
	m_fMinVol = fMin;
	m_fMaxVol = fMax;
}

void Asura_Sound_Event::SetPitch( float fMin, float fMax )
{
	Asura_Assert(fMin <= fMax, "invalid settings");
	Asura_Assert(fMin > 0.0f, "invalid settings");
	Asura_Assert(fMax > 0.0f, "invalid settings");
	m_fMinPitch = fMin;
	m_fMaxPitch = fMax;
}

void Asura_Sound_Event::SetDelay( float fMin, float fMax )
{
	Asura_Assert(fMin <= fMax, "invalid settings");
	Asura_Assert(fMin >= 0.0f, "invalid settings");
	Asura_Assert(fMax >= 0.0f, "invalid settings");
	m_fMinDelay = fMin;
	m_fMaxDelay = fMax;
}

void Asura_Sound_Event::SetDuration( float fMin, float fMax )
{
	Asura_Assert(fMin <= fMax, "invalid settings");
	Asura_Assert(fMin >= 0.0f, "invalid settings");
	Asura_Assert(fMax >= 0.0f, "invalid settings");
	m_fMinDuration = fMin;
	m_fMaxDuration = fMax;
}


void Asura_Sound_Event::SetSurroundPan( const Asura_SurroundPan& xPan )
{
	m_xSurroundPan = xPan;
}

void Asura_Sound_Event::SetPriority( float fPriority )
{
	Asura_Assert(fPriority >= fASURA_SOUND_EVENT_PRIORITY_MIN, "invalid settings");
	Asura_Assert(fPriority <= fASURA_SOUND_EVENT_PRIORITY_MAX, "invalid settings");
	m_fPriority = fPriority;
}



void Asura_Sound_Event::SetRadii( float fInner, float fOuter )
{
	Asura_Assert(fInner <= fOuter, "invalid settings");
	Asura_Assert(fInner >= 0.0f, "invalid settings");
	Asura_Assert(fOuter >= 0.0f, "invalid settings");
	m_fInnerRadius = fInner;
	m_fOuterRadius = fOuter;
}

void Asura_Sound_Event::SetPlayableList(  const Asura_Sound_Playable* const* apxPlayables, u_int uNumPlayables )
{
	// check that the list doesn't mix events and samples.
	u_int uNumEvents = 0;
	u_int uNumSamples = 0;
	for (u_int u = 0; u < uNumPlayables; ++u)
	{
		bool bIsSample = apxPlayables[u]->IsSample();
		if (bIsSample)
		{
			++uNumSamples;
		}
		else
		{
			++uNumEvents;
		}
	}
	m_bIsMetaEvent = (uNumEvents != 0);

	// overwrite the existing array with this new one
	u_int uCurrentSize = m_xPlayableList.Size();
	if (uNumPlayables > uCurrentSize)
	{
		m_xPlayableList.Clear();
		uCurrentSize = 0;
	}
	if (!uCurrentSize)
	{
		m_xPlayableList.SetSize(uNumPlayables);
	}
	else
	{
		m_xPlayableList.Shrink(uNumPlayables);
	}

	for (u_int u = 0; u < uNumPlayables; ++u)
	{
		m_xPlayableList[u] = apxPlayables[u];
	}

	// ensure the random playback object has the right number of elements
	if (m_ePlaybackType == ASURA_PT_CDPLAYERRANDOM)
	{
		// changing playback type will realloc the random thingy
		SetPlaybackType(ASURA_PT_RANDOM);
		SetPlaybackType(ASURA_PT_CDPLAYERRANDOM);
	}
}
#endif

#ifdef ASURA_TOOLS	// tools-only data

void Asura_Sound_Event::SetMixerChannelName(const char* szMixerChannelName)
{ 
	m_xMixerChannelName = Asura_String(szMixerChannelName); 
	m_pxMixingChannel = Asura_Sound_Mixer_System::FindChannel(Asura_GetHashID(szMixerChannelName));
}

void Asura_Sound_Event::SetInstanceGroupName(const char* szInstanceGroupName)
{
	m_xInstanceGroupName = szInstanceGroupName;
	m_uInstanceGroupID = szInstanceGroupName ? Asura_GetHashID(szInstanceGroupName) : 0;
}

const Asura_Sound_PlayableID& Asura_Sound_Event::GetPlayableID(u_int uIndex) const
{
	u_int uNumPlayables = m_xPlayableIDList.Size();
	Asura_Assert(uIndex < uNumPlayables, "invalid playable sound index");

	return m_xPlayableIDList[uIndex];
}

bool Asura_Sound_Event::SetPlayableIDList( const Asura_Sound_PlayableID* pxPlayables, u_int uNumPlayables)
{
	if(uNumPlayables == m_xPlayableIDList.Size())
	{
		//If nothing has changed ,then we can return early
		//RAR 05/07/2006
		bool bDiff = false;
		for(u_int i=0;i<uNumPlayables;i++)
		{
			if(pxPlayables[i] != m_xPlayableIDList[i])
			{
				bDiff = true;
				break;
			}
		}
		if(!bDiff) return false;
	}


	// overwrite the existing array with this new one
	u_int uCurrentSize = m_xPlayableIDList.Size();
	if (uNumPlayables > uCurrentSize)
	{
		m_xPlayableIDList.Clear();
		uCurrentSize = 0;
	}
	if (!uCurrentSize)
	{
		m_xPlayableIDList.SetSize(uNumPlayables);
	}
	else
	{
		m_xPlayableIDList.Shrink(uNumPlayables);
	}

	for (u_int u = 0; u < uNumPlayables; ++u)
	{
		m_xPlayableIDList[u] = pxPlayables[u];
	}

	UpdatePlayableListFromIDList();
	return true;
}

bool Asura_Sound_Event::RemovePlayableID(const Asura_Sound_PlayableID& xID)
{
	u_int uDeleted = 0;
	//Remove any references to the playable ID
	for(u_int i=0;i<m_xPlayableIDList.Size();i++)
	{
		if(m_xPlayableIDList[i] != xID)
		{
			if(uDeleted)
			{
				m_xPlayableIDList[i-uDeleted] = m_xPlayableIDList[i];
			}
		}
		else
		{
			uDeleted++;
		}
	}
	if(!uDeleted) return false;

	//Some have been deleted , so update the playable pointers as well
	m_xPlayableIDList.Shrink(m_xPlayableIDList.Size()-uDeleted);
	UpdatePlayableListFromIDList();
	return true;
}

bool Asura_Sound_Event::ReplacePlayableID(const Asura_Sound_PlayableID& xOldID,const Asura_Sound_PlayableID& xNewID)
{
	u_int uReplaced = 0;
	//Replace any references to the playable ID
	for(u_int i=0;i<m_xPlayableIDList.Size();i++)
	{
		if(m_xPlayableIDList[i] == xOldID)
		{
			m_xPlayableIDList[i] = xNewID;
			uReplaced++;
		}
	}
	if(!uReplaced) return false;

	//Some have been replaced , so update the playable pointers as well
	UpdatePlayableListFromIDList();
	return true;
}

void Asura_Sound_Event::UpdatePlayableListFromIDList()
{
	//build array of pointers to the playables for the non-tools data
	const Asura_Sound_Playable** ppxPlayables = m_xPlayableIDList.Size() ? new const Asura_Sound_Playable*[m_xPlayableIDList.Size()] : 0;
	u_int uNumFound = 0;
	for(u_int i=0;i<m_xPlayableIDList.Size();i++)
	{
		if(m_xPlayableIDList[i].m_eType == ASURA_SOUND_PLAYABLE_EVENT)
		{
			ppxPlayables[uNumFound] = Asura_Sound_Event_Storage::GetEventByHash(m_xPlayableIDList[i].m_uHashID);
		}
		else
		{
			Asura_Assert(m_xPlayableIDList[i].m_eType == ASURA_SOUND_PLAYABLE_SAMPLE,"");
			ppxPlayables[uNumFound] = Asura_Sound_Event_Storage::GetSampleByHash(m_xPlayableIDList[i].m_uHashID);
		}
		if(ppxPlayables[uNumFound]) uNumFound++;
	}

	SetPlayableList(ppxPlayables,uNumFound);

	delete [] ppxPlayables;
}

bool Asura_Sound_Event::IsEndlessPlayback() const
{
	UpdateEndlessPlayback();
	return m_bEndlessPlayback;
}

float Asura_Sound_Event::GetMaxOuterRadius() const
{
	UpdateMaxOuterRadius();
	return m_fMaxOuterRadius;
}

// UpdateEndlessPlayback() goes through every possibility of event playback to establish
// whether this event plays forever. If some paths do and some don't the function returns
// false. It should be called whenever the playable list changes.
bool Asura_Sound_Event::UpdateEndlessPlayback() const
{
	// if this event is looping we're done
	// If the event mapping flag is set , then any settings from this event are ignored (such as the looping flag). RAR 30/08/2006
	if (m_bEventLooping && !m_bEventMapping && !HasDuration())
	{
		m_bEndlessPlayback = true;
		return true;
	}

	bool bOK = true;
	// all playables must match looping or non-looping
	bool bFoundLooping = false;
	bool bFoundNonLooping = false;
	for (u_int u = 0; u < m_xPlayableList.Size(); ++u, bOK)
	{
		bool bTempLooping;
		if (m_xPlayableList[u]->IsEvent())
		{
			const Asura_Sound_Event* pxEvent = static_cast<const Asura_Sound_Event*>(m_xPlayableList[u]);
			bOK = pxEvent->UpdateEndlessPlayback();
			bTempLooping = pxEvent->m_bEndlessPlayback;
		}
		else
		{
			const Asura_Sound_Sample* pxSample = static_cast<const Asura_Sound_Sample*>(m_xPlayableList[u]);
			bTempLooping = pxSample->GetLooping() && pxSample->m_fDuration == 0.0f;
		}

		if (bOK)
		{
			if (bTempLooping)
			{
				bFoundLooping = true;
				bOK = !bFoundNonLooping;
			}
			else
			{
				bFoundNonLooping = true;
				bOK = !bFoundLooping;
			}
		}
	}

	m_bEndlessPlayback = bFoundLooping;

	// (AAM - 07.12.2006) Don't fire this assert if this is a collection of simultaneous events
	#if defined ASURA_SOUND_INCLUDE_TOOLS_DATA
		Asura_Assertf(bOK || GetPlaybackType() == ASURA_PT_SIMULTANEOUS,
			("Sound event '%s': some paths loop and others do not.", GetName()));
	#else
		Asura_Assertf(bOK || GetPlaybackType() == ASURA_PT_SIMULTANEOUS,
			("Sound event 0x%X: some paths loop and others do not.", GetHashID()));
	#endif

	return bOK;
}

void Asura_Sound_Event::UpdateMaxOuterRadius() const
{
	m_fMaxOuterRadius = 0;


	for (u_int u = 0; u < m_xPlayableList.Size(); ++u)
	{
		if (m_xPlayableList[u]->IsEvent())
		{
			//Get max outer radius from any child events
			const Asura_Sound_Event* pxEvent = static_cast<const Asura_Sound_Event*>(m_xPlayableList[u]);
			pxEvent->UpdateMaxOuterRadius();

			m_fMaxOuterRadius = Asura_Maths::Max(m_fMaxOuterRadius,pxEvent->m_fMaxOuterRadius);
		}
		else
		{
			//This event has samples , so the outer radius and 3d type of the event will be used
			if(m_e3dSoundType == ASURA_SOUND_DIRECT || m_e3dSoundType == ASURA_SOUND_RANDOMPOS)
			{
				//2d sound , so effectively infinite range
				m_fMaxOuterRadius = Asura_Maths::LargeFloat;
				break;
			}
			else
			{
				m_fMaxOuterRadius = Asura_Maths::Max(m_fMaxOuterRadius,m_fOuterRadius);
			}

		}
	}

}
#endif 


const Asura_Sound_VariableParams* Asura_Sound_Event::GetVariableParams() const
{
	Asura_Assert(m_ePlaybackType == ASURA_PT_VARIABLE,"Only valid for variable playback type");
	return m_pxVariableParams;
}

#ifdef ASURA_TOOLS
void Asura_Sound_Event::SetVariableParams(const Asura_Sound_VariableParams& xVariableParams)
{
	Asura_Assert(m_ePlaybackType == ASURA_PT_VARIABLE,"Only valid for variable playback type");
	if(m_ePlaybackType == ASURA_PT_VARIABLE)
	{
		*m_pxVariableParams = xVariableParams;
	}
}
#endif //ASURA_TOOLS

// A S U R A _ S O U N D _ S A M P L E ///////////////////////////////////////
Asura_Sound_Sample::Asura_Sound_Sample(const char* szName)
#ifdef ASURA_USE_AUDIO_SYSTEM
: m_uWave(ASURA_HASH_ID_UNSET)
#else
: m_eSoundID(ASURA_SID_NO_SOUND)
#endif
, m_fVolume(1.0f)
, m_fPitch(1.0f)
, m_fDuration(0.0f)
, m_bLooping(false)
#ifdef ASURA_SOUND_INCLUDE_TOOLS_DATA
, m_xSampleName(szName)
, m_xSoundType("Default")
, m_xSoundSubType("Default")
#endif
#ifdef ASURA_TOOLS
, m_iNumPlatforms(0)
, m_pxPlatformSettings(0)
#endif
{
	m_uHashID = Asura_GetHashID(szName);
#if defined(ASURA_USE_MULTIPLE_HANDLERS)
	m_pxSoundHandler = &Asura_Sound_System::GetCurrentHandler();
#endif
}

Asura_Sound_Sample::~Asura_Sound_Sample()
{
	// TODO: check no events still reference this

	#ifdef ASURA_TOOLS
	delete [] m_pxPlatformSettings;
	#endif
}

#ifdef ASURA_TOOLS
int Asura_Sound_Sample::GetSampleRate(int iPlatform) const
{
	const Asura_Sound_Sample_PlatformSettings* pxSettings = GetPlatformSettings(iPlatform);
	return pxSettings ? pxSettings->m_iSampleRate : 0;
}

int Asura_Sound_Sample::GetCompressionQuality( const int iPlatform ) const
{
	//Actually the same as the above function
	const Asura_Sound_Sample_PlatformSettings* pxSettings = GetPlatformSettings(iPlatform);
	return pxSettings ? pxSettings->m_iSampleRate : TSS_QUALITY_USE_DEFAULT;
}

void Asura_Sound_Sample::SetSampleRate(int iPlatform,int iSampleRate)
{
	//Get settings. No need to create them if the sample rate is being set to zero though.
	Asura_Sound_Sample_PlatformSettings* pxSettings = GetPlatformSettings(iPlatform,iSampleRate!=0);
	if(pxSettings) pxSettings->m_iSampleRate = iSampleRate;
}

void Asura_Sound_Sample::SetCompressionQuality( const int iPlatform, const int iCompressionQuality )
{
	//Get settings
	Asura_Sound_Sample_PlatformSettings* pxSettings = GetPlatformSettings( iPlatform, true );
	if( pxSettings ) 
	{
		pxSettings->m_iSampleRate = iCompressionQuality;
	}
}

const Asura_Sound_Sample_PlatformSettings* Asura_Sound_Sample::GetPlatformSettings(int iPlatform) const
{
	Asura_Assert(iPlatform<TOOLS_PLATFORM_NUM_PLATFORMS,"Unexpected platform");
	
	//Settings already exist?
	for(int i=0;i<m_iNumPlatforms;i++)
	{
		if(m_pxPlatformSettings[i].m_iPlatform == iPlatform) return &m_pxPlatformSettings[i];
	}

	return 0;
}

Asura_Sound_Sample_PlatformSettings* Asura_Sound_Sample::GetPlatformSettings(int iPlatform,bool bCreate)
{
	Asura_Assert(iPlatform<TOOLS_PLATFORM_NUM_PLATFORMS,"Unexpected platform");

	//Settings already exist?
	for(int i=0;i<m_iNumPlatforms;i++)
	{
		if(m_pxPlatformSettings[i].m_iPlatform == iPlatform) return &m_pxPlatformSettings[i];
	}

	if(!bCreate) return 0;

	//allocate new entry for the settings
	Asura_Sound_Sample_PlatformSettings* pxNewSettings = new Asura_Sound_Sample_PlatformSettings[m_iNumPlatforms+1];
	for(int i=0;i<m_iNumPlatforms;i++)
	{
		pxNewSettings[i] = m_pxPlatformSettings[i];
	}
	delete [] m_pxPlatformSettings;
	m_pxPlatformSettings = pxNewSettings;
	m_iNumPlatforms++;

	//Initialise the new settings and return it
	Asura_Sound_Sample_PlatformSettings* pxSettings = &m_pxPlatformSettings[m_iNumPlatforms-1];
	pxSettings->Initialise(iPlatform);
	return pxSettings;
	
}
#endif //ASURA_TOOLS

//eof
