/*!
	@file RuEntity_Widget_Label.cpp

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

#include "../../Scene/Widget/RuEntity_Widget_Label.h"

#include "../../Engine/Geometry/RuPrimitiveToolkit.h"

#pragma unmanaged

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

ruRTTI_DEFINE_SUBCLASS(CRuEntity_Widget_Label, IRuEntity_Renderable)
ruHEAP_DEFINE_SUBCLASS(CRuEntity_Widget_Label, "CRuEntity_Widget_Label", "IRuEntity_Renderable")
ruCLASSGUID_DEFINE(CRuEntity_Widget_Label, 0xA022AD46, 0x6BFE444e, 0xA0648907, 0xBD007589)

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

CRuEntity_Widget_Label::CRuEntity_Widget_Label()
:	m_meshIterationNumber(0),
	m_geometryDirty(TRUE),
	m_texture(NULL),
	m_mesh(NULL),
	m_material(NULL)
{
}

CRuEntity_Widget_Label::~CRuEntity_Widget_Label()
{
	ruSAFE_RELEASE(m_texture);
	ruSAFE_RELEASE(m_mesh);
	ruSAFE_RELEASE(m_material);

	for(INT32 i = 0; i < m_rowDescriptors.Count(); ++i)
	{
		delete m_rowDescriptors[i].m_glyphRects;
	}
}

void CRuEntity_Widget_Label::SetTexture(IRuTexture *texture)
{
	if(m_material == NULL)
	{
		m_material = ruNEW CRuMaterial();
		m_material->SetShader(g_ruResourceManager->GetShader("ruSHADER_WIDGET_LABEL"));
	}

	ruSAFE_RELEASE(m_texture);

	m_texture = texture;

	if(m_texture)
	{
		m_texture->AddRef();
		m_material->SetTexture(ruTEXCHANNEL_DIFFUSEMAP, m_texture);
	}
}

void CRuEntity_Widget_Label::AddRowText(INT32 numRects, const CRuRectangle *rects, REAL textScale, DWORD textColor)
{
	// Create new row descriptor
	INT32 descIndex = m_rowDescriptors.Count();
	m_rowDescriptors.Add(RowDescriptor());

	// Copy row information into descriptor
	m_rowDescriptors[descIndex].m_textScale = textScale;
	m_rowDescriptors[descIndex].m_textColor = textColor;
	m_rowDescriptors[descIndex].m_numGlyphs = numRects;
	m_rowDescriptors[descIndex].m_glyphRects = ruNEW CRuRectangle [m_rowDescriptors[descIndex].m_numGlyphs];

	for(INT32 i = 0; i < m_rowDescriptors[descIndex].m_numGlyphs; ++i)
	{
		m_rowDescriptors[descIndex].m_glyphRects[i] = rects[i];
	}

	m_geometryDirty = TRUE;
}

void CRuEntity_Widget_Label::BuildGeometry()
{
	if(m_texture == NULL || m_geometryDirty == FALSE)
	{
		return;
	}

	INT32 totalGlyphs = 0;
	REAL yOffset = 0.0f;

	// Iterate through each row of text to find the total number of glyphs
	for(INT32 i = 0; i < m_rowDescriptors.Count(); ++i)
	{
		totalGlyphs += m_rowDescriptors[i].m_numGlyphs;
	}

	// Create mesh
	ruSAFE_RELEASE(m_mesh);
	m_mesh = ruNEW CRuDynamicMesh();
	m_mesh->Initialize(ruPRIMTYPE_TRIANGLELIST, 4 * totalGlyphs, 2 * totalGlyphs, ruVERTEXFORMAT_POSITION | ruVERTEXFORMAT_DIFFUSE, 1);
	m_mesh->AddTextureCoordinate(ruTEXCHANNEL_DIFFUSEMAP);
	m_mesh->SetNumPrimitives(2 * totalGlyphs);
	m_mesh->SetNumVertices(4 * totalGlyphs);

	CRuMorphTarget *morphTarget = static_cast<CRuMorphTarget *>(m_mesh->GetMorphTarget(0));
	CRuVector3 *position = morphTarget->GetPosition();
	DWORD *diffuse = morphTarget->GetDiffuse();
	REAL *uv = morphTarget->GetTextureCoordinate(ruTEXCHANNEL_DIFFUSEMAP);
	UINT16 *indices = m_mesh->GetIndices();

	UINT32 texWidth = m_texture->GetLevelWidth(0);
	UINT32 texHeight = m_texture->GetLevelHeight(0);

	// Iterate through each row of text
	for(INT32 i = 0, vertexCount = 0, uvCount = 0, indexCount = 0; i < m_rowDescriptors.Count(); ++i)
	{
		INT32 numGlyphs = m_rowDescriptors[i].m_numGlyphs;
		CRuRectangle *glyphRects = m_rowDescriptors[i].m_glyphRects;

		REAL rowWidth = 0.0f;
		REAL rowHeight = 0.0f;

		// Determine row width and height in texel space
		for(INT32 j = 0; j < numGlyphs; ++j)
		{
			rowWidth += glyphRects[j].m_right - glyphRects[j].m_left;
			rowHeight = max(rowHeight, glyphRects[j].m_bottom - glyphRects[j].m_top);
		}

		// Divide texel-space row dimensions by text scale to get the row dimensions in world space
		rowWidth /= m_rowDescriptors[i].m_textScale;
		rowHeight /= m_rowDescriptors[i].m_textScale;

		CRuVector3 rowOrigin(-rowWidth / 2.0f, yOffset, 0.0f);
		yOffset -= rowHeight;

		// Iterate through glyphs and build geometry
		for(INT32 j = 0; j < numGlyphs; ++j)
		{
			INT32 vertexOffset = vertexCount;
			REAL glyphWidth = (glyphRects[j].m_right - glyphRects[j].m_left) / m_rowDescriptors[i].m_textScale;
			REAL glyphHeight = (rowHeight, glyphRects[j].m_bottom - glyphRects[j].m_top) / m_rowDescriptors[i].m_textScale;

			position[vertexCount].Set(rowOrigin.x, rowOrigin.y, 0.0f);
			diffuse[vertexCount++] = m_rowDescriptors[i].m_textColor;
			uv[uvCount++] = glyphRects[j].m_left / texWidth;
			uv[uvCount++] = glyphRects[j].m_top / texHeight;

			position[vertexCount].Set(rowOrigin.x + glyphWidth, rowOrigin.y, 0.0f);
			diffuse[vertexCount++] = m_rowDescriptors[i].m_textColor;
			uv[uvCount++] = glyphRects[j].m_right / texWidth;
			uv[uvCount++] = glyphRects[j].m_top / texHeight;

			position[vertexCount].Set(rowOrigin.x, rowOrigin.y - glyphHeight, 0.0f);
			diffuse[vertexCount++] = m_rowDescriptors[i].m_textColor;
			uv[uvCount++] = glyphRects[j].m_left / texWidth;
			uv[uvCount++] = glyphRects[j].m_bottom / texHeight;

			position[vertexCount].Set(rowOrigin.x + glyphWidth,	rowOrigin.y - glyphHeight, 0.0f);
			diffuse[vertexCount++] = m_rowDescriptors[i].m_textColor;
			uv[uvCount++] = glyphRects[j].m_right / texWidth;
			uv[uvCount++] = glyphRects[j].m_bottom / texHeight;

			indices[indexCount++] = vertexOffset;
			indices[indexCount++] = vertexOffset + 1;
			indices[indexCount++] = vertexOffset + 2;

			indices[indexCount++] = vertexOffset + 1;
			indices[indexCount++] = vertexOffset + 3;
			indices[indexCount++] = vertexOffset + 2;

			rowOrigin.x += glyphWidth;
		}
	}

	// Update bounds and mark geometry as not dirty
	m_mesh->CalculateBounds();
	m_geometryDirty = FALSE;

	// Set local entity bounds
	this->SetLocalEntityBounds(m_mesh->GetBounds());
}

CRuEntity *CRuEntity_Widget_Label::Clone(CRuEntity *clonedEntity)
{
	return NULL;
}

BOOL CRuEntity_Widget_Label::ResetStates()
{
	return TRUE;
}

BOOL CRuEntity_Widget_Label::Update(float elapsedTime)
{
	// Build widget geometry
	BuildGeometry();

	IRuEntity_Renderable::Update(elapsedTime);

	return TRUE;
}

INT32 CRuEntity_Widget_Label::GetNumMeshes() const
{
	if(m_mesh && m_material)
	{
		return 1;
	}

	return 0;
}

BOOL CRuEntity_Widget_Label::GetMesh(INT32 meshIndex, IRuMesh **meshOut, IRuMaterial **materialOut)
{
	if(meshIndex == 0)
	{
		*meshOut = m_mesh;
		*materialOut = m_material;
	}

	return TRUE;
}

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

#pragma managed
