
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_ResourceAllocator.h
// Author : Andy Tate
// Date   : 7-Feb-2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif   // ifdef ASURA_USE_PRAGMA_ONCE

#ifndef __ASURA_RESOURCEALLOCATOR_H__
#define __ASURA_RESOURCEALLOCATOR_H__

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura_MemoryManagement.h"
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
#include "Core/Memory/Asura_MemoryBank.h"
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

#include "Core/Asura_MemoryManagement_DebugOff.h"	// ...disable placement new overriding.


////////////////////////////////////////////////////////////////////////////////
// Forward class declarations...
////////////////////////////////////////////////////////////////////////////////

class Asura_Chunk_Stream;


////////////////////////////////////////////////////////////////////////////////
// Defines...
////////////////////////////////////////////////////////////////////////////////

#if !defined(ASURA_MAX_RESOURCE_STACK_ENTRIES)
// Maximum number of entries in the stack...
#define ASURA_MAX_RESOURCE_STACK_ENTRIES	16
#endif	// !defined(ASURA_MAX_RESOURCE_STACK_ENTRIES)


////////////////////////////////////////////////////////////////////////////////
// Enumerated types...
////////////////////////////////////////////////////////////////////////////////

enum ASURA_RESOURCE_TYPE
{
	ASURA_RESOURCE_UNSPECIFIED,

	ASURA_RESOURCE_AABB_TREE,
	ASURA_RESOURCE_ANIMATION,
	ASURA_RESOURCE_COLLISION_MESH,
	ASURA_RESOURCE_CUTSCENE,
	ASURA_RESOURCE_ENVIRONMENT_MESH,
	ASURA_RESOURCE_FRAGMENT,
	ASURA_RESOURCE_GAMESCENE,
	ASURA_RESOURCE_GLOBAL_ILLUMATION,
	ASURA_RESOURCE_META_MUSIC,
	ASURA_RESOURCE_MODULE,
	ASURA_RESOURCE_MODULE_REVERB,
	ASURA_RESOURCE_OBJECT_MESH,
	ASURA_RESOURCE_OBJECT_HIERARCHY_MESH,
	ASURA_RESOURCE_PFX,
	ASURA_RESOURCE_SKINNED_MESH,
	ASURA_RESOURCE_SOUND_SAMPLE,
	ASURA_RESOURCE_STATIC_CORONA,
	ASURA_RESOURCE_STATIC_MESSAGE,
	ASURA_RESOURCE_STREAMING_BACKGROUND_SOUND,
	ASURA_RESOURCE_TEXTURE,
	ASURA_RESOURCE_TEXTURE_ANIMATION,
	ASURA_RESOURCE_WAYPOINT,
	ASURA_RESOURCE_ATTRACTORS,
	ASURA_RESOURCE_BOTANICAL_MESH,

	ASURA_RESOURCE_TEMPORARY,

	ASURA_MAX_RESOURCE_TYPES
};



////////////////////////////////////////////////////////////////////////////////
// Class declaration...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Class        : Asura_ResourceAllocator
// Author       : Andy Tate
// Date         : 7-Feb-2006
//
// Description  : Class to handle the allocation and deallocation of resources
//				  which are used to make up the levels (i.e. models, animations,
//				  sounds, textures, etc...).
////////////////////////////////////////////////////////////////////////////////

class Asura_ResourceAllocator
{
public:
	// Public functions...

	// Allocation/deallocation functions...
						static	bool						AllocateMemoryBlock		(void*& rpvData, const u_int uNumEntries, const u_int uSizeOfEntry, const ASURA_ALIGN_ID eAlignment);
						static	bool						DeallocateMemoryBlock	(void*& rpvData);

	template<class T>	static	bool						AllocateArray			(T*& rpxData, const u_int uNumEntries);
	template<class T>	static	bool						DeallocateArray			(T*& rpxData);

#if defined(ASURA_MEMORYMANAGEMENT_ENABLED)
						static	bool						AllocateMemoryBlock		(void*& rpvData, const u_int uNumEntries, const u_int uSizeOfEntry, const ASURA_ALIGN_ID eAlignment, const char* szFile, const int iLine, const u_int uReturnAddr);
						static	bool						DeallocateMemoryBlock	(void*& rpvData, const char* szFile, const int iLine);

