//
// Asura_ModuleReverb_System.cpp
//
// Created by Richard Rice - 12/03/2003
// 
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Sound/Asura_ModuleReverb_System.h"

#include "Camera/Asura_Camera.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "Rendering/Asura_Module.h"


/////////////////
// Static data //
/////////////////
#if defined(ASURA_USE_STREAMING)
Asura_ModuleReverb_Handler*							Asura_ModuleReverb_System::s_pxCurrentHandler	= NULL;	// Currently active handler.
Asura_TemplateHashTree<Asura_ModuleReverb_Handler>	Asura_ModuleReverb_System::s_xHandlerTree;				// Tree of all of the handlers available.
#else
Asura_ModuleReverb_Handler							Asura_ModuleReverb_System::s_xHandler;					// Single static-instance of a zone.
#endif	// defined(ASURA_USE_STREAMING)


///////////////////////////////////////////////////////////////////////////////
// Asura_ModuleReverb_Handler
///////////////////////////////////////////////////////////////////////////////
Asura_ModuleReverb_Handler::~Asura_ModuleReverb_Handler()
{
	Reset();
}

void Asura_ModuleReverb_Handler::Reset()
{
	m_xDefaultReverb.m_iReverbType = 0;

	Asura_ResourcePushType(ASURA_RESOURCE_MODULE_REVERB);
	Asura_ResourceDeallocateArray(m_pxModuleReverbArray);
	Asura_ResourcePopType();

	m_pxModuleReverbArray = 0;

	m_iNumberOfModules = 0;
}


void Asura_ModuleReverb_Handler::AllocateModuleData(int iNumberOfModules)
{
	Asura_Assert(!m_pxModuleReverbArray, "Module reverb data already allocated");
	if (m_pxModuleReverbArray)
	{
		Reset();
	}

	Asura_ResourcePushType(ASURA_RESOURCE_MODULE_REVERB);
	Asura_ResourceAllocateArray(m_pxModuleReverbArray, iNumberOfModules);
	Asura_ResourcePopType();

	if (!m_pxModuleReverbArray) return;

	m_iNumberOfModules = iNumberOfModules;
	// initialise the reverb data for the modules
	for (int i=0; i<m_iNumberOfModules; ++i)
	{
		m_pxModuleReverbArray[i].m_iReverbType = ASURA_REVERB_USE_DEFAULT;
	}
}


const Asura_ModuleReverb* Asura_ModuleReverb_Handler::GetReverbSettings(const Asura_Vector_3* pxPosition, bool& bFoundModule) const
{
	bFoundModule = false;

	Asura_Assert(pxPosition, "Null position pointer");
	if (!pxPosition) return 0;

	const Asura_ModuleReverb* pxReverb = &m_xDefaultReverb;

	//see if the position is in a module that overides the settings
	if (m_pxModuleReverbArray)
	{
		if (m_iNumberOfModules == Asura_Module_System::GetNumberOfModules())
		{
			const int iModuleIndex = Asura_Module_System::GetModuleIndexFromPosition(*pxPosition);
			//Asura_Assert(iModuleIndex>=0 && iModuleIndex<m_iNumberOfModules, "Invalid module index");
			if (iModuleIndex>=0 && iModuleIndex<m_iNumberOfModules)
			{
				bFoundModule = true;

				if (m_pxModuleReverbArray[iModuleIndex].m_iReverbType != ASURA_REVERB_USE_DEFAULT)
				{
					//In a module that overides the default settings
					pxReverb = &m_pxModuleReverbArray[iModuleIndex];
				}
			}
		}
		else
		{
			Asura_Assert(0, "Incorrect number of modules in reverb system");
		}
	}

	return pxReverb;
}


void Asura_ModuleReverb_Handler::SetModuleReverbType(int iModule, int iReverbType)
{
	if (m_pxModuleReverbArray)
	{
		Asura_Assert(iModule>=0 && iModule<m_iNumberOfModules, "Invalid module index");
		if (iModule>=0 && iModule<m_iNumberOfModules)
		{
			m_pxModuleReverbArray[iModule].m_iReverbType = iReverbType;
		}
	}
}

int Asura_ModuleReverb_Handler::GetModuleReverbType(int iModule) const
{
	if (m_pxModuleReverbArray)
	{
		Asura_Assert(iModule>=0 && iModule<m_iNumberOfModules, "Invalid module index");
		if (iModule>=0 && iModule<m_iNumberOfModules)
		{
			return m_pxModuleReverbArray[iModule].m_iReverbType;
		}
	}

	return ASURA_REVERB_USE_DEFAULT;
}


#ifdef ASURA_TOOLS
void Asura_ModuleReverb_Handler::DeleteModule(int iModuleIndex)
{
	if (m_iNumberOfModules && m_pxModuleReverbArray)
	{
		Asura_Assert(iModuleIndex>=0 && iModuleIndex<m_iNumberOfModules, "Invalid module index");
		if (iModuleIndex>=0 && iModuleIndex<m_iNumberOfModules)
		{
			m_pxModuleReverbArray[iModuleIndex] = m_pxModuleReverbArray[m_iNumberOfModules-1];
			--m_iNumberOfModules;	
		}
	}	
}
#endif //ASURA_TOOLS



