/*!
	@file RuTextureAtlas_Aggregate.cpp

	Copyright (c) 2004-2005 All rights reserved
*/

#include "../../Engine/Texture/RuTextureAtlas_Aggregate.h"

#pragma unmanaged

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

ruRTTI_DEFINE_SUBCLASS(CRuTextureAtlas_Aggregate, CRuTextureAtlas_Base)
ruHEAP_DEFINE_SUBCLASS(CRuTextureAtlas_Aggregate, "CRuTextureAtlas_Aggregate", "CRuTextureAtlas_Base")
ruCLASSGUID_DEFINE(CRuTextureAtlas_Aggregate, 0x833DB671, 0x66FB4488, 0x88323D81, 0x30826B1C)

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

CRuTextureAtlas_Aggregate::CRuTextureAtlas_Aggregate()
{
	m_textureQueue = ruNEW CRuLRUQueue<CRuGUID, INT32>(4096, 16384);
}

CRuTextureAtlas_Aggregate::~CRuTextureAtlas_Aggregate()
{
	for(INT32 i = 0; i < m_subAtlases.Count(); ++i)
	{
		ruSAFE_RELEASE(m_subAtlases[i].m_subAtlas);
		delete m_subAtlases[i].m_bindQueue;
	}

	for(INT32 i = 0; i < m_boundTextures.Count(); ++i)
	{
		ruSAFE_RELEASE(m_boundTextures[i].m_texture);
	}

	delete m_textureQueue;
}

BOOL CRuTextureAtlas_Aggregate::BindSubAtlas(CRuTextureAtlas_Base *subAtlas)
{
	INT32 atlasIndex = m_subAtlases.Count();

	m_subAtlases.Add(SubAtlasDesc());
	m_subAtlases[atlasIndex].m_subAtlas = subAtlas;
	m_subAtlases[atlasIndex].m_bindQueue = ruNEW CRuLRUQueue<CRuGUID, INT32>(4096, 16384);

	m_subAtlases[atlasIndex].m_subAtlas->AddRef();

	return TRUE;
}

BOOL CRuTextureAtlas_Aggregate::UnbindSubAtlas(CRuTextureAtlas_Base *subAtlas)
{
	return FALSE;
}

IRuTexture *CRuTextureAtlas_Aggregate::GetAtlasTexture()
{
	return NULL;
}

IRuTexture *CRuTextureAtlas_Aggregate::GetTexture(INT32 textureHandle)
{
	if(textureHandle >= 0 && textureHandle < m_boundTextures.Count())
	{
		// If the texture's atlas is non-null, then the texture is bound to it with handle specified by
		// m_handle. Otherwise, the texture is not bound.
		if(m_boundTextures[textureHandle].m_atlas)
		{
			return m_boundTextures[textureHandle].m_atlas->GetTexture(m_boundTextures[textureHandle].m_handle);
		}
		else
		{
			return m_boundTextures[textureHandle].m_texture;
		}
	}

	return NULL;
}

UINT32 CRuTextureAtlas_Aggregate::GetTextureWidth(INT32 textureHandle)
{
	if(textureHandle >= 0 && textureHandle < m_boundTextures.Count())
	{
		if(m_boundTextures[textureHandle].m_atlas)
		{
			return m_boundTextures[textureHandle].m_atlas->GetTextureWidth(m_boundTextures[textureHandle].m_handle);
		}
		else
		{
			return m_boundTextures[textureHandle].m_texture->GetLevelWidth(0);
		}
	}

	return 4;
}

UINT32 CRuTextureAtlas_Aggregate::GetTextureHeight(INT32 textureHandle)
{
	if(textureHandle >= 0 && textureHandle < m_boundTextures.Count())
	{
		if(m_boundTextures[textureHandle].m_atlas)
		{
			return m_boundTextures[textureHandle].m_atlas->GetTextureHeight(m_boundTextures[textureHandle].m_handle);
		}
		else
		{
			return m_boundTextures[textureHandle].m_texture->GetLevelHeight(0);
		}
	}

	return 4;
}

BOOL CRuTextureAtlas_Aggregate::CanBindIntoAtlas(IRuTexture *texture)
{
	return TRUE;
}