	template<class T>	static	bool						AllocateArray			(T*& rpxData, const u_int uNumEntries, const char* szFile, const int iLine, const u_int uReturnAddr);
	template<class T>	static	bool						DeallocateArray			(T*& rpxData, const char* szFile, const int iLine);
#endif	// defined(ASURA_MEMORYMANAGEMENT_ENABLED)

	// Stack functions...
						static	void						PushResource			(const ASURA_RESOURCE_TYPE eResource);
						static	ASURA_RESOURCE_TYPE			PopResource				(void);
						static  ASURA_RESOURCE_TYPE			GetCurrentResourceType	(void);

	// Accessor functions...
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	static						void						SetCurrentMemoryBank	(Asura_MemoryBank* pxMemoryBank)		{ s_pxCurrentMemoryBank = pxMemoryBank; }
	static						Asura_MemoryBank*			GetCurrentMemoryBank	(void)									{ return(s_pxCurrentMemoryBank); }
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

#if defined(ASURA_MEMORYBANK_ALLOW_STATS)
	static						const char**				GetResourceNameArray	(void)									{ return(s_aszResourceNames); }
#endif

protected:
	// Protected variables...


	// Protected functions...


private:
	// Private variables...
	static	ASURA_RESOURCE_TYPE	s_aeResourceStack[ASURA_MAX_RESOURCE_STACK_ENTRIES];	// The type of resource we're currently allocating/deallocating.
	static	int					s_iResourceStackIndex;									// The index of the resource we're currently using.

#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	static	Asura_MemoryBank*	s_pxCurrentMemoryBank;									// The memory-bank currently in use.
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

#if defined(ASURA_MEMORYBANK_ALLOW_STATS)
	static	const char*			s_aszResourceNames[ASURA_MAX_RESOURCE_TYPES];			// Array of names for the resources to use.
#endif

	// Private functions...
	Asura_ResourceAllocator  (void);
	Asura_ResourceAllocator  (const Asura_ResourceAllocator& xOther);
	~Asura_ResourceAllocator ();

								Asura_ResourceAllocator&	operator=				(const Asura_ResourceAllocator& xOther);

	// Accessor functions...
	static						bool						IsResourceStackInUse	(void)	{ return((s_iResourceStackIndex >= 0) && (s_iResourceStackIndex < ASURA_MAX_RESOURCE_STACK_ENTRIES)); }
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	static						bool						IsUsingMemoryBank		(void)	{ return((s_pxCurrentMemoryBank != NULL) && IsResourceStackInUse() && (s_aeResourceStack[s_iResourceStackIndex] != ASURA_RESOURCE_TEMPORARY)); }
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)

};   // End of class 'Asura_ResourceAllocator'.



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::AllocateArray
// Author       : Andy Tate
// Date         : 10-Feb-2006
////////////////////////////////////////////////////////////////////////////////

template<class T> bool Asura_ResourceAllocator::AllocateArray (T*& rpxData,
															   const u_int uNumEntries)
{
	// Do we have anything to allocate?
	if(uNumEntries == 0)
	{
		// No - allocate nothing, but return success...
		rpxData	= NULL;
		return(true);
	}
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else if(IsUsingMemoryBank())
	{
		void*	pvData;

		// Allocate a block of memory...
		if(AllocateMemoryBlock(pvData, uNumEntries, sizeof(T), ASURA_ALIGN_16_BYTE))
		{
			// Allocation successful - initialise the data...
			rpxData	= reinterpret_cast<T*>(pvData);

			// Call the constructors and return...
			for(u_int u = 0; u < uNumEntries; u++)
			{
				pvData	= reinterpret_cast<void*>(&rpxData[u]);
				pvData	= new (pvData) T;
			}

			// Return success...
			return(true);
		}
		else
		{
			// Allocation failed - return failure...
			return(false);
		}
	}
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else
	{
		// Call the array constructor...
		rpxData	= new T [uNumEntries];

		// Return success...
		return(true);
	}
}

/// End of function 'Asura_ResourceAllocator::AllocateArray'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::DeallocateArray
// Author       : Andy Tate
// Date         : 10-Feb-2006
////////////////////////////////////////////////////////////////////////////////

