#include "../../Engine/Manager/RuEngine_ResourceManager.h"

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

#include "../../Engine/Texture/RuTexture_Proxy.h"
#include "../../Engine/Texture/RuTextureUtility.h"

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

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

#include "../../Helper/RuHelper_Entity.h"
#include "../../Helper/RuHelper_IRuEntity_Renderable.h"

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

#pragma managed(push, off)

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

ruRTTI_DEFINE_SUBCLASS(CRuEngine_ResourceManager, IRuEngine_ResourceManager)
ruHEAP_DEFINE_SUBCLASS(CRuEngine_ResourceManager, "CRuEngine_ResourceManager", "IRuEngine_ResourceManager")
ruCLASSGUID_DEFINE(CRuEngine_ResourceManager, 0xEC2A9DFE, 0x2D7E4dc6, 0x9EA267B9, 0x7E6F5AC5)

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

CRuEngine_ResourceManager::CRuRenderTargetPool::CRuRenderTargetPool(IRuEngine *engine, INT32 rtWidth, INT32 rtHeight, RuSurfaceFormat rtFormat)
{
	// Store pool parameters
	m_rtWidth = rtWidth;
	m_rtHeight = rtHeight;
	m_rtFormat = rtFormat;
}

CRuEngine_ResourceManager::CRuRenderTargetPool::~CRuRenderTargetPool()
{
	ClearRenderTargetPool();
}

BOOL CRuEngine_ResourceManager::CRuRenderTargetPool::IsIdenticalPoolType(INT32 rtWidth, INT32 rtHeight, RuSurfaceFormat rtFormat)
{
	if(m_rtWidth == rtWidth && m_rtHeight == rtHeight && m_rtFormat == rtFormat)
	{
		return TRUE;
	}

	return FALSE;
}

INT32 CRuEngine_ResourceManager::CRuRenderTargetPool::GetMatchScore(INT32 rtWidth, INT32 rtHeight, 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_rtWidth == rtWidth && m_rtHeight == rtHeight && m_rtFormat == rtFormat)
	{
		return 100;
	}

//	return 50;
	return 0;
}

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

	if(m_freeRenderTargets.IsEmpty())
	{
		IRuTexture *rt = g_ruEngine->CreateRenderTarget(m_rtWidth, m_rtHeight, 1, m_rtFormat);

		if(rt)
		{
			m_usedRenderTargets.Add(rt);
			return rt;
		}
		else
		{
			return NULL;
		}
	}

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

	return rt;
}

BOOL CRuEngine_ResourceManager::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 CRuEngine_ResourceManager::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;
}

BOOL CRuEngine_ResourceManager::CRuRenderTargetPool::ClearRenderTargetPool()
{
	// 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);
	}

	return TRUE;
}

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

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

	// Create resource dictionary hash tables
	m_streamTree = ruNEW CRuTernaryStringTree<IRuStream *>();
	m_textureHash = ruNEW CRuTernaryStringTree<TextureDesc>();
	m_animationHash = ruNEW CRuStringHash<CRuAnimation *>(4096);
	m_animationHash_GR2 = ruNEW CRuStringHash<CRuAnimation_GR2 *>(4096);
	m_entityHash = ruNEW CRuTernaryStringTree<ObjectDesc>();
	m_genericObjectTree = ruNEW CRuTernaryStringTree<IRuObject *>();

	// Create render target atlas
	m_rtAtlas = ruNEW CRuTextureAtlas_Aggregate();

	// Set auto-bind textures to true
	m_enableTextureLoading = TRUE;

	// Create critical sections
	m_filePackage_CS = ruNEW CRuThread_CriticalSection();

	// Create performance probes
	m_probe_TextureLoads = g_ruProfiler->CreateProbe("TexRds", "Engine");
	m_probe_TextureLoadFails = g_ruProfiler->CreateProbe("Failed TexRds", "Engine");
	m_probe_TextureLoadTransientFails = g_ruProfiler->CreateProbe("Failed TexRds [T]", "Engine");
	m_probe_EntityLoads = g_ruProfiler->CreateProbe("EntRds", "Engine");
	m_probe_EntityLoadFails = g_ruProfiler->CreateProbe("Failed EntRds", "Engine");
	m_probe_EntityLoadTransientFails = g_ruProfiler->CreateProbe("Failed EntRds [T]", "Engine");

	// Create work queues
	m_streamWorkQueue = ruNEW CRuThread_WorkQueue<char *, IRuStream *>();
	m_objectWorkQueue = ruNEW CRuThread_WorkQueue<char *, IRuObject *>();
	m_textureWorkQueue = ruNEW CRuThread_WorkQueue<char *, IRuBaseTexture *>();

	// Journal
	m_journalOn = FALSE;

	// Register worker function with worker thread
	g_ruGlobalWorkerThread->RegisterWorkerFunction(m_workerFunctionGUID, boost::bind(&CRuEngine_ResourceManager::WorkerFunction, this));
}

CRuEngine_ResourceManager::~CRuEngine_ResourceManager()
{
	// Unregister worker function
	g_ruGlobalWorkerThread->UnregisterWorkerFunction(m_workerFunctionGUID);

	// Unregister device reset handler
	if(m_engine)
	{
		m_engine->Event_DeviceReset().UnregisterHandler(this->GetGUID());
	}

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

	// Unpin all textures
	UnpinAllTextures();

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

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

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

	// Clear generic object dictionary
	ClearGenericObjectDictionary();

	// Release file manager
	delete m_filePackage;

	// Release resource dictionaries
	delete m_textureHash;
	delete m_animationHash;
	delete m_animationHash_GR2;
	delete m_entityHash;
	delete m_genericObjectTree;

	// Release render target atlas
	ruSAFE_RELEASE(m_rtAtlas);

	// Destroy critical sections
	delete m_filePackage_CS;

	// Destroy work queues
	char *orphanedStreamKey;
	IRuStream *orphanedStream;
	while(m_streamWorkQueue->DequeueResult(&orphanedStreamKey, orphanedStream))
	{
		delete[] orphanedStreamKey;
		delete orphanedStream;
	}

	delete m_streamWorkQueue;

	char *orphanedObjectKey;
	IRuObject *orphanedObject;
	while(m_objectWorkQueue->DequeueResult(&orphanedObjectKey, orphanedObject))
	{
		delete[] orphanedObjectKey;
		ruSAFE_RELEASE(orphanedObject);
	}

	delete m_objectWorkQueue;

	char *orphanedTextureKey;
	IRuBaseTexture *orphanedTexture;
	while(m_textureWorkQueue->DequeueResult(&orphanedTextureKey, orphanedTexture))
	{
		delete[] orphanedTextureKey;
		ruSAFE_RELEASE(orphanedTexture);
	}

	delete m_textureWorkQueue;
}
					
