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

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

#include "Core/Asura.h"

// This file's header...
#include "Core/Memory/Asura_ResourceAllocator.h"

// Asura independent header-files...
#include "Core/Asura_MemoryManagement_DebugOff.h"	// ...disable placement new overriding.
#include "Debugging/Asura_Assert.h"

// Asura platform header-files...

// Game independent header-files...

// Game platform header-files...

// Tools header-files...


////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////

// The type of resource we're currently allocating/deallocating...
ASURA_RESOURCE_TYPE	Asura_ResourceAllocator::s_aeResourceStack [ASURA_MAX_RESOURCE_STACK_ENTRIES];
int					Asura_ResourceAllocator::s_iResourceStackIndex	= -1;

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

#if defined(ASURA_MEMORYBANK_ALLOW_STATS)
// Array of names for the resources to use...
const char* Asura_ResourceAllocator::s_aszResourceNames[ASURA_MAX_RESOURCE_TYPES] =
{
	"Unspecified               ",	// ASURA_RESOURCE_UNSPECIFIED

	"AABB Tree                 ",	// ASURA_RESOURCE_AABB_TREE
	"Animation                 ",	// ASURA_RESOURCE_ANIMATION
	"Collision Mesh            ",	// ASURA_RESOURCE_COLLISION_MESH
	"Cutscene                  ",	// ASURA_RESOURCE_CUTSCENE
	"Environment Mesh          ",	// ASURA_RESOURCE_ENVIRONMENT_MESH
	"Fragment                  ",	// ASURA_RESOURCE_FRAGMENT
	"Gamescene                 ",	// ASURA_RESOURCE_GAMESCENE
	"Global Illumination       ",	// ASURA_RESOURCE_GLOBAL_ILLUMINATION
	"Meta Music                ",	// ASURA_RESOURCE_META_MUSIC
	"Module                    ",	// ASURA_RESOURCE_MODULE
	"Module Reverb             ",	// ASURA_RESOURCE_MODULE_REVERB
	"Object Mesh               ",	// ASURA_RESOURCE_OBJECT_MESH
	"Object-hierarchy Mesh     ",	// ASURA_RESOURCE_OBJECT_HIERARCHY_MESH
	"PFX                       ",	// ASURA_RESOURCE_PFX
	"Skinned Mesh              ",	// ASURA_RESOURCE_SKINNED_MESH
	"Sound Sample              ",	// ASURA_RESOURCE_SOUND_SAMPLE
	"Static Corona             ",	// ASURA_RESOURCE_STATIC_CORONA
	"Static Message            ",	// ASURA_RESOURCE_STATIC_MESSAGE
	"Streaming Background Sound",	// ASURA_RESOURCE_STREAMING_BACKGROUND_SOUND
	"Texture                   ",	// ASURA_RESOURCE_TEXTURE
	"Texture Animation         ",	// ASURA_RESOURCE_TEXTURE_ANIMATION
	"Waypoint                  ",	// ASURA_RESOURCE_WAYPOINT
	"Attractors                ",	// ASURA_RESOURCE_ATTRACTORS
	"Botanical Mesh            ",	// ASURA_RESOURCE_BOTANICAL_MESH
	"Temporary                 ",	// ASURA_RESOURCE_TEMPORARY
};
#endif // #if defined(ASURA_MEMORYBANK_ALLOW_STATS)


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::AllocateMemoryBlock
// Author       : Andy Tate
// Date         : 7-Feb-2006
//
// Description  : Allocates a resource of the specified type and size.
//
// Parameters   : The type of resource to allocate, a reference to the pointer
//				  to store the allocated data at, the size of data to allocate
//				  and the alignment to use when allocating.
//
// Return value : "true" if successful, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_ResourceAllocator::AllocateMemoryBlock (void*& rpvData,
												   const u_int uNumEntries,
												   const u_int uSizeOfEntry,
												   const ASURA_ALIGN_ID eAlignment)
{
	const u_int	uNumBytes	= uNumEntries * uSizeOfEntry;

	// Validation...
	Asura_Assert(IsResourceStackInUse(), "Invalid resource-stack index, have you remember to push your resource-type before allocating?");

	// Attempt the allocation...
	if(uNumBytes == 0)
	{
		// No data to allocate...
		rpvData	= NULL;
		return(true);
	}
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else if(IsUsingMemoryBank())
	{
		const ASURA_RESOURCE_TYPE		eResource	= s_aeResourceStack[s_iResourceStackIndex];
		const Asura_MemoryBank::Handle	xHandle		= s_pxCurrentMemoryBank->Allocate(uNumEntries, uSizeOfEntry, eResource, eAlignment);

		// Allocate from the memory-bank...
		rpvData	= s_pxCurrentMemoryBank->GetAddress(xHandle);
		return(true);
	}
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else
	{
		// No memory-bank - allocate normally...
		rpvData	= Asura_AllocateAlign(uNumBytes, eAlignment);
		return(true);
	}
}

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



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::DeallocateMemoryBlock
// Author       : Andy Tate
// Date         : 7-Feb-2006
//
// Description  : Deallocates a resource of the specified type and size.
//
// Parameters   : The type of resource to deallocate and a pointer to the data
//				  we're going to deallocate.
//
// Return value : "true" if successful, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_ResourceAllocator::DeallocateMemoryBlock (void*& rpvData)
{
	// Validation...
	Asura_Assert(IsResourceStackInUse(), "Invalid resource-stack index, have you remember to push your resource-type before deallocating?");

	// Attempt the deallocation...
	if(rpvData == NULL)
	{
		// Nothing to deallocate - return failure...
		return(false);
	}
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else if(IsUsingMemoryBank())
	{
		const ASURA_RESOURCE_TYPE		eResource	= s_aeResourceStack[s_iResourceStackIndex];
		const Asura_MemoryBank::Handle	xHandle		= s_pxCurrentMemoryBank->GetHandle(rpvData);

		// Deallocate from the memory-block...
		s_pxCurrentMemoryBank->Deallocate(eResource, xHandle);
		rpvData	= NULL;
		return(true);
	}
#endif	//  defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else
	{
		// No memory-bank - deallocate normally...
		Asura_DeallocateAlign(rpvData);
		rpvData	= NULL;
		return(true);
	}
}

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


