//////////////////////////////////////////////////////////////////////////////
//
// Asura_Project_Font_System.cpp
//
// Copyright (c) 2003 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_TiledTextureManagement.h"
#include "Rendering/Asura_Font_System.h"

#include "Actor/Vertebrates/AVP_Vertebrate_InputHandler.h"
#include "Core/AVP_Game_Core.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Network/AVP_Player.h"
#include "Rendering/AVP_Font_System.h"

// Forward declaration of font rendering function.
bool AVPPredatorFontRenderSpecialEffects(Asura_Font_System::Render_Effects_Glyph_Data* pxCharData);

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////

// Probably shouldn't re-order these as the text then needs to be updated.

DEBUGVAR float fSPECIAL_CHARACTER_WIDTH = 32.0f;

// order /must/ match special chars enum

// Helper functions
static Asura_Hash_ID GetTileForSpecialChar( Asura_UnicodeChar wcChar, u_int uComboID = 0 )
{
	// On PSP, Asura_UnicodeChar seems to evaluate to a signed type, which breaks 
	// the comparisons below. Convert to an unsigned type so everyone's happy
	u_short usUnsignedChar = static_cast<unsigned short> (wcChar);

	if( (usUnsignedChar >= AVP_Buttons::AVP_FONTSYSTEM_START_OF_SPECIAL_CHAR_MAPPINGS)
		&& (usUnsignedChar < AVP_Buttons::AVP_FONTSYSTEM_END_OF_SPECIAL_CHAR_MAPPINGS) )
	{
		// if the argument cha is in the range of the mapping ids, then convert it into the id of the key assigned to the mapping. 

		AVP_ClientEntity_Actor* pxClientActor = NULL;

#if !defined( ASURA_TOOLS )	
		if(AVP_Game_Core::GetPlayer())
		{
			pxClientActor = AVP_ClientEntity_Actor::FindEntity( AVP_Game_Core::GetPlayer()->GetPuppetGuid() );
		}
#endif //!defined( ASURA_TOOLS )

		if( pxClientActor )
		{
			AVP_Vertebrate_InputHandler* pxVertibrateInputHandler = static_cast<AVP_Vertebrate_InputHandler*>( pxClientActor->GetInputHandler() );
			usUnsignedChar =  static_cast<unsigned short>( pxVertibrateInputHandler->GetTileIdOrDescStringForMapping( 0, usUnsignedChar, 0, true, NULL, uComboID ) );
		}
		else
		{
#if !defined( ASURA_TOOLS )	
			Asura_Assert( false, "GetTileForSpecialChar\n The Local client can not be found, so the tile can not be looked up" );
#endif //!defined( ASURA_TOOLS )	
			usUnsignedChar = AVP_Buttons::AVP_FONTSYSTEM_SPECIALCHAR_NA;
		}
	}

	if( (usUnsignedChar >= AVP_Buttons::AVP_FONTSYSTEM_START_OF_SPECIAL_CHARS)
		&& (usUnsignedChar < AVP_Buttons::AVP_FONTSYSTEM_END_OF_SPECIAL_CHARS) )
	{
		return AVP_Buttons::s_auSpecialCharTiles[(usUnsignedChar & 0xFF)];
	}

	return ASURA_HASH_ID_UNSET;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : GetSpecialCharReffersToComboMapping (local helper function)
// Author       : Callum Hope
// Date         : 17th-Nov-2008
// Desc			: Static so it cant be called outside of this file, 
//				  Takes a text id for a mapping and finds if this is a combo mapping. 
//
// Arguments	: wcChar, the text id read from a string being printed on screen. eg \xE902
//
// Return		: bool high if this is a combo mapping, low if its anything else. 
////////////////////////////////////////////////////////////////////////////////

static bool GetSpecialCharReffersToComboMapping( Asura_UnicodeChar wcChar )
{
	// On PSP, Asura_UnicodeChar seems to evaluate to a signed type, which breaks 
	// the comparisons below. Convert to an unsigned type so everyone's happy
	u_short usUnsignedChar = static_cast<unsigned short> (wcChar);

	if( (usUnsignedChar >= AVP_Buttons::AVP_FONTSYSTEM_START_OF_SPECIAL_CHAR_MAPPINGS)
		&& (usUnsignedChar < AVP_Buttons::AVP_FONTSYSTEM_END_OF_SPECIAL_CHAR_MAPPINGS) )
	{
#if !defined( ASURA_TOOLS )
		// if the argument char is in the range of the mapping ids ( Above 0xE900 ) get the current input handler
		AVP_ClientEntity_Actor* pxClientActor = AVP_ClientEntity_Actor::FindEntity( AVP_Game_Core::GetPlayer()->GetPuppetGuid() );
		if( pxClientActor )
		{
			AVP_Vertebrate_InputHandler* pxVertibrateInputHandler = static_cast<AVP_Vertebrate_InputHandler*>( pxClientActor->GetInputHandler() );
			if( pxVertibrateInputHandler )
			{
				return pxVertibrateInputHandler->IsSpecialCharAComboMapping( usUnsignedChar );
			}
		}
		else
		{
			Asura_Assert( false, "GetSpecialCharIsComboMapping\n The Local client can not be found, so the mapping can not be checked for combo status. " );
			usUnsignedChar = AVP_Buttons::AVP_FONTSYSTEM_SPECIALCHAR_NA;
		}
#else  //!defined( ASURA_TOOLS )	
		return false;
#endif //!defined( ASURA_TOOLS )	
	}
	return false;
}

void Asura_Font_System::Project_Initialise()
{
	AVP_Buttons::Platform_Initialise();

	// So our encrypted strings use predator symbols.
	SetSpecialEffectsCallback(AVPPredatorFontRenderSpecialEffects);


}

void Asura_Font_System::Project_Shutdown()
{
}

bool Asura_Font_System::Project_PrintChar(
    const Asura_Bounding_Rectangle* pxRect,
    const float                     fStartX,
    const float                     fStartY,
          float                     fScale,
    const u_int                     uColour,
    const Asura_UnicodeChar         wcThisChar )
{
	bool bRetVal = false;
	int iCount = 0;
	float fChangableStartX = fStartX;

	// On PSP, Asura_UnicodeChar seems to evaluate to a signed type, which breaks 
	// the comparisons below. Convert to an unsigned type so everyone's happy
	u_short usUnsignedChar = static_cast<unsigned short> (wcThisChar);
	if( usUnsignedChar == AVP_Buttons::AVP_FONTSYSTEM_SPECIALCHAR_HARDSPACE )
	{
		return true;
	}

	// first find if this is a combo mapping, if so then the rest of this function must run throught twice; once for each mapping in the combo
	if( GetSpecialCharReffersToComboMapping( wcThisChar ) )
	{
		iCount++;
	}

	for( ; iCount >= 0; iCount -- )
	{
		const Asura_TiledTextureInfo* pxTileInfo = Asura_TiledTextureManagement::GetTileInfoByHash( GetTileForSpecialChar( wcThisChar, iCount ) );
		if( pxTileInfo )
		{	
			// Generate basic parameters
			const float fWidth    = (pxTileInfo->m_fu2 - pxTileInfo->m_fu1) * pxTileInfo->m_TiledToWidth;
			const float fHeight   = (pxTileInfo->m_fv2 - pxTileInfo->m_fv1) * pxTileInfo->m_TiledToHeight;
			const float fUseScale = (fScale > 1.0f) ? fScale : 1.0f;
			const float fYOffset  = (Asura_Font_System::GetFontHeight() - fHeight) * 0.5f;

			const Asura_Vector_2 xPosition( (fChangableStartX + (fWidth * 0.5f * fScale)), (fStartY + ((fHeight * 0.5f + fYOffset) * fScale)) );
			const Asura_Vector_2 xSize( (fWidth * fUseScale), (fHeight * fUseScale) );
			const Asura_Vector_2 xTopLeft( (xPosition.x - (xSize.x * 0.5f)), (xPosition.y - (xSize.y * 0.5f)) );

			// Initialise the overlay
			Asura_Render::Overlay.m_bNonRotated		= true;
			Asura_Render::Overlay.m_bGouraudShaded	= false;
			//Asura_Render::Overlay.Colour			= CanColouriseSpecialChar(wcThisChar) ? uColour : ((uColour & 0xFF000000) | 0xFFFFFF); // either the full colour or white plus the alpha from the colour
			Asura_Render::Overlay.TranslucencyMode	= ASURA_TRANSLUCENCY_NORMAL;
			Asura_Render::Overlay.TextureIndex		= pxTileInfo->m_iIndexOfTextureUsed;

			Asura_Render::Overlay.SetRectUV( pxTileInfo->m_fu1, pxTileInfo->m_fv1, (pxTileInfo->m_fu2 - pxTileInfo->m_fu1), (pxTileInfo->m_fv2 - pxTileInfo->m_fv1) );
			Asura_Render::Overlay.SetRectXY( xTopLeft.x, xTopLeft.y, xSize.x, xSize.y );

			// Finally, render...
			Asura_Render::Overlay.Render();

			// now incase we have to print another tile for a combo mapping move the starting x position along the width of the tile just rendered.
			fChangableStartX += fWidth;

			bRetVal = true;
		}
	}

	return bRetVal;
}

const Asura_Font_Glyph* Asura_Font_System::Project_GetGlyphData(
          Asura_UnicodeChar      wcThisChar,
          u_int                  uFormat,
    const Asura_Font_GlyphPage** ppxGlyphPage )
{
	// On PSP, Asura_UnicodeChar seems to evaluate to a signed type, which breaks 
	// the comparisons below. Convert to an unsigned type so everyone's happy
	u_short usUnsignedChar = static_cast<unsigned short> (wcThisChar);
	if( usUnsignedChar == AVP_Buttons::AVP_FONTSYSTEM_SPECIALCHAR_HARDSPACE )
	{
		static Asura_Font_Glyph	s_xGlyph;
		s_xGlyph.u = 0.0f;
		s_xGlyph.v = 0.0f;
		s_xGlyph.width = GetWidthOfChar_Unscaled(L' ', 1.0f);
		s_xGlyph.wcThisChar = AVP_Buttons::AVP_FONTSYSTEM_SPECIALCHAR_HARDSPACE;
		s_xGlyph.usKerningIndex = Asura_Font_Glyph::INVALID_KERNING_IDX;
		return &s_xGlyph;
	}	

	// Is this a special character?
	// no need to worry about combo mappings here as in the other functions as the project specific glyph data is only used in this file and the other functions take care of it. 
	const Asura_TiledTextureInfo* pxTileInfo = Asura_TiledTextureManagement::GetTileInfoByHash( GetTileForSpecialChar( wcThisChar ) );
	s_bNextGlyphRequiresProjectRender = (pxTileInfo != NULL);

	if( pxTileInfo )
	{
		// Yes - generate and return the glyph...
		static Asura_Font_Glyph	s_xGlyph;

		s_xGlyph.u = pxTileInfo->m_fu1;
		s_xGlyph.v = pxTileInfo->m_fv1;
		s_xGlyph.width = (pxTileInfo->m_fu2 - pxTileInfo->m_fu1) * pxTileInfo->m_TiledToWidth;
		s_xGlyph.wcThisChar = wcThisChar;
		s_xGlyph.usKerningIndex = Asura_Font_Glyph::INVALID_KERNING_IDX;

		return &s_xGlyph;
	}

	return NULL;
}

float Asura_Font_System::Project_GetCharSpacing(
	Asura_UnicodeChar wChar,Asura_UnicodeChar wNextChar,float fScale
)
{
    return 0.0f;
}

float Asura_Font_System::Project_GetWidthOfChar(Asura_UnicodeChar wcChar, float fScale)
{
	// Is this a special character?
	if( wcChar == AVP_Buttons::AVP_FONTSYSTEM_SPECIALCHAR_HARDSPACE )
	{
		return GetWidthOfChar_Unscaled(L' ', fScale);
	}
	else if( (wcChar >= AVP_Buttons::AVP_FONTSYSTEM_START_OF_SPECIAL_CHARS)
		&& (wcChar < AVP_Buttons::AVP_FONTSYSTEM_END_OF_SPECIAL_CHARS) )
	{
		int iCount = 0;
		float fWidth = 0.0f;

		// first find if this is a combo mapping, if so then the rest of this function must run throught twice; once for each mapping in the combo
		if( GetSpecialCharReffersToComboMapping( wcChar ) )
		{
			iCount++;
		}

		for( ; iCount >= 0; iCount -- )
		{
			const Asura_TiledTextureInfo* pxTileInfo = Asura_TiledTextureManagement::GetTileInfoByHash( GetTileForSpecialChar( wcChar, iCount ) );
			if( pxTileInfo )
			{
				fWidth += ((pxTileInfo->m_fu2 - pxTileInfo->m_fu1) * pxTileInfo->m_TiledToWidth) * fScale;
			}
			else
			{
				fWidth += fSPECIAL_CHARACTER_WIDTH * fScale;
			}
		}

		return fWidth;
	}

	return 0.0f;
}

Asura_UnicodeChar Asura_Font_System::Project_GetUnsupportedGlyphReplacementCharacter()
{
	return L'?';
}



// Default function that supports Teletype, decryption and cursor rendering.
bool AVPPredatorFontRenderSpecialEffects(Asura_Font_System::Render_Effects_Glyph_Data* pxCharData)
{
	bool bDrawnAnything = false;

	Asura_Assert(pxCharData->m_pxSpecialEffectsSettings != NULL,"pxSpecialEffectsSettings == NULL" );

	// The the age of the character
	float fCharAge = pxCharData->m_pxSpecialEffectsSettings->CalcCharacterAge(pxCharData->m_uCharCount,
		pxCharData->m_uStringLength, 
		pxCharData->m_uStartLinePos,
		pxCharData->m_uEndLinePos);

	if ( pxCharData->m_bRenderCursor == true )
	{
		if ( pxCharData->m_pxSpecialEffectsSettings->GetCursor() == false)
		{
			// If its time to render a cursor and its not required by the effect early out.
			return bDrawnAnything;
		}

		// Is the cursor being rendered at the end of the string. If so start it flashing.
		if ( pxCharData->m_uCharCount >= pxCharData->m_uStringLength )
		{
			if (  pxCharData->m_pxSpecialEffectsSettings->GetCursorStopTime() > 0.0f &&
				fCharAge > pxCharData->m_pxSpecialEffectsSettings->GetCursorStopTime() )
			{
				// The cursor has past it stop time so don't render it anymore
				return bDrawnAnything;
			}

			// Get the timer and test if the cursor should be on or off.
			float fBlinkRate = pxCharData->m_pxSpecialEffectsSettings->GetCursorFlashTime();

			if (fBlinkRate > 0.0f )
			{
				float fTime = pxCharData->m_pxSpecialEffectsSettings->GetTime();

				int iBlink = static_cast<int>(fTime * 1000.0f) % static_cast<int>((fBlinkRate * 2.0f) * 1000.0f);

				if ( iBlink < (fBlinkRate * 1000.0f) )
				{
					// The cursor has blinked off so don't render anything
					return bDrawnAnything;
				}
			}
		}
	}

	// If a cursor is being rendered or teletyping is off or the character is old enough to render,
	// then render it.
	if ( pxCharData->m_bRenderCursor == true ||
		pxCharData->m_pxSpecialEffectsSettings->GetTeletype() == false || 
		fCharAge > 0.0f )
	{
		u_int uNewColour = pxCharData->m_uColour;

		// Modify the colour for the alpha fade effect
		if ( pxCharData->m_bRenderCursor == false )
		{
			Asura_RGBA xColour(pxCharData->m_uColour);

			// Apply the alpha for the teletype effect
			xColour.m_fA *= pxCharData->m_pxSpecialEffectsSettings->GetAlpha(fCharAge);

			u_int uNewColour = xColour.ToUIntARGB(true);
			Asura_Font_System::s_uCharacterColour = uNewColour;
		}

		// If decryption effect is one pick a random character
		if ( pxCharData->m_wCurChar != L' ' &&
			pxCharData->m_bRenderCursor == false &&
			pxCharData->m_pxSpecialEffectsSettings->GetDecrypt() == true &&
			pxCharData->m_pxSpecialEffectsSettings->GetDecryptTime() > fCharAge &&
			pxCharData->m_pxSpecialEffectsSettings->GetDecryptChangeCharTime() > 0.0f)
		{
			// Replace char with a random one.
			const Asura_Font* pxFont = pxCharData->m_pxFont;

			const Asura_Font_GlyphPage* pxGlyphPage = pxCharData->m_pxGlyphPage;
			const Asura_Font_Glyph* pxThisGlyph = pxCharData->m_pxThisGlyph;
			Asura_UnicodeChar wCurChar = pxCharData->m_wCurChar;

			u_int encryptChar = wCurChar + pxCharData->m_uCharCount + static_cast<u_int>(fCharAge / pxCharData->m_pxSpecialEffectsSettings->GetDecryptChangeCharTime());
			encryptChar = encryptChar % 10;

			wCurChar = static_cast<Asura_UnicodeChar>(encryptChar+10); // <- STARTS AT CHAR 10

			pxThisGlyph = pxFont->GetGlyphData( wCurChar, pxCharData->m_uFormat, &pxGlyphPage );
			if( !pxThisGlyph )
			{
				return bDrawnAnything;
			}

			// Draw the random decryption character
			if (pxCharData->m_bGlyphRequiresProjectRender)
			{
				bDrawnAnything |= Asura_Font_System::Project_PrintChar( &pxCharData->m_xClipPixelRect, 
					pxCharData->m_fX, 
					pxCharData->m_fY, 
					pxCharData->m_fGlyphScale, 
					uNewColour, 
					wCurChar );
			}
			else
			{
				bDrawnAnything |= Asura_Font_System::PrintChar( &pxCharData->m_xClipPixelRect, 
					pxCharData->m_fX, 
					pxCharData->m_fY, 
					pxCharData->m_fScale, 
					uNewColour, 
					pxThisGlyph, 
					pxGlyphPage );
			}
		}
		else
		{
			// Render the normal character
			if (pxCharData->m_bGlyphRequiresProjectRender)
			{
				bDrawnAnything |= Asura_Font_System::Project_PrintChar( &pxCharData->m_xClipPixelRect, 
					pxCharData->m_fX, 
					pxCharData->m_fY, 
					pxCharData->m_fGlyphScale, 
					uNewColour, 
					pxCharData->m_wCurChar );
			}
			else
			{
				bDrawnAnything |= Asura_Font_System::PrintChar( &pxCharData->m_xClipPixelRect, 
					pxCharData->m_fX, 
					pxCharData->m_fY, 
					pxCharData->m_fScale, 
					uNewColour, 
					pxCharData->m_pxThisGlyph, 
					pxCharData->m_pxGlyphPage );
			}
		}

		// Restore the colour to what it was
		Asura_Font_System::s_uCharacterColour = pxCharData->m_uColour;
	}

	return bDrawnAnything;
}
//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////
