#include "../../Direct3D/Engine/RuResourceManagerD3D.h"

// Rune base renderer core
#include "../../Engine/Base/RuShaderBase.h"
#include "../../Engine/Base/RuMaterialBase.h"
#include "../../Engine/Base/RuInstanceManagerBase.h"
#include "../../Engine/Geometry/RuMeshBase.h"

#include "../../Engine/Animation/RuAnimBase.h"

// Rune base entity system core
#include "../../Scene/Base/RuEntityBase.h"
#include "../../Scene/Base/RuEntityACT.h"

#include <tchar.h>
#include "../../../Tools/FilePackage/cFilePackage.h"

#pragma unmanaged

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

CRuResourceManagerD3D::CRuRenderTargetPool::CRuRenderTargetPool(IRuEngine *engine, INT32 poolSize, INT32 rtSize, RuSurfaceFormat rtFormat)
{
	// Store pool parameters
	m_poolSize = poolSize;
	m_rtSize = rtSize;
	m_rtFormat = rtFormat;

	// Construct pool
	for(INT32 i = 0; i < m_poolSize; ++i)
	{
		// Create a render target with the specified size and format
		IRuTexture *renderTarget = engine->CreateRenderTarget(m_rtSize, m_rtSize, 1, m_rtFormat);

		if(renderTarget)
		{
			// Push render target to the stack of free render targets
			m_freeRenderTargets.Push(renderTarget);
		}
		else
		{
			// Throw some exception?
		}
	}
}

CRuResourceManagerD3D::CRuRenderTargetPool::~CRuRenderTargetPool()
{
	// Release all render targets in the free pool
	while(m_freeRenderTargets.IsEmpty() == FALSE)
	{
		// Release the render target
		m_freeRenderTargets.Pop()->Release();
	}

	// Release all render targets in the used pool
	while(m_usedRenderTargets.Count() > 0)
	{
		// Release the render target
		m_usedRenderTargets[0]->Release();

		// Remove the render target entry
		m_usedRenderTargets.RemoveAt(0);
	}
}

BOOL CRuResourceManagerD3D::CRuRenderTargetPool::IsIdenticalPoolType(INT32 rtSize, RuSurfaceFormat rtFormat)
{
	if(m_rtSize == rtSize && m_rtFormat == rtFormat)
	{
		return TRUE;
	}

	return FALSE;
}

INT32 CRuResourceManagerD3D::CRuRenderTargetPool::GetMatchScore(INT32 rtSize, RuSurfaceFormat rtFormat)
{
	// If there are no free render targets, return a score of zero
	if(m_freeRenderTargets.IsEmpty())
	{
		return 0;
	}

	// If the pool is a perfect match, return a score of 100
	if(m_rtSize == rtSize && m_rtFormat == rtFormat)
	{
		return 100;
	}

	return 50;
}

IRuTexture *CRuResourceManagerD3D::CRuRenderTargetPool::GetRenderTarget()
{
	// If there are no free render targets, return NULL
	if(m_freeRenderTargets.IsEmpty())
	{
		return NULL;
	}

	// Pop a free render target
	IRuTexture *rt = m_freeRenderTargets.Pop();
	m_usedRenderTargets.Add(rt);

	return rt;
}