template<class T> bool Asura_ResourceAllocator::DeallocateArray (T*& rpxData)
{
	// Do we have anything to deallocate?
	if(rpxData == NULL)
	{
		// No - return success...
		return(true);
	}
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else if(IsUsingMemoryBank())
	{
		void*							pvData			= reinterpret_cast<void*>(rpxData);
		u_char*							pucData			= static_cast<u_char*>(pvData);
		const Asura_MemoryBank::Handle	xHandle			= s_pxCurrentMemoryBank->GetHandle(pvData);
		const u_int						uNumEntries		= s_pxCurrentMemoryBank->GetNumEntries(xHandle);
		const u_int						uSizeOfEntry	= s_pxCurrentMemoryBank->GetSizeOfEntry(xHandle);

		// Destroy the data...
		for(u_int u = 0; u < uNumEntries; u++, pucData += uSizeOfEntry)
		{
			reinterpret_cast<T*>(pucData)->~T();
		}

		// Use the regular allocator...
		if(DeallocateMemoryBlock(pvData))
		{
			// Deallocation successful - return...
			rpxData	= NULL;
			return(true);
		}
		else
		{
			// Deallocation failed - return failure...
			return(false);
		}
	}
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else
	{
		// Call the array destructor...
		delete[] rpxData;
		rpxData	= NULL;
		return(true);
	}
}

/// End of function 'Asura_ResourceAllocator::DeallocateArray'. ///



////////////////////////////////////////////////////////////////////////////////
// Global functions to call our class-specific ones...
////////////////////////////////////////////////////////////////////////////////

inline void Asura_ResourcePushType (const ASURA_RESOURCE_TYPE eResource)
{
	Asura_ResourceAllocator::PushResource(eResource);
}

inline void Asura_ResourcePopType (void)
{
	Asura_ResourceAllocator::PopResource();
}

template<class T> inline bool Asura_ResourceAllocateArray (T*& rpxData, const u_int uNumEntries)
{
	return(Asura_ResourceAllocator::AllocateArray(rpxData, uNumEntries));
}

template<class T> inline bool Asura_ResourceDeallocateArray (T*& rpxData)
{
	return(Asura_ResourceAllocator::DeallocateArray(rpxData));
}

template<class T> inline bool Asura_ResourceAllocateMemoryBlock (T*& rpxData, const u_int uSizeInBytes, const ASURA_ALIGN_ID eAlignment)
{
	void*	pvData		= NULL;
	bool	bSuccess	= Asura_ResourceAllocator::AllocateMemoryBlock(pvData, uSizeInBytes, 1, eAlignment);

	// Set the allocated pointer and return...
	rpxData	= static_cast<T*>(pvData);
	return(bSuccess);
}

template<class T> inline bool Asura_ResourceDeallocateMemoryBlock (T*& rpxData)
{
	void*	pvData		= static_cast<void*>(rpxData);
	bool	bSuccess	= Asura_ResourceAllocator::DeallocateMemoryBlock(pvData);

	// Set the deallocated pointer and return...
	rpxData	= static_cast<T*>(pvData);
	return(bSuccess);
}


#if defined(ASURA_MEMORYMANAGEMENT_ENABLED)

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::AllocateArray
// Author       : Andy Tate
// Date         : 10-Feb-2006
////////////////////////////////////////////////////////////////////////////////

template<class T> bool Asura_ResourceAllocator::AllocateArray (T*& rpxData,
															   const u_int uNumEntries,
															   const char* szFile,
															   const int iLine,
															   const u_int uReturnAddr)
{
	// Do we have anything to allocate?
	if(uNumEntries == 0)
	{
		// No - allocate nothing, but return success...
		rpxData	= NULL;
		return(true);
	}
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else if(IsUsingMemoryBank())
	{
		void*	pvData;

		// Allocate a block of memory...
		if(AllocateMemoryBlock(pvData, uNumEntries, sizeof(T), ASURA_ALIGN_16_BYTE, szFile, iLine, uReturnAddr))
		{
			// Allocation successful - initialise the data...
			rpxData	= reinterpret_cast<T*>(pvData);

			// Call the constructors and return...
			for(u_int u = 0; u < uNumEntries; u++)
			{
				pvData	= reinterpret_cast<void*>(&rpxData[u]);
				pvData	= new (pvData) T;
			}

			// Return success...
			return(true);
		}
		else
		{
			// Allocation failed - return failure...
			return(false);
		}
	}
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else
	{
		// Call the array constructor...
		rpxData	= new (szFile, iLine, uReturnAddr) T [uNumEntries];

		// Return success...
		return(true);
	}
}