INT32 CRuEngine_ResourceManager::GetMemoryFootprint(CRuKeyHash<PTRVALUE> *enumHash)
{
	INT32 footprint = IRuEngine_ResourceManager::GetMemoryFootprint(enumHash);

	if(footprint < 0)
	{
		return -1;
	}

	m_temp_Footprint = 0;
	m_temp_EnumHash = enumHash;
	m_textureHash->ForAllElements(boost::bind(&CRuEngine_ResourceManager::TextureDictionaryMemoryFootprintCallback, this, _1, _2));
	m_entityHash->ForAllElements(boost::bind(&CRuEngine_ResourceManager::EntityDictionaryMemoryFootprintCallback, this, _1, _2));
	footprint += m_temp_Footprint;

	return footprint;
}

BOOL CRuEngine_ResourceManager::SetEngine(IRuEngine *engine)
{
	// Set engine reference
	m_engine = engine;

	// Schedule work queue processing function
	g_ruFunctionSchedule->ScheduleFunction(m_guid_ProcessWorkQueues, 0, 0, 0, boost::bind(&CRuEngine_ResourceManager::ProcessWorkQueues, this, _1), NULL);

	// Schedule garbage collect function
	g_ruFunctionSchedule->ScheduleFunction(m_guid_GarbageCollect, 0, 0, 2500, boost::bind(&CRuEngine_ResourceManager::GarbageCollect, this, _1), NULL);

	// Register event handlers on engine
	m_engine->Event_DeviceReset().RegisterHandler(this->GetGUID(), boost::bind(&CRuEngine_ResourceManager::DeviceReset_Handler, this, _1));

	return TRUE;
}

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

const char *CRuEngine_ResourceManager::GetRootDirectory()
{
	return m_filePackage->GetDirectory();
}

void CRuEngine_ResourceManager::SetRootDirectory(const char *rootDirectory)
{
	m_filePackage_CS->Enter();
	{
		if(m_filePackage)
		{
			delete m_filePackage;
		}

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

		m_filePackage->GetDirectory();
	}
	m_filePackage_CS->Leave();
}

void CRuEngine_ResourceManager::LoadPackageFile(const char *filename)
{
	m_filePackage_CS->Enter();
	{
		if ( m_filePackage )
		{
			char fullPath[2048];
			sprintf(fullPath, "%s%s", m_filePackage->GetDirectory(), filename);

			m_filePackage->LoadFromFile(fullPath);
		}
	}
	m_filePackage_CS->Leave();
}

BOOL CRuEngine_ResourceManager::FileExists(const char* filename)
{
	BOOL retVal = FALSE;

	m_filePackage_CS->Enter();
	{
		if(m_filePackage && m_filePackage->FileExists(filename))
		{
			retVal = TRUE;
		}
	}
	m_filePackage_CS->Leave();

	return retVal;
}

BOOL CRuEngine_ResourceManager::SetObjectDequeueRate(REAL dequeueRate)
{
	return TRUE;
}

BOOL CRuEngine_ResourceManager::SetTextureDequeueRate(REAL dequeueRate)
{
	return TRUE;
}

BOOL CRuEngine_ResourceManager::EnableTextureLoading(BOOL enableTextureLoading)
{
	m_enableTextureLoading = enableTextureLoading;
	return m_enableTextureLoading;
}

BOOL CRuEngine_ResourceManager::OpenTempFileStream(const char *resourceName, CRuFileStream &fileStream)
{
	if(m_filePackage)
	{
		return m_filePackage->GetCRuTempFileStream(resourceName, fileStream);
	}

	return NULL;
}

IRuStream *CRuEngine_ResourceManager::LoadStream(const char *resourceName)
{
	DWORD t0, t1, et;
	t0 = GetTickCount();

	IRuStream *stream = NULL;
	
	stream = LoadStream_Internal(resourceName);

	t1 = GetTickCount();
	et = t1 - t0;
	char outputStr[2048];
	sprintf(outputStr, "Stream %s loaded in %d ms.\n", resourceName, et);
	OutputDebugString(outputStr);

	return stream;
}

BOOL CRuEngine_ResourceManager::LoadStream(const char *resourceName, IRuStream **streamOut)
{
	BOOL retVal = FALSE;

	if(resourceName == NULL)
	{
		return FALSE;
	}

	// Look for the entity in the hash
	if(m_streamTree->Get(resourceName, *streamOut) == TRUE)
	{
		// Remove from stream tree
		m_streamTree->Remove(resourceName);

		// Return TRUE
		retVal = TRUE;
	}
	else
	{
		// Make sure the previous attempt to load the stream did not fail
		if(m_streamWorkQueue->WorkFailed(resourceName, FALSE) == FALSE)
		{
			// Queue texture for loading
			m_streamWorkQueue->QueueWork(resourceName);

			// Return TRUE
			retVal = TRUE;
		}
		else
		{
			// Previous load attempt failed
			retVal = FALSE;
		}
	}

	return retVal;
}

BOOL CRuEngine_ResourceManager::AddTexture(IRuBaseTexture *texture)
{
	if(texture)
	{
		TextureDesc texDesc;

		// Look for the texture in the dictionary
		if(m_textureHash->Get(texture->GetTextureName(), texDesc) == FALSE)
		{
			// Increment texture reference count
			texture->AddRef();

			// Setup texture descriptor
			texDesc.m_texture = texture;

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

			return TRUE;
		}
		else
		{
			char outputStr[2048];
			sprintf(outputStr, "ERROR: Attempting to add duplicate texture %s into dictionary.\n", texture->GetTextureName());
			OutputDebugString(outputStr);
		}
	}

	return FALSE;
}

