//-----------------------------------------------------------------------------------------------
// BitmapFont.cpp
//
#include "GypsyPrecompiledHeader.hpp"

//-----------------------------------------------------------------------------------------------
#include "Engine/Renderer/BitmapFont.hpp"


//-----------------------------------------------------------------------------------------------
BitmapFont::BitmapFont()
	: m_glyphsTexture( nullptr )
{
	for( int glyphUCS = 0; glyphUCS < 256; ++ glyphUCS )
	{
		GlyphData& glyphData = m_glyphsData[ glyphUCS ];
		glyphData.m_cellHeightFractionBeforeGlyph = 0.0f;
		glyphData.m_cellHeightFractionDuringGlyph = 0.5f;
		glyphData.m_cellHeightFractionAfterGlyph = 0.0f;
		glyphData.m_texCoordMins.SetXY( 0.f, 0.f );
		glyphData.m_texCoordMaxs.SetXY( 1.f, 1.f );
	}
}


//-----------------------------------------------------------------------------------------------
void BitmapFont::PopulateMetaData( const char* fontFilePath )
{
	std::string fontFilePathString( fontFilePath );
	std::string fontMetaDataFilePathBMFont = fontFilePathString + ".fnt";
	std::string fontMetaDataFilePathJazz = fontFilePathString + ".xml";
	SquirrelXMLDocument fontMetaDataBMFont( fontMetaDataFilePathBMFont.c_str() );
	SquirrelXMLElement* rootElement = fontMetaDataBMFont.GetRootElement();
	if( rootElement && rootElement->DoesElementNameEqual( "font" ) )
	{
		// BMFont-style font metadata
//		float glyphHeight = 0.f;
		float cellHeight = 0.f;
		float invCellHeight = 0.f;
		float invTextureWidth = 0.f;
		float invTextureHeight = 0.f;
		SquirrelXMLElement* topElement = rootElement->GetFirstChildElement();
		while( topElement != nullptr )
		{
			if( topElement->DoesElementNameEqual( "info" ) )
			{
				// Ignore for now
			}
			else if( topElement->DoesElementNameEqual( "common" ) )
			{
				float lineHeight = 1.f;
				float textureWidth = 1.f;
				float textureHeight = 1.f;
				XMLGetFloatAttribute( *topElement, "lineHeight", lineHeight );
				XMLGetFloatAttribute( *topElement, "scaleW", textureWidth );
				XMLGetFloatAttribute( *topElement, "scaleH", textureHeight );
				invTextureWidth = 1.f / textureWidth;
				invTextureHeight = 1.f / textureHeight;
				cellHeight = lineHeight * invTextureHeight;
				invCellHeight = 1.f / cellHeight;
			}
			else if( topElement->DoesElementNameEqual( "pages" ) )
			{
				// Ignore for now
			}
			else if( topElement->DoesElementNameEqual( "chars" ) )
			{
				SquirrelXMLElement* charElement = topElement->GetFirstChildElement();
				int glyphUCS = 0;
				float glyphTexelX = 0.f;
				float glyphTexelY = 0.f;
				float glyphTexelWidth = 0.f;
				float glyphTexelHeight = 0.f;
				float glyphTexelOffsetX = 0.f;
				float glyphTexelAdvanceX = 0.f;
				while( charElement != nullptr )
				{
					XMLGetIntAttribute( *charElement, "id", glyphUCS );
					if( glyphUCS < GLYPH_UCS_MAX )
					{
						XMLGetFloatAttribute( *charElement, "x", glyphTexelX );
						XMLGetFloatAttribute( *charElement, "y", glyphTexelY );
						XMLGetFloatAttribute( *charElement, "width", glyphTexelWidth );
						XMLGetFloatAttribute( *charElement, "height", glyphTexelHeight );
						XMLGetFloatAttribute( *charElement, "xoffset", glyphTexelOffsetX );
						XMLGetFloatAttribute( *charElement, "xadvance", glyphTexelAdvanceX );

						GlyphData& glyphData = m_glyphsData[ glyphUCS ];
						glyphData.m_texCoordMins.x = glyphTexelX * invTextureWidth;
						glyphData.m_texCoordMins.y = glyphTexelY * invTextureHeight;
						glyphData.m_texCoordMaxs.x = glyphData.m_texCoordMins.x + (glyphTexelWidth * invTextureWidth);
						glyphData.m_texCoordMaxs.y = glyphData.m_texCoordMins.y + (glyphTexelHeight * invTextureHeight);
						glyphData.m_cellHeightFractionBeforeGlyph = (glyphTexelOffsetX * invTextureWidth) * invCellHeight;
						glyphData.m_cellHeightFractionDuringGlyph = (glyphTexelWidth * invTextureWidth) * invCellHeight;
						glyphData.m_cellHeightFractionAfterGlyph = ((glyphTexelAdvanceX * invTextureWidth) * invCellHeight) - (glyphData.m_cellHeightFractionBeforeGlyph + glyphData.m_cellHeightFractionDuringGlyph);
					}

					charElement = charElement->GetNextSiblingElement();
				}
			}
			else if( topElement->DoesElementNameEqual( "kernings" ) )
			{
				// Ignore for now
			}

			topElement = topElement->GetNextSiblingElement();
		}
	}
	else
	{
		// Metadata is either Jazz-style (.xml) or missing (oldschool 16x16 fixed/monospace)
		SquirrelXMLDocument fontMetaDataJazz( fontMetaDataFilePathJazz.c_str() );
		rootElement = fontMetaDataJazz.GetRootElement();
		if( rootElement && rootElement->DoesElementNameEqual( "FontDefinition" ) )
		{
			// Jazz-style font metadata
			SquirrelXMLElement* glyphElement = rootElement->GetFirstChildElement();
			while( glyphElement != nullptr )
			{
				int glyphUCS = -1;
				XMLGetIntAttribute( *glyphElement, "ucsIndex", glyphUCS );
				if( glyphUCS >= 0 && glyphUCS < 256 )
				{
					GlyphData& glyphData = m_glyphsData[ glyphUCS ];
					XMLGetVector2Attribute( *glyphElement, "texCoordMins", glyphData.m_texCoordMins );
					XMLGetVector2Attribute( *glyphElement, "texCoordMaxs", glyphData.m_texCoordMaxs );
					XMLGetFloatAttribute( *glyphElement, "ttfA", glyphData.m_cellHeightFractionBeforeGlyph );
					XMLGetFloatAttribute( *glyphElement, "ttfB", glyphData.m_cellHeightFractionDuringGlyph );
					XMLGetFloatAttribute( *glyphElement, "ttfC", glyphData.m_cellHeightFractionAfterGlyph );
				}

				glyphElement = glyphElement->GetNextSiblingElement();
			}
		}
		else
		{
			// No metadata was available; assume this is a simple old-school 16x16 layout of square fixed/monospace glyphs
			for( int glyphUCS = 0; glyphUCS < 256; glyphUCS ++ )
			{
				int glyphColumnX = glyphUCS & 0x0F; // Mod by 16 (per row)
				int glyphRowY = glyphUCS >> 4; // Divide by 16 (per row)
				const float texCoordsPerGlyph = (1.f / 16.f);
				GlyphData& glyphData = m_glyphsData[ glyphUCS ];
				glyphData.m_cellHeightFractionBeforeGlyph = -0.18f;
				glyphData.m_cellHeightFractionDuringGlyph = 0.7f;
				glyphData.m_cellHeightFractionAfterGlyph = -0.18f;
				glyphData.m_texCoordMins.x = texCoordsPerGlyph * static_cast< float >( glyphColumnX );
				glyphData.m_texCoordMins.y = texCoordsPerGlyph * static_cast< float >( glyphRowY );
				glyphData.m_texCoordMaxs.SetXY( glyphData.m_texCoordMins.x + texCoordsPerGlyph, glyphData.m_texCoordMins.y + texCoordsPerGlyph );
			}
		}
	}
}


//-----------------------------------------------------------------------------------------------
BitmapFont::~BitmapFont()
{
}


//-----------------------------------------------------------------------------------------------
void BitmapFont::AdvanceToEndOfLineByFit( const char*& scanChar, float maxWidth, float cellHeight )
{
	float widthSoFar = 0.f;
	while( *scanChar != '\0' )
	{
		if( *scanChar == '\n' )
			return;

		const GlyphData& glyphData = m_glyphsData[ *scanChar ];
		float glyphWidthABC = glyphData.m_cellHeightFractionBeforeGlyph + glyphData.m_cellHeightFractionDuringGlyph + glyphData.m_cellHeightFractionAfterGlyph;
		float widthIncludingGlyph = widthSoFar + (glyphWidthABC * cellHeight);
		if( widthIncludingGlyph > maxWidth )
			return;

		widthSoFar = widthIncludingGlyph;
		++ scanChar;
	}
}