/// End of function 'Asura_ResourceAllocator::AllocateArray'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::DeallocateArray
// Author       : Andy Tate
// Date         : 10-Feb-2006
////////////////////////////////////////////////////////////////////////////////

template<class T> bool Asura_ResourceAllocator::DeallocateArray (T*& rpxData,
																 const char* szFile,
																 const int iLine)
{
	// Do we have anything to deallocate?
	if(rpxData == NULL)
	{
		// No data to deallocate...
		return(true);
	}
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else if(IsUsingMemoryBank())
	{
		void*							pvData			= reinterpret_cast<void*>(rpxData);
		u_char*							pucData			= static_cast<u_char*>(pvData);
		const Asura_MemoryBank::Handle	xHandle			= s_pxCurrentMemoryBank->GetHandle(pvData);
		const u_int						uNumEntries		= s_pxCurrentMemoryBank->GetNumEntries(xHandle);
		const u_int						uSizeOfEntry	= s_pxCurrentMemoryBank->GetSizeOfEntry(xHandle);

		// Destroy the data...
		for(u_int u = 0; u < uNumEntries; u++, pucData += uSizeOfEntry)
		{
			reinterpret_cast<T*>(pucData)->~T();
		}

		// Use the regular allocator...
		if(DeallocateMemoryBlock(pvData, szFile, iLine))
		{
			// Deallocation successful - return...
			rpxData	= NULL;
			return(true);
		}
		else
		{
			// Deallocation failed - return failure...
			return(false);
		}
	}
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else
	{
		// Call the array destructor...
		delete[] rpxData;
		rpxData	= NULL;
		return(true);
	}
}

/// End of function 'Asura_ResourceAllocator::DeallocateArray'. ///



////////////////////////////////////////////////////////////////////////////////
// Global functions to call our class-specific ones...
////////////////////////////////////////////////////////////////////////////////

template<class T> inline bool Asura_ResourceAllocateArray (T*& rpxData, const u_int uNumEntries, const char* szFile, const int iLine, const u_int uReturnAddr)
{
	return(Asura_ResourceAllocator::AllocateArray(rpxData, uNumEntries, szFile, iLine, uReturnAddr));
}

template<class T> inline bool Asura_ResourceDeallocateArray (T*& rpxData, const char* szFile, const int iLine)
{
	return(Asura_ResourceAllocator::DeallocateArray(rpxData, szFile, iLine));
}

template<class T> inline bool Asura_ResourceAllocateMemoryBlock (T*& rpxData, const u_int uSizeInBytes, const ASURA_ALIGN_ID eAlignment, const char* szFile, const int iLine, const u_int uReturnAddr)
{
	void*	pvData		= NULL;
	bool	bSuccess	= Asura_ResourceAllocator::AllocateMemoryBlock(pvData, uSizeInBytes, 1, eAlignment, szFile, iLine, uReturnAddr);

	// Set the allocated pointer and return...
	rpxData	= static_cast<T*>(pvData);
	return(bSuccess);
}

template<class T> inline bool Asura_ResourceDeallocateMemoryBlock (T*& rpxData, const char* szFile, const int iLine)
{
	void*	pvData		= static_cast<void*>(rpxData);
	bool	bSuccess	= Asura_ResourceAllocator::DeallocateMemoryBlock(pvData, szFile, iLine);

	// Set the deallocated pointer and return...
	rpxData	= static_cast<T*>(pvData);
	return(bSuccess);
}

#endif	// defined(ASURA_MEMORYMANAGEMENT_ENABLED)


#include "Core/Asura_MemoryManagement_DebugOn.h"	// ...enable placement new overriding.


#endif   // ifndef __ASURA_RESOURCEALLOCATOR_H__


/// End of file. ///