INT32 CRuTextureAtlas_Aggregate::BindIntoAtlas(IRuTexture *texture)
{
	INT32 textureHandle = -1;

	// Attempt to locate the texture
	if(m_textureQueue->ContainsKey(texture->GetGUID(), texture->GetGUID().HashCode()))
	{
		// Get the texture handle
		m_textureQueue->Get(texture->GetGUID(), texture->GetGUID().HashCode(), textureHandle);
	}

	// If the texture is not found in the queue, create a new entry for it
	if(textureHandle < 0)
	{
		// Get a free texture handle
		textureHandle = m_unusedHandles.IsEmpty() ? m_boundTextures.Count() : m_unusedHandles.Pop();

		if(textureHandle >= m_boundTextures.Count())
		{
			m_boundTextures.Add(TextureDesc());
		}

		// Store reference to texture
		m_boundTextures[textureHandle].m_texture = texture;
		m_boundTextures[textureHandle].m_texture->AddRef();

		// Set texture status to unbound
		m_boundTextures[textureHandle].m_atlas = NULL;
		m_boundTextures[textureHandle].m_handle = -1;

		// Store into texture queue
		m_textureQueue->Add(texture->GetGUID(), texture->GetGUID().HashCode(), textureHandle, 1);
	}

	// If the texture is not bound to an atlas, attempt to bind texture
	if(m_boundTextures[textureHandle].m_atlas == NULL)
	{
		for(INT32 i = 0; i < m_subAtlases.Count(); ++i)
		{
			if(m_subAtlases[i].m_subAtlas->CanBindIntoAtlas(texture))
			{
				INT32 subHandle = -1;

				while(subHandle < 0)
				{
					subHandle = m_subAtlases[i].m_subAtlas->BindIntoAtlas(texture);

					if(subHandle >= 0)
					{
						m_boundTextures[textureHandle].m_atlas = m_subAtlases[i].m_subAtlas;
						m_boundTextures[textureHandle].m_handle = subHandle;

						// Store into sub atlas bind queue
						m_subAtlases[i].m_bindQueue->Add(texture->GetGUID(), texture->GetGUID().HashCode(), textureHandle, 1);
					}
					else
					{
						// Free space from sub atlas bind queue
						INT32 freedHandle;
						if(m_subAtlases[i].m_bindQueue->ReclaimUnconditional(freedHandle))
						{
							// Unbind texture from the atlas
							if(m_boundTextures[freedHandle].m_atlas)
							{
								m_boundTextures[freedHandle].m_atlas->UnbindFromAtlas(m_boundTextures[freedHandle].m_handle);
							}

							// Reset freed entry
							m_boundTextures[freedHandle].m_atlas = NULL;
							m_boundTextures[freedHandle].m_handle = -1;
						}
						else
						{
							break;
						}
					}
				}

				break;
			}
		}
	}

	return textureHandle;
}

BOOL CRuTextureAtlas_Aggregate::UnbindFromAtlas(INT32 textureHandle)
{
	return FALSE;
/*
	if(textureHandle >= 0 && textureHandle < m_boundTextures.Count())
	{
		// Release texture
		ruSAFE_RELEASE(m_boundTextures[textureHandle].m_texture);

		// If the texture is bound to an atlas, unbind it
		if(m_boundTextures[textureHandle].m_atlas)
		{
			if(m_boundTextures[textureHandle].m_atlas->UnbindFromAtlas(m_boundTextures[textureHandle].m_handle) == FALSE)
			{
				return FALSE;
			}
		}

		// Recycle texture descriptor
		m_unusedHandles.Push(textureHandle);
		m_boundTextures[textureHandle].m_texture = NULL;
		m_boundTextures[textureHandle].m_atlas = NULL;
		m_boundTextures[textureHandle].m_handle = -1;

		return TRUE;
	}
*/
	return FALSE;
}

BOOL CRuTextureAtlas_Aggregate::TranslateUV(INT32 textureHandle, REAL u, REAL v, REAL uBias, REAL vBias, REAL &uOut, REAL &vOut)
{
	if(textureHandle >= 0 && textureHandle < m_boundTextures.Count())
	{
		if(m_boundTextures[textureHandle].m_atlas)
		{
			return m_boundTextures[textureHandle].m_atlas->TranslateUV(m_boundTextures[textureHandle].m_handle, u, v, uBias, vBias, uOut, vOut);
		}
		else
		{
			uOut = u;
			vOut = v;

			return TRUE;
		}
	}

	return FALSE;
}
/*
void CRuTextureAtlas_Aggregate::FlushTextureQueue()
{
	m_textureQueue->ForAllElements(FlushTextureQueueCallback, this);
	m_textureQueue->Clear();
}

static BOOL CRuTextureAtlas_Aggregate::FlushTextureQueueCallback(const void *key, const void *data, void *userData)
{
	CachedSkin cachedSkin = *((CachedSkin *) data);

	ruSAFE_RELEASE(cachedSkin.m_mesh);

	return TRUE;
}
*/
// ************************************************************************************************************************************************************

#pragma managed