#if defined(ASURA_MEMORYMANAGEMENT_ENABLED)

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::AllocateMemoryBlock
// Author       : Andy Tate
// Date         : 7-Feb-2006
//
// Description  : Allocates a resource of the specified type and size.
//
// Parameters   : The type of resource to allocate, a reference to the pointer
//				  to store the allocated data at, the size of data to allocate
//				  and the alignment to use when allocating.
//
// Return value : "true" if successful, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_ResourceAllocator::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)
{
	const u_int	uNumBytes	= uNumEntries * uSizeOfEntry;

	// Validation...
	Asura_Assert(IsResourceStackInUse(), "Invalid resource-stack index, have you remember to push your resource-type before allocating?");

	// Attempt the allocation...
	if(uNumBytes == 0)
	{
		// No data to allocate...
		rpvData	= NULL;
		return(true);
	}
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else if(IsUsingMemoryBank())
	{
		const ASURA_RESOURCE_TYPE		eResource	= s_aeResourceStack[s_iResourceStackIndex];
		const Asura_MemoryBank::Handle	xHandle		= s_pxCurrentMemoryBank->Allocate(uNumEntries, uSizeOfEntry, eResource, eAlignment, szFile, iLine, uReturnAddr);

		// Use a memory-bank instead...
		rpvData	= s_pxCurrentMemoryBank->GetAddress(xHandle);
		return(true);
	}
#endif	// defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else
	{
		// No memory-bank - allocate normally...
		rpvData	= Asura_AllocateAlign(uNumBytes, eAlignment, szFile, iLine, uReturnAddr);
		return(true);
	}
}

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



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::DeallocateMemoryBlock
// Author       : Andy Tate
// Date         : 7-Feb-2006
//
// Description  : Deallocates a resource of the specified type and size.
//
// Parameters   : The type of resource to deallocate and a pointer to the data
//				  we're going to deallocate.
//
// Return value : "true" if successful, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_ResourceAllocator::DeallocateMemoryBlock (void*& rpvData,
													 const char* szFile,
													 const int iLine)
{
	// Validation...
	Asura_Assert(IsResourceStackInUse(), "Invalid resource-stack index, have you remember to push your resource-type before deallocating?");

	// Attempt the allocation...
	if(rpvData == NULL)
	{
		// Nothing to deallocate - return failure...
		return(false);
	}
#if defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else if(IsUsingMemoryBank())
	{
		const ASURA_RESOURCE_TYPE		eResource	= s_aeResourceStack[s_iResourceStackIndex];
		const Asura_MemoryBank::Handle	xHandle		= s_pxCurrentMemoryBank->GetHandle(rpvData);

		// Deallocate from the memory-block...
		s_pxCurrentMemoryBank->Deallocate(eResource, xHandle, szFile, iLine);
		rpvData	= NULL;
		return(true);
	}
#endif	//  defined(ASURA_RESOURCE_ALLOCATOR_USES_MEMORY_BANK)
	else
	{
		// No memory-bank - deallocate normally...
		Asura_DeallocateAlign(rpvData, szFile, iLine);
		rpvData	= NULL;
		return(true);
	}
}

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