BOOL CRuEngine_ResourceManager::RemoveTexture(IRuBaseTexture *texture)
{
	if(texture)
	{
		TextureDesc texDesc;

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

			// Release texture reference
			ruSAFE_RELEASE(texDesc.m_texture);

			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuEngine_ResourceManager::RemoveTexture(const char *textureName)
{
	if(textureName)
	{
		TextureDesc texDesc;

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

			// Release texture reference
			ruSAFE_RELEASE(texDesc.m_texture);

			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuEngine_ResourceManager::TextureExistsInCache(const char *textureName)
{
	RUPROFILE_SAMPLE_ACCUMULATE(m_probe_TextureLoads, 1, 1)

	if(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);
		}

		if(m_textureHash->ContainsKey(canonicalTextureName))
		{
			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuEngine_ResourceManager::EnableTextureGarbageCollection(IRuBaseTexture *texture, BOOL enableGC)
{
	if(texture)
	{
		TextureDesc texDesc;

		if(m_textureHash->Get(texture->GetTextureName(), texDesc))
		{
			// Set new GC option
			texDesc.m_disableGC = !enableGC;

			// Replace the texture descriptor
			m_textureHash->Set(texture->GetTextureName(), texDesc);

			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuEngine_ResourceManager::PinTexture(IRuBaseTexture *texture, BOOL pinTexture)
{
	if(texture)
	{
		TextureDesc texDesc;

		if(m_textureHash->Get(texture->GetTextureName(), texDesc))
		{
			// Set new pin option
			texDesc.m_pinned = pinTexture;

			// Replace the texture descriptor
			m_textureHash->Set(texture->GetTextureName(), texDesc);

			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuEngine_ResourceManager::PurgeTexture(IRuBaseTexture *texture)
{
	if(texture)
	{
		TextureDesc texDesc;

		if(m_textureHash->Get(texture->GetTextureName(), texDesc))
		{
			// Set GC age to an arbitrarily large value to force purging
			texDesc.m_gcAge = 1000000;

			// Replace the texture descriptor
			m_textureHash->Set(texture->GetTextureName(), texDesc);

			return TRUE;
		}
	}

	return FALSE;
}

IRuTexture *CRuEngine_ResourceManager::LoadTexture(const char *textureName, RuSurfaceFormat surfaceFormat, UINT32 maxMipLevel, BOOL async)
{
	IRuTexture *texture = NULL;

	// Locate existing texture
	if(textureName && textureName[0])
	{
		// 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);
		}

		// Look for the texture in the dictionary
		TextureDesc texDesc;

		if(m_textureHash->Get(canonicalTextureName, texDesc))
		{
			// Reset GC age and update object descriptor
			texDesc.m_gcAge = 0;
			m_textureHash->Set(canonicalTextureName, texDesc);

			if(texDesc.m_texture && texDesc.m_texture->GetType().IsSubClassOf(IRuTexture::Type()))
			{
				// Increment reference count on texture
				texDesc.m_texture->AddRef();

				// Fill out return parameters
				texture = static_cast<IRuTexture *>(texDesc.m_texture);
			}
		}

		if(texture == NULL)
		{
			if(async)
			{
				// Make sure that the load didn't fail before
				if(m_textureWorkQueue->WorkFailed(canonicalTextureName, FALSE) == FALSE)
				{
					// Allocate stub texture
					CRuTexture_Proxy *stubTexture = ruNEW CRuTexture_Proxy();
					stubTexture->Allocate(16, 16, 1, surfaceFormat == ruSURFACEFMT_DEFAULT ? ruSURFACEFMT_DXT1 : surfaceFormat);
					stubTexture->SetTextureName(canonicalTextureName);

					if(m_enableTextureLoading)
					{
						// Invalidate stub texture
						stubTexture->Invalidate();

						// Build a texture work entry
						TextureWorkEntry workEntry = { stubTexture, surfaceFormat, maxMipLevel };
						m_streamingTextures.Add(canonicalTextureName, workEntry);
						stubTexture->AddRef();

						// Queue texture for loading
						m_textureWorkQueue->QueueWork(canonicalTextureName);
					}

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

					// Return stub texture
					texture = stubTexture;
				}
			}
			else
			{
				if(m_enableTextureLoading)
				{
					// not yet done
					IRuTexture *srcTexture = LoadTexture_Internal(canonicalTextureName);

					if(srcTexture)
					{
						// Create new target texture
						CRuTexture_Proxy *stubTexture = ruNEW CRuTexture_Proxy();
						stubTexture->SetTextureName(canonicalTextureName);

						// Convert texture
						ConvertTexture_Internal(srcTexture, stubTexture, surfaceFormat, maxMipLevel);

						// Release source texture
						ruSAFE_RELEASE(srcTexture);

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

						// Return stub texture
						texture = stubTexture;
					}
				}
			}
		}
	}

	return texture;
}
/*
BOOL CRuEngine_ResourceManager::CreateRenderTargetPool(INT32 poolSize, INT32 rtSize, RuSurfaceFormat rtFormat)
{
	// 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))
		{
			return FALSE;
		}
	}

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

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

		return TRUE;
	}

	return FALSE;
}

BOOL CRuEngine_ResourceManager::ResetRenderTargetPools()
{
	for(INT32 i = 0; i < m_renderTargetPools.Count(); ++i)
	{
		if(m_renderTargetPools[i]->ResetRenderTargetPool() == FALSE)
		{
			return FALSE;
		}
	}

	return TRUE;
}

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

	// 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)
	{
		return m_renderTargetPools[highestMatchPoolIndex]->GetRenderTarget();
	}

	return NULL;
}

BOOL CRuEngine_ResourceManager::FreePooledRenderTarget(IRuTexture *renderTarget)
{
	for(INT32 i = 0; i < m_renderTargetPools.Count(); ++i)
	{
		if(m_renderTargetPools[i]->FreeRenderTarget(renderTarget))
		{
			return TRUE;
		}
	}

	return FALSE;
}
*/

IRuTexture *CRuEngine_ResourceManager::CreateRenderTarget(INT32 width, INT32 height, RuSurfaceFormat format)
{
	INT32 highestMatchScore = 0;
	INT32 highestMatchPoolIndex = 0;

	// 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(width, height, format);

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

	// If there is a match, return a render target from that pool
	if(highestMatchScore > 0)
	{
		return m_renderTargetPools[highestMatchPoolIndex]->GetRenderTarget();
	}
	else
	{
		// Create pool
		CRuRenderTargetPool *rtPool = ruNEW CRuRenderTargetPool(m_engine, width, height, format);

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

			return rtPool->GetRenderTarget();
		}
	}

	return NULL;
}

BOOL CRuEngine_ResourceManager::ResetRenderTargetPools()
{
	for(INT32 i = 0; i < m_renderTargetPools.Count(); ++i)
	{
		if(m_renderTargetPools[i]->ResetRenderTargetPool() == FALSE)
		{
			return FALSE;
		}
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::CreateRenderTargetSubAtlas(INT32 subAtlasSize, INT32 pageSize, RuSurfaceFormat rtFormat)
{
	// Create a render target texture for the sub atlas
	IRuTexture *subAtlasTexture = m_engine->CreateRenderTarget(subAtlasSize, subAtlasSize, 1, rtFormat);

	if(subAtlasTexture)
	{
		// Create the sub atlas
		CRuTextureAtlas_Paged *subAtlas = ruNEW CRuTextureAtlas_Paged();
		subAtlas->Initialize(subAtlasTexture, pageSize, pageSize, 0, 0);

		// Bind the sub atlas into the aggregate atlas
		m_rtAtlas->BindSubAtlas(subAtlas);

		// Release local reference to the sub atlas
		ruSAFE_RELEASE(subAtlas);

		// Release local reference to the sub atlas texture
		ruSAFE_RELEASE(subAtlasTexture);

		return TRUE;
	}

	return FALSE;
}

BOOL CRuEngine_ResourceManager::RestoreRenderTargetView(IRuTextureView *textureView)
{
	// Check to see if we can bind into the render target atlas
	if(m_rtAtlas->CanBindIntoAtlas(textureView))
	{
		INT32 textureHandle = m_rtAtlas->BindIntoAtlas(textureView, TRUE);

		if(textureHandle != -1)
		{
			// This should never, ever happen!
			if(textureView->GetTexture() == NULL)
			{
				return FALSE;
			}

			// Return TRUE to indicate binding success
			return TRUE;
		}
	}

	return FALSE;
}

BOOL CRuEngine_ResourceManager::ResetRenderTargetViews()
{
	// Increment eviction marker on the atlas
	INT32 nextEvictionMarker = m_rtAtlas->IncrementEvictionMarker();

	return TRUE;
}

CRuAnimation *CRuEngine_ResourceManager::LoadAnimation(const char *animationName)
{
	DWORD t0, t1, et;
	t0 = timeGetTime();

	CRuAnimation *animation = NULL;

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

			return animation;
		}

		IRuStream *srcStream = LoadStream_Internal(animationName);

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

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

			DestroyStream_Internal(srcStream);
		}

		t1 = timeGetTime();
		et = t1 - t0;
		char outputStr[256];
		sprintf(outputStr, "Animation %s loaded in %u ms.\n", animationName, et);
		OutputDebugString(outputStr);
	}

	return animation;
}

CRuAnimation_GR2 *CRuEngine_ResourceManager::LoadAnimation_GR2(const char *animationName)
{
	DWORD t0, t1, et;
	t0 = timeGetTime();

	CRuAnimation_GR2 *animation = NULL;

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

			return animation;
		}

		IRuStream *srcStream = LoadStream_Internal(animationName);

		if(srcStream)
		{
			animation = ruNEW CRuAnimation_GR2();

			if(animation->SerializeFrom(srcStream) == FALSE)
			{
				ruSAFE_RELEASE(animation);
			}
			else
			{
				animation->SetObjectName(animationName);
				animation->AddRef();
				m_animationHash_GR2->Add(animationName, animation);
			}

			DestroyStream_Internal(srcStream);
		}

		t1 = timeGetTime();
		et = t1 - t0;
		char outputStr[256];
		sprintf(outputStr, "Animation %s loaded in %u ms.\n", animationName, et);
		OutputDebugString(outputStr);
	}

	return animation;
}

BOOL CRuEngine_ResourceManager::AddEntity(const char *entityName, CRuEntity *entity)
{
	ObjectDesc objDesc;

	// Look for the entity in the hash
	if(m_entityHash->Get(entityName, objDesc) == FALSE)
	{
		entity->AddRef();
		objDesc.m_object = entity;

		// Set the object's resource name
		objDesc.m_object->SetResourceName(entityName);

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

		return TRUE;
	}

	return FALSE;
}

CRuEntity *CRuEngine_ResourceManager::LoadEntity(const char *entityName)
{
	RUPROFILE_SAMPLE_ACCUMULATE(m_probe_EntityLoads, 1, 1)

	ObjectDesc objDesc;
	CRuEntity *returnedEntity = NULL;

	if(entityName == NULL || entityName[0] == NULL)
	{
		return returnedEntity;
	}

//	entityName = "model\\element\\common\\prop\\missingbox\\missingbox.ros";

	// Look for the entity in the hash
	if(m_entityHash->Get(entityName, objDesc) == FALSE)
	{
		DWORD t0, t1, et;
		t0 = GetTickCount();

		IRuStream *stream = LoadStream_Internal(entityName);

		t1 = GetTickCount();
		et = t1 - t0;
		char outputStr[2048];
		sprintf(outputStr, "Texture stream %s loaded in %d ms.\n", entityName, et);
		OutputDebugString(outputStr);


		// Attempt to retrieve the named stream from the data store
//		IRuStream *stream = LoadStream_Internal(entityName);

		if(stream)
		{
			// Create object using the object factory
			objDesc.m_object = RuObjectFactory().CreateObject(stream);

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

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

				// Register event handler for object clone on the template object
				objDesc.m_object->Event_Cloned().RegisterHandler(this->GetGUID(), boost::bind(&CRuEngine_ResourceManager::HandleEvent_ObjectCloned, this, _1));
			}

			// Destroy the data stream
			DestroyStream_Internal(stream);
		}
	}

	// Custom processing based on object type
	if(objDesc.m_object)
	{
		// Reset GC age and update object descriptor
		objDesc.m_gcAge = 0;
		m_entityHash->Set(entityName, objDesc);

		if(objDesc.m_object->GetType().IsSubClassOf(CRuEntity::Type()))
		{
			// Enumerate all materials
			CRuArrayList<IRuMaterial *> materialList(64);
			RuEntity_EnumerateMaterials(static_cast<CRuEntity *>(objDesc.m_object), materialList);

			// Bind textures for all materials
			for(INT32 i = 0; i < materialList.Count(); ++i)
			{
				// Bind textures synchronously
				materialList[i]->BindTextures(FALSE);
			}

			// Return a clone of the entity
			returnedEntity = static_cast<CRuEntity *>(objDesc.m_object->Clone());

			// Register event handler for object dispose on the instanced(cloned) object
			returnedEntity->Event_Dispose().RegisterHandler(this->GetGUID(), boost::bind(&CRuEngine_ResourceManager::HandleEvent_ObjectDisposed, this, _1));
		}
		else if(objDesc.m_object->GetType().IsSubClassOf(CRuACTTemplate::Type()))
		{
			// Preload all dependencies for the ACT template synchronously
			static_cast<CRuACTTemplate *>(objDesc.m_object)->PreloadDependencies(FALSE);

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

	if(returnedEntity)
	{
		RuEntity_UpdateSkinShaders(returnedEntity);
	}
	else
	{
		RUPROFILE_SAMPLE_ACCUMULATE(m_probe_EntityLoadFails, 1, 1)
	}

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

BOOL CRuEngine_ResourceManager::LoadEntity(const char *entityName, CRuEntity **entityOut)
{
	RUPROFILE_SAMPLE_ACCUMULATE(m_probe_EntityLoads, 1, 1)

	BOOL retVal = FALSE;
	ObjectDesc objDesc;

	if(entityName == NULL || entityName[0] == NULL)
	{
		return FALSE;
	}

//	entityName = "model\\element\\common\\prop\\missingbox\\missingbox.ros";

	// Clear output parameters
	*entityOut = NULL;

	// Look for the entity in the hash
	if(m_entityHash->Get(entityName, objDesc) == FALSE)
	{
		// Make sure the previous attempt to load the object did not fail
		if(m_objectWorkQueue->WorkFailed(entityName, FALSE) == FALSE)
		{
			RUPROFILE_SAMPLE_ACCUMULATE(m_probe_EntityLoadTransientFails, 1, 1)

			// Queue Object for loading
			m_objectWorkQueue->QueueWork(entityName);

			// Mark return value as true
			retVal = TRUE;
		}
		else
		{
			RUPROFILE_SAMPLE_ACCUMULATE(m_probe_EntityLoadFails, 1, 1)
		}
	}

	// Custom processing based on object type
	if(objDesc.m_object)
	{
		// Reset GC age and update object descriptor
		objDesc.m_gcAge = 0;
		m_entityHash->Set(entityName, objDesc);

		if(objDesc.m_object->GetType().IsSubClassOf(CRuEntity::Type()))
		{
			// Enumerate all materials
			CRuArrayList<IRuMaterial *> materialList(64);
			RuEntity_EnumerateMaterials(static_cast<CRuEntity *>(objDesc.m_object), materialList);

			// Ensure that all materials have had their textures bound
			BOOL allTexturesAreBound = TRUE;

			for(INT32 i = 0; i < materialList.Count(); ++i)
			{
				// Bind textures asynchronously
				allTexturesAreBound = allTexturesAreBound & materialList[i]->BindTextures(TRUE);
			}

			// Wait until all textures are bound
			if(allTexturesAreBound)
			{
				// Return a clone of the entity
				*entityOut = static_cast<CRuEntity *>(objDesc.m_object->Clone());

				// Register event handler for object dispose on the instanced(cloned) object
				(*entityOut)->Event_Dispose().RegisterHandler(this->GetGUID(), boost::bind(&CRuEngine_ResourceManager::HandleEvent_ObjectDisposed, this, _1));
			}
			else
			{
				// We are waiting for all textures to be loaded, return true
				retVal = TRUE;
			}
		}
		else if(objDesc.m_object->GetType().IsSubClassOf(CRuACTTemplate::Type()))
		{
			// Check dependencies and make sure they are loaded
			if(static_cast<CRuACTTemplate *>(objDesc.m_object)->PreloadDependencies(TRUE))
			{
				// Construct an object from the ACT template and return it
				*entityOut = static_cast<CRuACTTemplate *>(objDesc.m_object)->Construct();
			}
			else
			{
				// We are waiting for all dependencies to be loaded, return true
				retVal = TRUE;
			}
		}
		else if(objDesc.m_object->GetType().IsSubClassOf(CRuPaperdollTemplate::Type()))
		{
			// Construct an object from the paperdoll template and return it
			*entityOut = static_cast<CRuPaperdollTemplate *>(objDesc.m_object)->Construct();
		}
	}

	if(*entityOut)
	{
		retVal = TRUE;
	}

	return retVal;
}

BOOL CRuEngine_ResourceManager::AddGenericObject(const char *objectName, IRuObject *obj)
{
	IRuObject *existingObject = NULL;

	// Make sure the object does not already exist in the dictionary
	if(m_genericObjectTree->Get(objectName, existingObject) == FALSE)
	{
		// Add object to the dictionary
		obj->AddRef();
		m_genericObjectTree->Add(objectName, obj);

		return TRUE;
	}

	return FALSE;
}

IRuObject *CRuEngine_ResourceManager::GetGenericObject(const char *objectName)
{
	IRuObject *existingObject = NULL;

	// Look for generic object in the dictionary
	m_genericObjectTree->Get(objectName, existingObject);

	if(existingObject)
	{
		existingObject->AddRef();
	}

	return existingObject;
}

BOOL CRuEngine_ResourceManager::ClearTextureDictionary()
{
	// Clear pinned texture descriptor list
	m_temp_TextureDescList.Clear();

	// Release all textures held in the dictionary
	m_textureHash->ForAllElements(boost::bind(&CRuEngine_ResourceManager::ClearTextureDictionaryCallback, this, _1, _2));

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

	// Add pinned texture descriptors back into hash
	for(INT32 i = 0; i < m_temp_TextureDescList.Count(); ++i)
	{
		// Add texture to hash and set its GC & pin settings
		AddTexture(m_temp_TextureDescList[i].m_texture);
		EnableTextureGarbageCollection(m_temp_TextureDescList[i].m_texture, m_temp_TextureDescList[i].m_disableGC);
		PinTexture(m_temp_TextureDescList[i].m_texture, m_temp_TextureDescList[i].m_pinned);

		// Release local reference to the texture
		ruSAFE_RELEASE(m_temp_TextureDescList[i].m_texture);
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::ClearAnimationDictionary()
{
	// Release all animations held in the dictionary
	m_animationHash->ForAllElements(ClearAnimationDictionaryCallback, this);

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

	return TRUE;
}

BOOL CRuEngine_ResourceManager::ClearAnimationDictionary_GR2()
{
	// Release all animations held in the dictionary
	m_animationHash_GR2->ForAllElements(ClearAnimationDictionaryCallback_GR2, this);

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

	return TRUE;
}

BOOL CRuEngine_ResourceManager::ClearEntityDictionary()
{
	// Release all entities held in the dictionary
	m_entityHash->ForAllElements(boost::bind(&CRuEngine_ResourceManager::ClearEntityDictionaryCallback, this, _1, _2));

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

	return TRUE;
}

BOOL CRuEngine_ResourceManager::ClearGenericObjectDictionary()
{
	// Release all generic objects held in the dictionary
	m_genericObjectTree->ForAllElements(boost::bind(&CRuEngine_ResourceManager::ClearGenericObjectDictionaryCallback, this, _1, _2));

	// Clear all entries
	m_genericObjectTree->Clear();

	return TRUE;
}

BOOL CRuEngine_ResourceManager::GarbageCollect()
{
	// Back-track...
	m_lastGCTime -= 100000;

	// Forcibly run garbage collect
	GarbageCollect(0);

	return TRUE;
}

BOOL CRuEngine_ResourceManager::IsObjectWorkQueueEmpty()
{
	return (m_objectWorkQueue->WorkCount() == 0) && (m_objectWorkQueue->ResultCount() == 0);
}

BOOL CRuEngine_ResourceManager::IsTextureWorkQueueEmpty()
{
	return (m_textureWorkQueue->WorkCount() == 0) && (m_textureWorkQueue->ResultCount() == 0);
}

BOOL CRuEngine_ResourceManager::BeginJournal(const char *journalName)
{
	m_journalOn = TRUE;
	m_journalName = journalName;
	m_journal.Clear();

	return TRUE;
}

BOOL CRuEngine_ResourceManager::EndJournal()
{
	m_journalOn = FALSE;

	CRuFileStream journalStream;
	journalStream.Create(m_journalName);

	// Write journal
	m_journalStream = &journalStream;
	m_journal.ForAllElements(boost::bind(&CRuEngine_ResourceManager::EndJournalCallback, this, _1, _2));

	journalStream.Close();

	return TRUE;
}

BOOL CRuEngine_ResourceManager::OutputResourceManifest()
{
	char moot[2048];

	OutputDebugString("----------------\n");
	OutputDebugString("Texture Manifest\n");
	OutputDebugString("----------------\n");
	m_temp_Footprint = 0;
	m_textureHash->ForAllElements(boost::bind(&CRuEngine_ResourceManager::TextureManifestCallback, this, _1, _2));

	sprintf(moot, "Total Texture Footprint: %d\n", m_temp_Footprint);
	OutputDebugString(moot);

	OutputDebugString("---------------\n");
	OutputDebugString("Entity Manifest\n");
	OutputDebugString("---------------\n");
	m_temp_Footprint = 0;
	m_temp_Footprint2 = 0;
	CRuKeyHash<PTRVALUE> hash(4096);
	m_temp_EnumHash = &hash;
	m_entityHash->ForAllElements(boost::bind(&CRuEngine_ResourceManager::EntityManifestCallback, this, _1, _2));

	sprintf(moot, "Total Mesh Footprint: %d\n", m_temp_Footprint);
	OutputDebugString(moot);

	sprintf(moot, "Total ColStruct Footprint: %d\n", m_temp_Footprint2);
	OutputDebugString(moot);

	return TRUE;
}

BOOL CRuEngine_ResourceManager::HandleEvent_ObjectCloned(RuEventArgs *eventArgs)
{
	RuObjectEvent_Dispose_Args *disposeArgs = static_cast<RuObjectEvent_Dispose_Args *>(eventArgs);
	const char *entityName = disposeArgs->m_object->GetResourceName();

	if(entityName)
	{
		ObjectDesc objDesc;

		if(m_entityHash->Get(entityName, objDesc))
		{
			// Increment clone count
			objDesc.m_cloneCount = (objDesc.m_cloneCount < 0) ? 1 : objDesc.m_cloneCount + 1;

			// Update object descriptor
			m_entityHash->Set(entityName, objDesc);
		}
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::HandleEvent_ObjectDisposed(RuEventArgs *eventArgs)
{
	RuObjectEvent_Cloned_Args *clonedArgs = static_cast<RuObjectEvent_Cloned_Args *>(eventArgs);
	const char *entityName = clonedArgs->m_object->GetResourceName();

	if(entityName)
	{
		ObjectDesc objDesc;

		if(m_entityHash->Get(entityName, objDesc))
		{
			if(objDesc.m_cloneCount >= 0)
			{
				// Decrement clone count
				--objDesc.m_cloneCount;
			}
			else
			{
				// Do nothing, this object is not a cloned object
			}

			// Update object descriptor
			m_entityHash->Set(entityName, objDesc);
		}
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::ProcessWorkQueues(PTRVALUE userData)
{
	// Process the preloaded streams queue
	char *dequeuedStreamKey = NULL;
	IRuStream *dequeuedStream = NULL;
	while(m_streamWorkQueue->DequeueResult(&dequeuedStreamKey, dequeuedStream))
	{
		// Add preloaded stream to the stream tree
		m_streamTree->Add(dequeuedStreamKey, dequeuedStream);

		// Delete the stream key
		delete[] dequeuedStreamKey;
	}

	// Process the object work queue
	char *dequeuedObjectKey = NULL;
	IRuObject *dequeuedObject = NULL;

	while(m_objectWorkQueue->DequeueResult(&dequeuedObjectKey, dequeuedObject))
	{
		delete[] dequeuedObjectKey;

		// Add preloaded object to the entity hash
		ObjectDesc objDesc;
		objDesc.m_object = dequeuedObject;
		m_entityHash->Add(dequeuedObject->GetResourceName(), objDesc);

		// Register event handler for object clone on the template object
		objDesc.m_object->Event_Cloned().RegisterHandler(this->GetGUID(), boost::bind(&CRuEngine_ResourceManager::HandleEvent_ObjectCloned, this, _1));
	}

	// Process the texture work queue
	char *dequeuedTextureKey = NULL;
	IRuBaseTexture *dequeuedTexture = NULL;

	while(m_textureWorkQueue->DequeueResult(&dequeuedTextureKey, dequeuedTexture))
	{
		TextureWorkEntry texWorkEntry = m_streamingTextures.Get(dequeuedTextureKey);
		m_streamingTextures.Remove(dequeuedTextureKey);

		CRuTexture_Proxy *stubTexture = texWorkEntry.m_stubTexture;
		RuSurfaceFormat surfaceFormat = texWorkEntry.m_surfaceFormat;
		INT32 maxMipLevel = texWorkEntry.m_maxMipLevel;

		if(dequeuedTexture)
		{
			// Perform texture conversion
			ConvertTexture_Internal(static_cast<IRuTexture *>(dequeuedTexture), stubTexture, surfaceFormat, maxMipLevel);

			// Release local reference to texture
			ruSAFE_RELEASE(dequeuedTexture);
		}
		else
		{
			// Discard the stub texture
			stubTexture->Discard();

			// Mark texture load as failed
			m_textureWorkQueue->MarkWorkFailed(dequeuedTextureKey);
		}

		// Free the texture key
		ruSAFE_DELETE_ARRAY(dequeuedTextureKey);

		// Release reference to stub texture
		ruSAFE_RELEASE(stubTexture);
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::GarbageCollect(PTRVALUE userData)
{
/*
	char outputStr[2048];
	sprintf(outputStr, "GC Status [obj %d, tex %d]\n", m_objectWorkQueue->ResultCount(), m_textureWorkQueue->ResultCount());
	OutputDebugString(outputStr);
*/
	DWORD t = timeGetTime();
	m_timeSinceLastGC = t - m_lastGCTime;

	// Garbage collect stale objects
	m_gc_Object.Clear();
	m_entityHash->ForAllElements(boost::bind(&CRuEngine_ResourceManager::GCEntityDictionaryCallback, this, _1, _2));
	for(INT32 i = 0; i < m_gc_Object.Count(); ++i)
	{
/*
		char outputStr[2048];
		sprintf(outputStr, "GC %s\n", m_gc_Object[i]->GetResourceName());
		OutputDebugString(outputStr);
*/
		m_entityHash->Remove(m_gc_Object[i]->GetResourceName());
		ruSAFE_RELEASE(m_gc_Object[i]);
	}

	// Garbage collect stale textures....
	m_gc_Texture.Clear();
	m_textureHash->ForAllElements(boost::bind(&CRuEngine_ResourceManager::GCTextureDictionaryCallback, this, _1, _2));
	for(INT32 i = 0; i < m_gc_Texture.Count(); ++i)
	{
/*
		char outputStr[2048];
		sprintf(outputStr, "GC %s\n", m_gc_Texture[i]->GetTextureName());
		OutputDebugString(outputStr);
*/
		m_textureHash->Remove(m_gc_Texture[i]->GetTextureName());
		ruSAFE_RELEASE(m_gc_Texture[i]);
	}

/*
	char outputStr[2048];
	sprintf(outputStr, "GC [%d textures @ %d] [%d objects @ %d]\n", m_gc_Texture.Count(), et0, m_gc_Object.Count(), et1);
	OutputDebugString(outputStr);
*/
	// Update last GC time
	m_lastGCTime = t;

	return TRUE;
}

BOOL CRuEngine_ResourceManager::TextureDictionaryMemoryFootprintCallback(const void *key, void *data)
{
	IRuBaseTexture *texture = *reinterpret_cast<IRuBaseTexture **>(data);

	if(texture)
	{
		m_temp_Footprint += RuTextureUtility_GetMemoryFootprint(texture, m_temp_EnumHash);
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::EntityDictionaryMemoryFootprintCallback(const void *key, void *data)
{
	ObjectDesc &objDesc = *reinterpret_cast<ObjectDesc *>(data);

	if(objDesc.m_object)
	{
		m_temp_Footprint += objDesc.m_object->GetMemoryFootprint(m_temp_EnumHash);
	}

	return TRUE;
}

void CRuEngine_ResourceManager::UnpinAllTextures()
{
	m_textureHash->ForAllElements(boost::bind(&CRuEngine_ResourceManager::UnpinAllTexturesCallback, this, _1, _2));
}

BOOL CRuEngine_ResourceManager::UnpinAllTexturesCallback(const void *key, void *data)
{
	TextureDesc *texDesc = reinterpret_cast<TextureDesc *>(data);
	texDesc->m_pinned = FALSE;
	return TRUE;
}

BOOL CRuEngine_ResourceManager::ClearTextureDictionaryCallback(const void *key, void *data)
{
	TextureDesc *texDesc = reinterpret_cast<TextureDesc *>(data);

	if(texDesc->m_pinned)
	{
		// Store texture descriptor into the list of pinned textures
		m_temp_TextureDescList.Add(*texDesc);
	}
	else
	{
		// Release the texture
		ruSAFE_RELEASE(texDesc->m_texture);
	}

	return TRUE;
}

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

	return TRUE;
}

BOOL CRuEngine_ResourceManager::ClearAnimationDictionaryCallback_GR2(const void *key, const void *data, void *userData)
{
	// Release animation
	(*((CRuAnimation_GR2 **) data))->Release();

	return TRUE;
}

BOOL CRuEngine_ResourceManager::ClearEntityDictionaryCallback(const void *key, void *data)
{
	ObjectDesc *objDesc = reinterpret_cast<ObjectDesc *>(data);

	ruSAFE_RELEASE(objDesc->m_object);

	return TRUE;
}

BOOL CRuEngine_ResourceManager::ClearGenericObjectDictionaryCallback(const void *key, void *data)
{
	IRuObject *objDesc = *reinterpret_cast<IRuObject **>(data);

	ruSAFE_RELEASE(objDesc);

	return TRUE;
}

BOOL CRuEngine_ResourceManager::GCTextureDictionaryCallback(const void *key, void *data)
{
	TextureDesc *texDesc = reinterpret_cast<TextureDesc *>(data);

	// If the texture's reference count is one, place it into the GC list
	if(texDesc->m_texture)
	{
		if(texDesc->m_texture->GetRefCount() <= 1 && !texDesc->m_disableGC)
		{
			// Update GC age
			texDesc->m_gcAge += m_timeSinceLastGC;

			if(texDesc->m_gcAge > 20000)
			{
				m_gc_Texture.Add(texDesc->m_texture);
			}
		}
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::GCEntityDictionaryCallback(const void *key, void *data)
{
	ObjectDesc *objDesc = reinterpret_cast<ObjectDesc *>(data);

	if(objDesc->m_cloneCount == 0)
	{
		objDesc->m_gcAge += m_timeSinceLastGC;

		if(objDesc->m_gcAge > 20000)
		{
			m_gc_Object.Add(objDesc->m_object);
		}
	}
	else if(objDesc->m_cloneCount < 0)
	{
		if(objDesc->m_object->GetRefCount() == 1)
		{
			objDesc->m_gcAge += m_timeSinceLastGC;

			if(objDesc->m_gcAge > 20000)
			{
				m_gc_Object.Add(objDesc->m_object);
			}
		}
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::TextureManifestCallback(const void *key, void *data)
{
	TextureDesc *texDesc = reinterpret_cast<TextureDesc *>(data);

	// If the texture's reference count is one, place it into the GC list
	if(texDesc->m_texture)
	{
		INT32 textureWidth = 0;
		INT32 textureHeight = 0;
		INT32 surfFormat = 0;
		INT32 bytesThisTexture = 0;

		if(texDesc->m_texture->GetType().IsSubClassOf(IRuTexture::Type()))
		{
			IRuTexture *texture = static_cast<IRuTexture *>(texDesc->m_texture);

			textureWidth = texture->GetLevelWidth(0);
			textureHeight = texture->GetLevelHeight(0);
			surfFormat = texture->GetSurfaceFormat();

			for(UINT32 i = 0; i < texture->GetLevelCount(); ++i)
			{
				INT32 bitsPerTexel = RuSurfaceFormat_GetBitsPerTexel(texture->GetSurfaceFormat());
				INT32 bytesThisLevel = texture->GetLevelWidth(i) * texture->GetLevelHeight(i) * bitsPerTexel / 8;
				bytesThisTexture += bytesThisLevel;
			}
		}

		m_temp_Footprint += bytesThisTexture;

		char moo[2048];
		sprintf(moo, "REF: %d, %s, Size: %d, %dx%d\n", texDesc->m_texture->GetRefCount(), texDesc->m_texture->GetTextureName(), bytesThisTexture, textureWidth, textureHeight);
		OutputDebugString(moo);
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::EntityManifestCallback(const void *key, void *data)
{
	ObjectDesc *objDesc = reinterpret_cast<ObjectDesc *>(data);

	INT32 footprint = objDesc->m_object->GetMemoryFootprint(m_temp_EnumHash);

	char moo[2048];
	sprintf(moo, "REF: %d, CLONE: %d, SIZE: %d, %s\n", objDesc->m_object->GetRefCount(), objDesc->m_cloneCount, footprint, objDesc->m_object->GetResourceName());
	OutputDebugString(moo);

	if(objDesc->m_object->GetType().IsSubClassOf(CRuEntity::Type()))
	{
		m_temp_Footprint += RuEntity_GetMeshFootprint(static_cast<CRuEntity *>(objDesc->m_object));
		m_temp_Footprint2 += RuEntity_GetExternalColStructFootprint(static_cast<CRuEntity *>(objDesc->m_object));
	}

	return TRUE;
}

IRuStream *CRuEngine_ResourceManager::LoadStream_Internal(const char *streamName)
{
	m_filePackage_CS->Enter();

	IRuStream *stream = m_filePackage->GetIRuStream(streamName);

	if(m_journalOn)
	{
		m_journal.Add(streamName, 0);
	}

	m_filePackage_CS->Leave();

	return stream;
}

BOOL CRuEngine_ResourceManager::DestroyStream_Internal(IRuStream *stream)
{
	m_filePackage_CS->Enter();

	delete stream;

	m_filePackage_CS->Leave();

	return TRUE;
}

RuSurfaceFormat CRuEngine_ResourceManager::GetImageSurfaceFormat(IRuStream *imgStream)
{
	INT32 numImageFormats = 8;
	INT32 imageFormatTable[][2] =	{ 
										{ IMAGE_R5G6B5,		ruSURFACEFMT_R5G6G5 },
										{ IMAGE_A1R5G5B5,	ruSURFACEFMT_A1R5G5B5 },
										{ IMAGE_R8G8B8,		ruSURFACEFMT_R8G8B8 },
										{ IMAGE_A8R8G8B8,	ruSURFACEFMT_A8R8G8B8 },
										{ IMAGE_DXT1,		ruSURFACEFMT_DXT1 },
										{ IMAGE_DXT1a,		ruSURFACEFMT_DXT1 },
										{ IMAGE_DXT3,		ruSURFACEFMT_DXT3 },
										{ IMAGE_DXT5,		ruSURFACEFMT_DXT5 }
									};

	cImgFile *imgFile = static_cast<cImgFile *>(imgStream);

	for(INT32 i = 0; i < numImageFormats; ++i)
	{
		if(imgFile->GetImageFormat() == imageFormatTable[i][0])
		{
			return static_cast<RuSurfaceFormat>(imageFormatTable[i][1]);
		}
	}

	return ruSURFACEFMT_DEFAULT;
}

INT32 CRuEngine_ResourceManager::GetImageMipLevels(IRuStream *imgStream)
{
	cImgFile *imgFile = static_cast<cImgFile *>(imgStream);
	INT32 levels = max(1, imgFile->GetMipmapLevels());

	return levels;
}

IRuTexture *CRuEngine_ResourceManager::CreateTextureToImageSpec(IRuStream *imgStream)
{
	cImgFile *imgFile = static_cast<cImgFile *>(imgStream);
	INT32 levels = GetImageMipLevels(imgStream);

	RuSurfaceFormat surfFmt = GetImageSurfaceFormat(imgStream);

	if(surfFmt != ruSURFACEFMT_DEFAULT)
	{
		CRuTexture_Proxy *texture = ruNEW CRuTexture_Proxy();
		texture->Allocate(imgFile->GetXSize(), imgFile->GetYSize(), levels, surfFmt);
		return texture;
	}

	return NULL;
}

BOOL CRuEngine_ResourceManager::CopyImageToTexture(IRuStream *imgStream, IRuTexture *texture)
{
	cImgFile *imgFile = static_cast<cImgFile *>(imgStream);
	BOOL retVal = FALSE;

	switch(imgFile->GetImageFormat())
	{
		case IMAGE_R5G6B5:
		case IMAGE_A1R5G5B5:
		case IMAGE_R8G8B8:
			break;

		case IMAGE_A8R8G8B8:
			{
				INT32 levels = max(1, imgFile->GetMipmapLevels());
				INT32 xSize = imgFile->GetXSize();
				INT32 ySize = imgFile->GetYSize();

				for(INT32 i = 0; i < levels; ++i)
				{
					void *levelBits;
					INT32 levelPitch;

					if(texture->Lock(i, &levelBits, &levelPitch))
					{
						imgFile->BltToRGB(levelBits, imgFile->GetImageFormat(), 0, 0, xSize, ySize, levelPitch, i);

						texture->Unlock(i);
					}

					xSize /= 2;
					ySize /= 2;
				}

				retVal = TRUE;
			}

			break;

		case IMAGE_DXT1:
		case IMAGE_DXT1a:
		case IMAGE_DXT3:
		case IMAGE_DXT5:
			{
				INT32 levels = max(1, imgFile->GetMipmapLevels());
				INT32 xSize = imgFile->GetXSize();
				INT32 ySize = imgFile->GetYSize();

				for(INT32 i = 0; i < levels; ++i)
				{
					void *levelBits;
					INT32 levelPitch;

					if(texture->Lock(i, &levelBits, &levelPitch))
					{
						// Make sure the surface area is still at least 4x4 (the size of a DXT block) - anything smaller makes no sense
						if(xSize >= 4 && ySize >= 4)
						{
							imgFile->BltToDXT(levelBits, imgFile->GetImageFormat(), 0, 0, xSize, ySize, i);
						}

						texture->Unlock(i);
					}

					xSize /= 2;
					ySize /= 2;
				}

				retVal = TRUE;
			}

			break;
	}

	return retVal;
}

IRuTexture *CRuEngine_ResourceManager::LoadTexture_Internal(const char *textureName)
{
	IRuTexture *texture = NULL;
	IRuStream *stream = NULL;
	CRuString canonicalTextureName(textureName);

	// 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 = LoadStream_Internal(static_cast<const char *>(canonicalTextureName));

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

		if(stream)
		{
			break;
		}
	}

	if(stream)
	{
		cFileObj *fileObj = (cFileObj *) stream;

		if(fileObj->GetType() == FILE_TYPE_IMAGE)
		{
			cImgFile *imgObj = (cImgFile *) fileObj;

			texture = CreateTextureToImageSpec(imgObj);

			if(texture)
			{
				// Set texture name
				texture->SetTextureName(static_cast<const char *>(canonicalTextureName));

				// Copy image contents to texture
				CopyImageToTexture(imgObj, texture);
			}
		}

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

	return texture;
}

BOOL CRuEngine_ResourceManager::ConvertTexture_Internal(IRuTexture *srcTexture, CRuTexture_Proxy *dstTexture, RuSurfaceFormat surfaceFormat, INT32 maxMipLevel)
{
	// Stub processing
	if(maxMipLevel >= static_cast<INT32>(srcTexture->GetLevelCount()))
	{
		char moo[2048];
		sprintf(moo, "Insufficient mip levels for reduction: %s\n", srcTexture->GetTextureName());
		OutputDebugString(moo);
	}

	// Cap max mip level so it is always in the valid range
	maxMipLevel = min(maxMipLevel, static_cast<INT32>(srcTexture->GetLevelCount()) - 1);

	// Set surface format
	if(surfaceFormat == ruSURFACEFMT_DEFAULT)
		surfaceFormat = srcTexture->GetSurfaceFormat();

	// Create new texture with the specified load parameters
	UINT32 convertedMipLevels = srcTexture->GetLevelCount() - maxMipLevel;

	// Re-allocate destination texture and restore it
	dstTexture->Allocate(srcTexture->GetLevelWidth(maxMipLevel), srcTexture->GetLevelHeight(maxMipLevel), convertedMipLevels, surfaceFormat);
	dstTexture->Restore();

//	dstTexture->SetProperty("texture:loaded mip level", maxMipLevel + loadedMipLevel);

	// Convert all mip levels
	for(UINT32 i = 0, j = maxMipLevel; i < convertedMipLevels; ++i, ++j)
	{
		IRuSurface *srcSurface = srcTexture->GetLevelSurface(j);
		IRuSurface *dstSurface = dstTexture->GetLevelSurface(i);

		RuSurfaceUtility_Convert(srcSurface, dstSurface);
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::DeviceReset_Handler(RuEventArgs *eventArgs)
{
	// Clear all render target pools
	for(INT32 i = 0; i < m_renderTargetPools.Count(); ++i)
	{
		m_renderTargetPools[i]->ClearRenderTargetPool();
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::EndJournalCallback(const char *name, void *data)
{
	char *newline = "\n";

	if(name)
	{
		m_journalStream->Write(name, strlen(name));
		m_journalStream->Write(newline, 1);
	}

	return TRUE;
}

BOOL CRuEngine_ResourceManager::WorkerFunction()
{
	char *streamName = NULL;
	if(m_streamWorkQueue->DequeueWork(&streamName))
	{
		// Load the stream
		IRuStream *stream = LoadStream_Internal(streamName);

		if(stream)
		{
			// Push stream onto preloaded queue
			m_streamWorkQueue->QueueResult(streamName, stream);
			streamName = NULL;
		}
		else
		{
			// Push stream name onto the missing queue
			m_streamWorkQueue->MarkWorkFailed(streamName);
		}
	}

	char *entityName = NULL;
	if(m_objectWorkQueue->DequeueWork(&entityName))
	{
		// Load the entity stream
		IRuStream *stream = LoadStream_Internal(entityName);

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

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

				// Push object onto preloaded queue
				m_objectWorkQueue->QueueResult(entityName, entityObject);
				entityName = NULL;
			}
			else
			{
				// Push object name onto the missing queue
				m_objectWorkQueue->MarkWorkFailed(entityName);
			}

			// Destroy the data stream
			DestroyStream_Internal(stream);
		}
		else
		{
			m_objectWorkQueue->MarkWorkFailed(entityName);
		}

		delete[] entityName;
	}

	char *textureName = NULL;
	if(m_textureWorkQueue->DequeueWork(&textureName))
	{
		IRuTexture *texture = LoadTexture_Internal(textureName);

		// Queue resulting texture
		m_textureWorkQueue->QueueResult(textureName, texture);
	}

	return TRUE;
}

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

#pragma managed(pop)