BOOL CRuResourceManagerD3D::CRuRenderTargetPool::FreeRenderTarget(IRuTexture *renderTarget)
{
	// Find the given render target and place it in the free pool
	for(INT32 i = 0; i < m_usedRenderTargets.Count(); ++i)
	{
		if(m_usedRenderTargets[i] == renderTarget)
		{
			m_freeRenderTargets.Push(renderTarget);
			m_usedRenderTargets.RemoveAt(i);

			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuResourceManagerD3D::CRuRenderTargetPool::ResetRenderTargetPool()
{
	// Put every render target back into the free pool
	for(INT32 i = 0; i < m_usedRenderTargets.Count(); ++i)
	{
		m_freeRenderTargets.Push(m_usedRenderTargets[i]);
	}

	m_usedRenderTargets.Clear();

	return TRUE;
}

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

CRuResourceManagerD3D::CRuResourceManagerD3D()
{
	// Create file manager
	m_filePackage = ruNEW cFilePackage();

	// Create resource dictionary hash tables
	m_textureHash = ruNEW CRuStringHash<IRuBaseTexture *>(4096);
	m_shaderHash = ruNEW CRuStringHash<IRuShader *>(2048);
	m_animationHash = ruNEW CRuStringHash<CRuAnimation *>(4096);
	m_entityHash = ruNEW CRuStringHash<IRuObject *>(4096);

	// Create object-level critical section
	m_objectCS = ruNEW CRuThread_CriticalSection();
}

CRuResourceManagerD3D::~CRuResourceManagerD3D()
{
	// Release render target pools
	for(INT32 i = 0; i < m_renderTargetPools.Count(); ++i)
	{
		delete m_renderTargetPools[i];
	}

	// Release textures held by the dictionary
	ClearTextureDictionary();

	// Release shaders held by the dictionary
	ClearShaderDictionary();

	// Release animations held by the dictionary
	ClearAnimationDictionary();

	// Release entities held by the dictionary
	ClearEntityDictionary();

	// Release file manager
	delete m_filePackage;

	// Release resource dictionaries
	delete m_textureHash;
	delete m_shaderHash;
	delete m_animationHash;
	delete m_entityHash;

	// Destroy object-level critical section
	delete m_objectCS;
}

BOOL CRuResourceManagerD3D::SetEngine(CRuEngineD3D *engine)
{
	// Acquire critical section
	m_objectCS->Enter();

	// Set engine reference
	m_engine = engine;

	// Release critical section
	m_objectCS->Leave();

	return TRUE;
}

IRuEngine *CRuResourceManagerD3D::GetEngine()
{
	return m_engine;
}

void CRuResourceManagerD3D::SetRootDirectory(const char *rootDirectory)
{
	// Acquire critical section
	m_objectCS->Enter();

	if(m_filePackage)
	{
		delete m_filePackage;
	}

	m_filePackage = ruNEW cFilePackage();
	m_filePackage->SetDirectory(rootDirectory);

	// Release critical section
	m_objectCS->Leave();
}

BOOL CRuResourceManagerD3D::AddTexture(IRuBaseTexture *texture)
{
	if(texture)
	{
		if(!m_textureHash->ContainsKey(texture->GetTextureName()))
		{
			// Increment texture reference count
			texture->AddRef();

			// Acquire critical section
			m_objectCS->Enter();

			// Add texture to the dictionary
			m_textureHash->Add(texture->GetTextureName(), texture);

			// Release critical section
			m_objectCS->Leave();

			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuResourceManagerD3D::RemoveTexture(IRuBaseTexture *texture)
{
	if(texture)
	{
		IRuBaseTexture *removedTexture = NULL;

		// Acquire critical section
		m_objectCS->Enter();

		if(m_textureHash->Get(texture->GetTextureName(), removedTexture))
		{
			// Remove texture from the dictionary
			m_textureHash->Remove(removedTexture->GetTextureName());

			// Release texture reference
			removedTexture->Release();

			// Release critical section
			m_objectCS->Leave();

			return TRUE;
		}

		// Release critical section
		m_objectCS->Leave();
	}

	return FALSE;
}

BOOL CRuResourceManagerD3D::RemoveTexture(const char *textureName)
{
	if(textureName)
	{
		IRuBaseTexture *removedTexture = NULL;

		// Acquire critical section
		m_objectCS->Enter();

		if(m_textureHash->Get(textureName, removedTexture))
		{
			// Remove texture from the dictionary
			m_textureHash->Remove(removedTexture->GetTextureName());

			// Release texture reference
			removedTexture->Release();

			// Release critical section
			m_objectCS->Leave();

			return TRUE;
		}

		// Release critical section
		m_objectCS->Leave();
	}

	return FALSE;
}

IRuTexture *CRuResourceManagerD3D::LoadTexture(const char *textureName)
{
	if(m_engine && textureName)
	{
		// Make a copy of the texture name
		CRuString canonicalTextureName(textureName);

		INT32 lastSlashIndex = canonicalTextureName.LastIndexOf("\\");
		INT32 lastPeriodIndex = canonicalTextureName.LastIndexOf(".");

		if(lastPeriodIndex > lastSlashIndex)
		{
			// Strip extension from the texture name if necessary
			canonicalTextureName.TruncateAt(lastPeriodIndex);
		}

		// Acquire critical section
		m_objectCS->Enter();

		// Get the texture from the dictionary if possible
		if(m_textureHash->ContainsKey(static_cast<const char *>(canonicalTextureName)))
		{
			// Get texture from hash table and increment its reference
			IRuBaseTexture *texture;
			m_textureHash->Get(static_cast<const char *>(canonicalTextureName), texture);
			texture->AddRef();

			// Release critical section
			m_objectCS->Leave();

			if(texture->GetType().IsSubClassOf(IRuTexture::Type) == FALSE)
			{
				texture->Release();
				texture = NULL;
			}

			return static_cast<IRuTexture *>(texture);
		}
		else
		{
			IRuStream *stream = NULL;
			cFileObj *fileObj = NULL;

			// Iterate through all texture file extensions
			const INT32 numTexExtensions = 4;
			const char *texExtensions[] = { ".dds", ".tga", ".png", ".bmp" };

			for(INT32 i = 0; i < numTexExtensions; ++i)
			{
				canonicalTextureName.Append(texExtensions[i]);

				stream = m_filePackage->GetIRuStream(static_cast<const char *>(canonicalTextureName));

				canonicalTextureName.TruncateAt(canonicalTextureName.LastIndexOf("."));

				if(stream)
				{
					break;
				}
			}

			fileObj = (cFileObj *) stream;
			if(fileObj)
			{
				if(fileObj->GetType() == FILE_TYPE_IMAGE)
				{
					cImgFile *imgObj = (cImgFile *) fileObj;
					IDirect3DTexture9 *d3dTexture = NULL;

					INT32 levels = max(1, imgObj->GetMipmapLevels());

					switch(imgObj->GetImageFormat())
					{
						case IMAGE_R5G6B5:
							m_engine->GetD3DDevice()->CreateTexture(imgObj->GetXSize(), imgObj->GetYSize(), levels, 0, D3DFMT_R5G6B5, D3DPOOL_MANAGED, &d3dTexture, NULL);
							break;

						case IMAGE_A1R5G5B5:
							m_engine->GetD3DDevice()->CreateTexture(imgObj->GetXSize(), imgObj->GetYSize(), levels, 0, D3DFMT_A1R5G5B5, D3DPOOL_MANAGED, &d3dTexture, NULL);
							break;

						case IMAGE_R8G8B8:
							m_engine->GetD3DDevice()->CreateTexture(imgObj->GetXSize(), imgObj->GetYSize(), levels, 0, D3DFMT_R8G8B8, D3DPOOL_MANAGED, &d3dTexture, NULL);
							break;

						case IMAGE_A8R8G8B8:
							m_engine->GetD3DDevice()->CreateTexture(imgObj->GetXSize(), imgObj->GetYSize(), levels, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &d3dTexture, NULL);
							break;

						case IMAGE_DXT1:
						case IMAGE_DXT1a:
							m_engine->GetD3DDevice()->CreateTexture(imgObj->GetXSize(), imgObj->GetYSize(), levels, 0, D3DFMT_DXT1, D3DPOOL_MANAGED, &d3dTexture, NULL);
							break;

						case IMAGE_DXT3:
							m_engine->GetD3DDevice()->CreateTexture(imgObj->GetXSize(), imgObj->GetYSize(), levels, 0, D3DFMT_DXT3, D3DPOOL_MANAGED, &d3dTexture, NULL);
							break;

						case IMAGE_DXT5:
							m_engine->GetD3DDevice()->CreateTexture(imgObj->GetXSize(), imgObj->GetYSize(), levels, 0, D3DFMT_DXT5, D3DPOOL_MANAGED, &d3dTexture, NULL);
							break;
					}

					if(d3dTexture)
					{
						switch(imgObj->GetImageFormat())
						{
							case IMAGE_R5G6B5:
							case IMAGE_A1R5G5B5:
							case IMAGE_R8G8B8:
							case IMAGE_A8R8G8B8:
								break;

							case IMAGE_DXT1:
							case IMAGE_DXT1a:
							case IMAGE_DXT3:
							case IMAGE_DXT5:
								{
									INT32 xSize = imgObj->GetXSize();
									INT32 ySize = imgObj->GetYSize();

									for(INT32 i = 0; i < levels; ++i)
									{
										D3DLOCKED_RECT lockedRect;

										if(d3dTexture->LockRect(i, &lockedRect, NULL, 0) == D3D_OK)
										{
											imgObj->BltToDXT(lockedRect.pBits, imgObj->GetImageFormat(), 0, 0, xSize, ySize, i);

											d3dTexture->UnlockRect(i);
										}

										xSize /= 2;
										ySize /= 2;
									}
								}

								break;
						}

						delete stream;

						// Create texture object
						CRuTextureD3D *texture = ruNEW CRuTextureD3D(d3dTexture, FALSE);
						texture->SetEngine(m_engine);
						texture->SetTextureName(static_cast<const char *>(canonicalTextureName));

						// Insert texture into dictionary
						this->AddTexture(texture);

						// Release critical section
						m_objectCS->Leave();

						return texture;
					}
				}

				delete stream;
			}
			else
			{
				char outputStr[2048];
				sprintf(outputStr, "Cannot load texture %s.\n", textureName);
				OutputDebugStr(outputStr);
			}
		}

		// Release critical section
		m_objectCS->Leave();
	}

	return NULL;
}

IRuTexture *CRuResourceManagerD3D::LoadTexture(const char *textureName, RuSurfaceFormat surfaceFormat)
{
	IRuTexture *texture = LoadTexture(textureName);

	if(texture)
	{
		if(texture->GetSurfaceFormat() != surfaceFormat)
		{
			IRuTexture *convertedTexture = CreateTexture(texture->GetTextureName(), texture->GetLevelWidth(0), texture->GetLevelHeight(0), texture->GetLevelCount(), surfaceFormat);

			for(UINT32 i = 0; i < texture->GetLevelCount(); ++i)
			{
				IDirect3DSurface9 *srcSurface;
				IDirect3DSurface9 *dstSurface;

				static_cast<CRuTextureD3D *>(texture)->GetIDirect3DTexture()->GetSurfaceLevel(i, &srcSurface);
				static_cast<CRuTextureD3D *>(convertedTexture)->GetIDirect3DTexture()->GetSurfaceLevel(i, &dstSurface);

				D3DXLoadSurfaceFromSurface(dstSurface, NULL, NULL, srcSurface, NULL, NULL, D3DX_FILTER_TRIANGLE, 0);

				srcSurface->Release();
				dstSurface->Release();
			}

			// Remove existing texture from the dictionary
			this->RemoveTexture(texture);
			ruSAFE_RELEASE(texture);

			// Insert texture into dictionary
			texture = convertedTexture;
			this->AddTexture(texture);
		}
	}

	return texture;
}

IRuTexture *CRuResourceManagerD3D::CreateTexture(const char *textureName, UINT32 width, UINT32 height, RuSurfaceFormat surfaceFormat)
{
	return CreateTexture(textureName, width, height, 0, surfaceFormat);
}

IRuTexture *CRuResourceManagerD3D::CreateTexture(const char *textureName, UINT32 width, UINT32 height, UINT32 levels, RuSurfaceFormat surfaceFormat)
{
	IDirect3DTexture9 *d3dTexture = NULL;

	// Attempt to construct the texture
	if(m_engine->GetD3DDevice()->CreateTexture(width, height, levels, 0, (D3DFORMAT) surfaceFormat, D3DPOOL_MANAGED, &d3dTexture, NULL) == D3D_OK)
	{
		// Create texture object
		CRuTextureD3D *texture = ruNEW CRuTextureD3D(d3dTexture, FALSE);
		texture->SetEngine(m_engine);
		texture->SetTextureName(textureName);

		// Acquire critical section
		m_objectCS->Enter();

		// Insert texture into dictionary
		this->AddTexture(texture);

		// Release critical section
		m_objectCS->Leave();

		return texture;
	}

	return NULL;
}

IRuVolumeTexture *CRuResourceManagerD3D::CreateVolumeTexture(const char *textureName, UINT32 width, UINT32 height, UINT32 depth, UINT32 levels, RuSurfaceFormat surfaceFormat)
{
	IDirect3DVolumeTexture9 *d3dTexture = NULL;

	// Attempt to construct the texture
	if(m_engine->GetD3DDevice()->CreateVolumeTexture(width, height, depth, levels, 0, (D3DFORMAT) surfaceFormat, D3DPOOL_MANAGED, &d3dTexture, NULL) == D3D_OK)
	{
		// Create texture object
		CRuVolumeTextureD3D *texture = ruNEW CRuVolumeTextureD3D(d3dTexture, FALSE);
		texture->SetEngine(m_engine);
		texture->SetTextureName(textureName);

		// Acquire critical section
		m_objectCS->Enter();

		// Insert texture into dictionary
		this->AddTexture(texture);

		// Release critical section
		m_objectCS->Leave();

		return texture;
	}

	return NULL;
}

BOOL CRuResourceManagerD3D::CreateRenderTargetPool(INT32 poolSize, INT32 rtSize, RuSurfaceFormat rtFormat)
{
	// Acquire critical section
	m_objectCS->Enter();

	// Make sure there are no identical pools that already exist
	for(INT32 i = 0; i < m_renderTargetPools.Count(); ++i)
	{
		if(m_renderTargetPools[i]->IsIdenticalPoolType(rtSize, rtFormat))
		{
			// Release critical section
			m_objectCS->Leave();

			return FALSE;
		}
	}

	// Create pool
	CRuRenderTargetPool *rtPool = ruNEW CRuRenderTargetPool(m_engine, poolSize, rtSize, rtFormat);

	if(rtPool)
	{
		m_renderTargetPools.Add(rtPool);

		// Release critical section
		m_objectCS->Leave();

		return TRUE;
	}

	// Release critical section
	m_objectCS->Leave();

	return FALSE;
}

BOOL CRuResourceManagerD3D::ResetRenderTargetPools()
{
	// Acquire critical section
	m_objectCS->Enter();

	for(INT32 i = 0; i < m_renderTargetPools.Count(); ++i)
	{
		if(m_renderTargetPools[i]->ResetRenderTargetPool() == FALSE)
		{
			// Release critical section
			m_objectCS->Leave();

			return FALSE;
		}
	}

	// Release critical section
	m_objectCS->Leave();

	return TRUE;
}

IRuTexture *CRuResourceManagerD3D::GetPooledRenderTarget(INT32 rtSize, RuSurfaceFormat rtFormat)
{
	INT32 highestMatchScore = 0;
	INT32 highestMatchPoolIndex = 0;

	// Acquire critical section
	m_objectCS->Enter();

	// Iterate through all render target pools to find the best match
	for(INT32 i = 0; i < m_renderTargetPools.Count(); ++i)
	{
		INT32 matchScore = m_renderTargetPools[i]->GetMatchScore(rtSize, rtFormat);

		if(matchScore > highestMatchScore)
		{
			highestMatchScore = matchScore;
			highestMatchPoolIndex = i;
		}
	}

	// If there is a match, return a render target from that pool
	if(highestMatchScore > 0)
	{
		// Release critical section
		m_objectCS->Leave();

		return m_renderTargetPools[highestMatchPoolIndex]->GetRenderTarget();
	}

	// Release critical section
	m_objectCS->Leave();

	return NULL;
}

BOOL CRuResourceManagerD3D::FreePooledRenderTarget(IRuTexture *renderTarget)
{
	// Acquire critical section
	m_objectCS->Enter();

	for(INT32 i = 0; i < m_renderTargetPools.Count(); ++i)
	{
		if(m_renderTargetPools[i]->FreeRenderTarget(renderTarget))
		{
			// Release critical section
			m_objectCS->Leave();

			return TRUE;
		}
	}

	// Release critical section
	m_objectCS->Leave();

	return FALSE;
}

BOOL CRuResourceManagerD3D::AddShader(IRuShader *shader)
{
	if(shader)
	{
		// Acquire critical section
		m_objectCS->Enter();

		if(!m_shaderHash->ContainsKey(shader->GetShaderName()))
		{
			// Increment shader reference count
			shader->AddRef();

			// Add shader to the dictionary
			m_shaderHash->Add(shader->GetShaderName(), shader);

			// Release critical section
			m_objectCS->Leave();

			return TRUE;
		}

		// Release critical section
		m_objectCS->Leave();
	}

	return FALSE;
}

BOOL CRuResourceManagerD3D::RemoveShader(IRuShader *shader)
{
	if(shader)
	{
		IRuShader *removedShader = NULL;

		// Acquire critical section
		m_objectCS->Enter();

		if(m_shaderHash->Get(shader->GetShaderName(), removedShader))
		{
			// Remove shader from the dictionary
			m_shaderHash->Remove(removedShader->GetShaderName());

			// Release shader reference
			removedShader->Release();

			// Release critical section
			m_objectCS->Leave();

			return TRUE;
		}

		// Release critical section
		m_objectCS->Leave();
	}

	return FALSE;
}

BOOL CRuResourceManagerD3D::RemoveShader(const char *shaderName)
{
	if(shaderName)
	{
		IRuShader *removedShader = NULL;

		// Acquire critical section
		m_objectCS->Enter();

		if(m_shaderHash->Get(shaderName, removedShader))
		{
			// Remove shader from the dictionary
			m_shaderHash->Remove(removedShader->GetShaderName());

			// Release shader reference
			removedShader->Release();

			// Release critical section
			m_objectCS->Leave();

			return TRUE;
		}

		// Release critical section
		m_objectCS->Leave();
	}

	return FALSE;
}

IRuShader *CRuResourceManagerD3D::GetShader(const char *shaderName)
{
	if(shaderName)
	{
		IRuShader *shader = NULL;

		// Acquire critical section
		m_objectCS->Enter();

		if(m_shaderHash->Get(shaderName, shader))
		{
			// Release critical section
			m_objectCS->Leave();

			return shader;
		}

		// Release critical section
		m_objectCS->Leave();
	}

	return NULL;
}

BOOL CRuResourceManagerD3D::GetShaderList(CRuArrayList<const char *> &shaderListOut)
{
	// Acquire critical section
	m_objectCS->Enter();

	// Iterate through all shaders in the shader dictionary
	m_shaderHash->ForAllElements(GetShaderListCallback, &shaderListOut);

	// Release critical section
	m_objectCS->Leave();

	return TRUE;
}

CRuAnimation *CRuResourceManagerD3D::LoadAnimation(const char *animationName)
{
	CRuAnimation *animation = NULL;

	if(animationName)
	{
		// Acquire critical section
		m_objectCS->Enter();

		if(m_animationHash->Get(animationName, animation))
		{
			animation->AddRef();

			// Release critical section
			m_objectCS->Leave();

			return animation;
		}

		IRuStream *srcStream = m_filePackage->GetIRuStream(animationName);

		if(srcStream)
		{
			animation = LoadRuAnimation(srcStream);

			if(animation)
			{
				animation->AddRef();
				m_animationHash->Add(animationName, animation);
			}

			delete srcStream;
		}

		// Release critical section
		m_objectCS->Leave();
	}

	return animation;
}

CRuEntity *CRuResourceManagerD3D::LoadEntity(const char *entityName)
{
	IRuObject *object = NULL;

	if(entityName == NULL)
	{
		return NULL;
	}

	// Acquire critical section
	m_objectCS->Enter();

	// Look for the entity in the hash
	if(m_entityHash->Get(entityName, object))
	{
		// Release critical section
		m_objectCS->Leave();

		// Custom processing based on object type
		if(object->GetType().IsSubClassOf(CRuEntity::Type))
		{
			// Return a clone of the entity
			IRuObject *clone = object->Clone();

			if(clone->GetType().IsSubClassOf(CRuEntity::Type))
				return static_cast<CRuEntity *>(clone);
			else
				return NULL;
		}
		else if(object->GetType().IsSubClassOf(CRuACTTemplate::Type))
		{
			// Construct an object from the ACT template and return it
			return static_cast<CRuACTTemplate *>(object)->Construct();
		}
		else if(object->GetType().IsSubClassOf(CRuPaperdollTemplate::Type))
		{
			// Construct an object from the paperdoll template and return it
			return static_cast<CRuPaperdollTemplate *>(object)->Construct();
		}
	}

	// Attempt to retrieve the named stream from the data store
	IRuStream *stream = m_filePackage->GetIRuStream(entityName);

	if(stream)
	{
		// Create object using the object factory
		IRuObject *entityObject = g_ruObjectFactory.CreateObject(stream);

		// Destroy the data stream
		delete stream;

		if(entityObject)
		{
			// Set the object's resource name
			entityObject->SetResourceName(entityName);

			if(entityObject->GetType().IsSubClassOf(CRuEntity::Type))
			{
				// Add entity to the entity hash
				CRuEntity *entity = static_cast<CRuEntity *>(entityObject);
				m_entityHash->Add(entityName, entity);

				// Release critical section
				m_objectCS->Leave();

				// Return a clone of the entity
				IRuObject *clone = entity->Clone();

				// If the clone is valid, return it
				if(clone && clone->GetType().IsSubClassOf(CRuEntity::Type))
					return static_cast<CRuEntity *>(clone);

				// Release clone
				ruSAFE_RELEASE(clone);

				return NULL;
			}
			else if(entityObject->GetType() == CRuACTTemplate::Type)
			{
				// Construct an object from the ACT template
				CRuEntity *entity = static_cast<CRuACTTemplate *>(entityObject)->Construct();

				// Add template to the entity hash
				m_entityHash->Add(entityName, entityObject);

				// Release critical section
				m_objectCS->Leave();

				// Return the constructed ACT entity
				return entity;
			}
			else if(entityObject->GetType() == CRuPaperdollTemplate::Type)
			{
				// Construct an object from the paperdoll template
				CRuEntity *entity = static_cast<CRuPaperdollTemplate *>(entityObject)->Construct();

				// Add template to the entity hash
				m_entityHash->Add(entityName, entityObject);

				// Release critical section
				m_objectCS->Leave();

				// Return the constructed paperdoll entity
				return entity;
			}
			else
			{
				// Unrecognized object, release it and return NULL
				entityObject->Release();

				// Release critical section
				m_objectCS->Leave();

				return NULL;
			}
		}
	}

	// Release critical section
	m_objectCS->Leave();

	// Could not find object in the hash and could not read from data store, return NULL
	return NULL;
}

BOOL CRuResourceManagerD3D::ClearTextureDictionary()
{
	// Acquire critical section
	m_objectCS->Enter();

	// Release all textures held in the dictionary
	m_textureHash->ForAllElements(ClearTextureDictionaryCallback, this);

	// Clear all texture entries
	m_textureHash->Clear();

	// Release critical section
	m_objectCS->Leave();

	return TRUE;
}

BOOL CRuResourceManagerD3D::ClearShaderDictionary()
{
	// Acquire critical section
	m_objectCS->Enter();

	// Release all Shaders held in the dictionary
	m_shaderHash->ForAllElements(ClearShaderDictionaryCallback, this);

	// Clear all Shader entries
	m_shaderHash->Clear();

	// Release critical section
	m_objectCS->Leave();

	return TRUE;
}

BOOL CRuResourceManagerD3D::ClearAnimationDictionary()
{
	// Acquire critical section
	m_objectCS->Enter();

	// Release all animations held in the dictionary
	m_animationHash->ForAllElements(ClearAnimationDictionaryCallback, this);

	// Clear all animation entries
	m_animationHash->Clear();

	// Release critical section
	m_objectCS->Leave();

	return TRUE;
}

BOOL CRuResourceManagerD3D::ClearEntityDictionary()
{
	// Acquire critical section
	m_objectCS->Enter();

	// Release all entities held in the dictionary
	m_entityHash->ForAllElements(ClearEntityDictionaryCallback, this);

	// Clear all entity entries
	m_entityHash->Clear();

	// Release critical section
	m_objectCS->Leave();

	return TRUE;
}

BOOL CRuResourceManagerD3D::GarbageCollect()
{
	// Acquire critical section
	m_objectCS->Enter();

	m_gc_Texture.Clear();
	m_textureHash->ForAllElements(GCTextureDictionaryCallback, this);
	for(INT32 i = 0; i < m_gc_Texture.Count(); ++i)
	{
//		m_textureHash->Remove(
	}

	// Release critical section
	m_objectCS->Leave();

	return TRUE;
}

BOOL CRuResourceManagerD3D::GetShaderListCallback(const void *key, const void *data, void *userData)
{
	CRuArrayList<const char *> *shaderList = (CRuArrayList<const char *> *) userData;

	shaderList->Add((*((IRuShader **) data))->GetShaderName());

	return TRUE;
}

BOOL CRuResourceManagerD3D::ClearTextureDictionaryCallback(const void *key, const void *data, void *userData)
{
	// Release texture
	(*((IRuTexture **) data))->Release();

	return TRUE;
}

BOOL CRuResourceManagerD3D::ClearShaderDictionaryCallback(const void *key, const void *data, void *userData)
{
	// Release Shader
	(*((IRuShader **) data))->Release();

	return TRUE;
}

BOOL CRuResourceManagerD3D::ClearAnimationDictionaryCallback(const void *key, const void *data, void *userData)
{
	// Release animation
	(*((CRuAnimation **) data))->Release();

	return TRUE;
}

BOOL CRuResourceManagerD3D::ClearEntityDictionaryCallback(const void *key, const void *data, void *userData)
{
	// Release object
	(*((IRuObject **) data))->Release();

	return TRUE;
}

BOOL CRuResourceManagerD3D::GCTextureDictionaryCallback(const void *key, const void *data, void *userData)
{
	IRuTexture *texture = (*((IRuTexture **) data));

	// If the texture's reference count is one, place it into the GC list
	if(texture->GetRefCount() <= 1)
	{
//		m_gc_Texture.Add(texture);		
	}

	return TRUE;
}

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

#pragma managed