///////////////////////////////////////////////////////////////////////////////
// Asura_ModuleReverb_System
///////////////////////////////////////////////////////////////////////////////
void Asura_ModuleReverb_System::Initialise()
{
#if defined(ASURA_USE_STREAMING)
	Asura_Assert(!s_pxCurrentHandler, "Current handler should be null!");
	if (s_pxCurrentHandler)
	{
		Shutdown();
	}
#endif // defined(ASURA_USE_STREAMING)
}

void Asura_ModuleReverb_System::Shutdown()
{
#if defined(ASURA_USE_STREAMING)
	// Reset the entire handler tree...
	s_xHandlerTree.Reset();
	s_pxCurrentHandler = 0;
#else
	// Reset the current instance...
	GetCurrentHandler().Reset();
#endif	// !defined(ASURA_USE_STREAMING)
}

void Asura_ModuleReverb_System::Reset()
{
	Shutdown();
	Initialise();
}

//////////////////////////////////
// Asura_ReverbCallbackUserData //
//////////////////////////////////
// User data for traversal of the tree.
class Asura_ReverbCallbackUserData
{
public:
	Asura_ReverbCallbackUserData(const Asura_Vector_3* pxPosition)
		: m_pxPosition(pxPosition)
		, m_pxReverb(0)
	{}

	const Asura_Vector_3* m_pxPosition;	  // Input
	const Asura_ModuleReverb* m_pxReverb; // Output
};

///////////////////////
// GetReverbCallback //
///////////////////////
// Return true to continue looking, false to stop.
#if defined(ASURA_USE_STREAMING)
static bool GetReverbCallback(Asura_Hash_ID uHash, Asura_ModuleReverb_Handler* pxData, void* pUserData)
{
	Asura_Assert(pxData && pUserData, "Null pointer");
	if (pxData && pUserData)
	{
		Asura_ReverbCallbackUserData* pxUserData = static_cast<Asura_ReverbCallbackUserData*>(pUserData);

		bool bFoundModule = false;
		pxUserData->m_pxReverb = pxData->GetReverbSettings(pxUserData->m_pxPosition, bFoundModule);

		if (bFoundModule)
		{
			return false; // Stop looking.
		}
	}

	return true; // Continue looking.
}
#endif //defined(ASURA_USE_STREAMING)

///////////////////////
// GetReverbSettings //
///////////////////////
const Asura_ModuleReverb* Asura_ModuleReverb_System::GetReverbSettings(const Asura_Vector_3* pxPosition)
{
#if defined(ASURA_USE_STREAMING)
	// MGH 23/3/2006: We want the reverb setting at the given position. This means
	// that we should search through all the handlers until we find one with a module
	// that contains the given position.
	Asura_ReverbCallbackUserData xUserData(pxPosition);
	s_xHandlerTree.Traverse(GetReverbCallback, &xUserData);
	return xUserData.m_pxReverb;
#else
	bool bFoundModule = false;
	return GetCurrentHandler().GetReverbSettings(pxPosition, bFoundModule);
#endif //!defined(ASURA_USE_STREAMING)
}

#if defined(ASURA_USE_STREAMING)
////////////////////////////////////////////////////////////////////////////////
// Function     : CreateHandler
//
// Description  : Attempts to create a module-reverb-system handler with the specified
//				  hash-ID.
//
// Parameters   : The hash-ID of the handler to create.
//
// Return value : A pointer to the handler created.
////////////////////////////////////////////////////////////////////////////////
Asura_ModuleReverb_Handler* Asura_ModuleReverb_System::CreateHandler(const Asura_Hash_ID uHash)
{
	Asura_ModuleReverb_Handler* pxHandler = s_xHandlerTree.GetDataByHash(uHash);
	Asura_Assert(!pxHandler, "Already have an module-reverb-handler with this hash-ID.");

	// Do we already have this handler? No - create one...
	if (pxHandler == NULL)
	{
		pxHandler = s_xHandlerTree.AddNode(uHash);
	}

	return pxHandler;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : DestroyHandler
//
// Description  : Attempts to destroy the module-reverb-handler with the specified
//				  unique hash-ID.
//
// Parameters   : The hash-ID of the handler to destroy.
//
// Return value : "true" if destroyed, "false" if failed.
////////////////////////////////////////////////////////////////////////////////
bool Asura_ModuleReverb_System::DestroyHandler(const Asura_Hash_ID uHash)
{
	// Is this the current handler?
	if(s_pxCurrentHandler == s_xHandlerTree.GetDataByHash(uHash))
	{
		s_pxCurrentHandler	= NULL;
	}

	// Call the delete function...
	return s_xHandlerTree.DeleteNodeByHash(uHash);
}
#endif	// defined(ASURA_USE_STREAMING)