#endif	// defined(ASURA_MEMORYMANAGEMENT_ENABLED)


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::PushResource
// Author       : Andy Tate
// Date         : 22-Feb-2006
//
// Description  : Pushes the current resource onto the resource-stack, preserving
//				  the previous resource types.
//
// Parameters   : The type of resource to use.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_ResourceAllocator::PushResource (const ASURA_RESOURCE_TYPE eResource)
{
	// Do we have room on the stack?
	if(s_iResourceStackIndex < (ASURA_MAX_RESOURCE_STACK_ENTRIES - 1))
	{
		// Yes - stack this value...
		s_aeResourceStack[++s_iResourceStackIndex]	= eResource;
	}
	else
	{
		// No - report this...
		Asura_Assert(false, "Run out of resource stack space - is a resource being pushed but not popped?");
	}
}

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



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::PopResource
// Author       : Andy Tate
// Date         : 22-Feb-2006
//
// Description  : Pops the top resource from the resource-stack.
//
// Return value : The type of resource popped from the stack.
////////////////////////////////////////////////////////////////////////////////

ASURA_RESOURCE_TYPE Asura_ResourceAllocator::PopResource (void)
{
	// Do we still have entries to pop?
	if(s_iResourceStackIndex >= 0)
	{
		// Yes - pop the value from the stack...
		return(s_aeResourceStack[s_iResourceStackIndex--]);
	}
	else
	{
		// No - report this...
		Asura_Assert(false, "Run out of resource stack space - is a resource being popped but not pushed?");
		return(ASURA_RESOURCE_UNSPECIFIED);
	}
}

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



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ResourceAllocator::GetCurrentResourceType
// Author       : Richard May
// Date         : 14-Dec-2006
//
// Description  : Gets the resource type currently at the top of the resource-stack.
//
// Return value : The type of resource on the top of the stack.
////////////////////////////////////////////////////////////////////////////////

ASURA_RESOURCE_TYPE Asura_ResourceAllocator::GetCurrentResourceType (void)
{
	if(s_iResourceStackIndex >= 0)
	{
		return(s_aeResourceStack[s_iResourceStackIndex]);
	}
	else
	{
		return(ASURA_RESOURCE_UNSPECIFIED);
	}
}

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


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


/// End of file. ///
