#include "stdafx.h"
#include "..\..\Tesselator.h"
#include "XUI_FontData.h"
#include "XUI_Font.h"

extern IDirect3DDevice9 *g_pD3DDevice;

//--------------------------------------------------------------------------------------
// Name: XUI_Font()
// Desc: Constructor
//--------------------------------------------------------------------------------------
XUI_Font::XUI_Font(int iFontData, float scaleFactor, XUI_FontData *fontData)
	: m_iFontData(iFontData), m_fScaleFactor(scaleFactor)
{
	m_fontData = fontData;
	refCount = 0;

    m_fCursorX = 0.0f;
    m_fCursorY = 0.0f;

    m_fXScaleFactor = m_fYScaleFactor = scaleFactor;

    m_fSlantFactor = 0.0f;
    m_bRotate = FALSE;
    m_dRotCos = cos( 0.0 );
    m_dRotSin = sin( 0.0 );

    m_dwNestedBeginCount = 0L;
	
	// Initialize the window
    D3DDISPLAYMODE DisplayMode;
    g_pD3DDevice->GetDisplayMode( 0, &DisplayMode );
    m_rcWindow.x1 = 0;
    m_rcWindow.y1 = 0;
    m_rcWindow.x2 = DisplayMode.Width;
    m_rcWindow.y2 = DisplayMode.Height;
}


//--------------------------------------------------------------------------------------
// Name: ~XUI_Font()
// Desc: Destructor
//--------------------------------------------------------------------------------------
XUI_Font::~XUI_Font()
{
}

//--------------------------------------------------------------------------------------
// Name: GetTextExtent()
// Desc: Get the dimensions of a text string
//--------------------------------------------------------------------------------------

VOID XUI_Font::GetTextExtent( const WCHAR* strText, FLOAT* pWidth,
                          FLOAT* pHeight, BOOL bFirstLineOnly ) const
{
    assert( pWidth != NULL );
    assert( pHeight != NULL );

    // Set default text extent in output parameters
    int iWidth = 0;
    FLOAT fHeight = 0.0f;

    if( strText )
    {
        // Initialize counters that keep track of text extent
        int ix = 0;
        FLOAT fy = m_fontData->getFontHeight();       // One character high to start
        if( fy > fHeight )
            fHeight = fy;

        // Loop through each character and update text extent
        DWORD letter;
        while( (letter = *strText) != 0 )
        {
            ++strText;

            // Handle newline character
            if( letter == L'\n' )
            {
                if( bFirstLineOnly )
                    break;
                ix = 0;
                fy += m_fontData->getFontYAdvance();
                // since the height has changed, test against the height extent
                if( fy > fHeight )
                    fHeight = fy;
           }

            // Handle carriage return characters by ignoring them. This helps when
            // displaying text from a file.
            if( letter == L'\r' )
                continue;

            // Translate unprintable characters
            XUI_FontData::SChar sChar = m_fontData->getChar(letter);
        
			// Get text extent for this character's glyph
			ix += sChar.getOffset();
			ix += sChar.getWAdvance();

            // Since the x widened, test against the x extent
            if (ix > iWidth) iWidth = ix;
        }
    }

    // Convert the width to a float here, load/hit/store. :(
    FLOAT fWidth = static_cast<FLOAT>(iWidth);          // Delay the use if fWidth to reduce LHS pain
    // Apply the scale factor to the result
    fHeight *= m_fYScaleFactor;
     // Store the final results
    *pHeight = fHeight;

    fWidth *= m_fXScaleFactor;
    *pWidth = fWidth;
}

//--------------------------------------------------------------------------------------
// Name: GetTextWidth()
// Desc: Returns the width in pixels of a text string
//--------------------------------------------------------------------------------------
FLOAT XUI_Font::GetTextWidth( const WCHAR* strText ) const
{
    FLOAT fTextWidth;
    FLOAT fTextHeight;
    GetTextExtent( strText, &fTextWidth, &fTextHeight );
    return fTextWidth;
}

//--------------------------------------------------------------------------------------
// Name: Begin()
// Desc: Prepares the font vertex buffers for rendering.
//--------------------------------------------------------------------------------------
VOID XUI_Font::Begin()
{
    PIXBeginNamedEvent( 0, "Text Rendering" );

    // Set state on the first call
    if( 0 == m_dwNestedBeginCount )
    {
        // Cache the global pointer into a register
        IDirect3DDevice9 *pD3dDevice = g_pD3DDevice;
        assert( pD3dDevice );

        // Set the texture scaling factor as a vertex shader constant
        //D3DSURFACE_DESC TextureDesc;
        //m_pFontTexture->GetLevelDesc( 0, &TextureDesc );		// Get the description
 
        // Set render state
		assert(m_fontData->m_pFontTexture != NULL || m_fontData->m_iFontTexture > 0);
		if(m_fontData->m_pFontTexture != NULL)
		{
			pD3dDevice->SetTexture( 0, m_fontData->m_pFontTexture );
		}
		else
		{
			glBindTexture(GL_TEXTURE_2D, m_fontData->m_iFontTexture);
		}
 
        //// Read the TextureDesc here to ensure no load/hit/store from GetLevelDesc()
        //FLOAT vTexScale[4];
        //vTexScale[0] = 1.0f / TextureDesc.Width;		// LHS due to int->float conversion
        //vTexScale[1] = 1.0f / TextureDesc.Height;
        //vTexScale[2] = 0.0f;
        //vTexScale[3] = 0.0f;
        //
        //pD3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
        //pD3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
        //pD3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
        //pD3dDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
        //pD3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
        //pD3dDevice->SetRenderState( D3DRS_ALPHAREF, 0x08 );
        //pD3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
        //pD3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
        //pD3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
        //pD3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
        //pD3dDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE );
        //pD3dDevice->SetRenderState( D3DRS_VIEWPORTENABLE, FALSE );
        //pD3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
        //pD3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
        //pD3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
        //pD3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
    }

    // Keep track of the nested begin/end calls.
    m_dwNestedBeginCount++;
}


//--------------------------------------------------------------------------------------
// Name: DrawText()
// Desc: Draws text as textured polygons
//--------------------------------------------------------------------------------------
VOID XUI_Font::DrawText( DWORD dwColor, const WCHAR* strText, DWORD dwFlags,
                     FLOAT fMaxPixelWidth )
{
    DrawText( m_fCursorX, m_fCursorY, dwColor, strText, dwFlags, fMaxPixelWidth );
}

//--------------------------------------------------------------------------------------
// Name: DrawShadowText()
// Desc: Draws text as textured polygons
//--------------------------------------------------------------------------------------
VOID XUI_Font::DrawShadowText( FLOAT fOriginX, FLOAT fOriginY, DWORD dwColor, DWORD dwShadowColor,
                     const WCHAR* strText, DWORD dwFlags, FLOAT fMaxPixelWidth)
{
	float fXShadow=1.0f, fYShadow=1.0f;
	// 4J Stu - Don't move the drop shadow as much
    //DrawText( fOriginX + (1*m_fXScaleFactor), fOriginY + (1*m_fYScaleFactor), dwColor, strText, dwFlags, fMaxPixelWidth, true );

	// 4J-PB - if we're in 480 widescreen, we need to draw the drop shadow at +2 pixels, so that when the scene is halved, it's at +1
	if(!RenderManager.IsHiDef())
	{
		if(RenderManager.IsWidescreen())
		{
			fXShadow=2.0f;
			fYShadow=2.0f;
		}
		//else
		//{
			// 480 SD mode - the draw text call will reposition the y
		//}
	}
    DrawText( fOriginX + fXShadow, fOriginY + fYShadow, dwColor, strText, dwFlags, fMaxPixelWidth, true );
    DrawText( fOriginX, fOriginY, dwColor, strText, dwFlags, fMaxPixelWidth );
	
	//DrawText( fOriginX + 1, fOriginY + 1, dwShadowColor, strText, dwFlags, fMaxPixelWidth);
    //DrawText( fOriginX, fOriginY, dwColor, strText, dwFlags, fMaxPixelWidth );
}

//--------------------------------------------------------------------------------------
// Name: DrawText()
// Desc: Draws text as textured polygons
//       TODO: This function should use the Begin/SetVertexData/End() API when it
//       becomes available.
//--------------------------------------------------------------------------------------
VOID XUI_Font::DrawText( FLOAT fOriginX, FLOAT fOriginY, DWORD dwColor,
                     const WCHAR* strText, DWORD dwFlags, FLOAT fMaxPixelWidth, bool darken /*= false*/ )
{
    if( NULL == strText )    return;
    if( L'\0' == strText[0] ) return;
 
	// 4J-PB - if we're in 480 widescreen mode, we need to ensure that the font characters are aligned on an even boundary if they are a 2x multiple
	if(!RenderManager.IsHiDef())
	{
		if(RenderManager.IsWidescreen())
		{		
			int iScaleX=(int)m_fXScaleFactor;
			int iOriginX;
			if(iScaleX%2==0)
			{
				iOriginX=(int)fOriginX;
				if(iOriginX%2==1)
				{
					fOriginX+=1.0f;
				}
			}
			int iScaleY=(int)m_fYScaleFactor;
			int iOriginY;
			if(iScaleY%2==0)
			{
				iOriginY=(int)fOriginY;
				if(iOriginY%2==1)
				{
					fOriginY+=1.0f;
				}
			}
		}
		else
		{
			// 480 SD mode - y needs to be on a pixel boundary when multiplied by 1.5, so if it's an odd number, subtract 1/3 from it
			int iOriginY=(int)fOriginY;
			if(iOriginY%2==1)
			{
				fOriginY-=1.0f/3.0f;
			}
		}
	}
    // Create a PIX user-defined event that encapsulates all of the text draw calls.
    // This makes DrawText calls easier to recognize in PIX captures, and it makes
    // them take up fewer entries in the event list.
    PIXBeginNamedEvent( dwColor, "DrawText: %S", strText );

    // Set up stuff to prepare for drawing text
    Begin();

	if (darken)
	{
        int oldAlpha = dwColor & 0xff000000;
        dwColor = (dwColor & 0xfcfcfc) >> 2;
        dwColor += oldAlpha;
    }

	float r = ((dwColor >> 16) & 0xff) / 255.0f;
	float g = ((dwColor >> 8) & 0xff) / 255.0f;
	float b = ((dwColor) & 0xff) / 255.0f;
	float a = ((dwColor >> 24) & 0xff) / 255.0f;
	if (a == 0) a = 1;
	// a = 1;
	glColor4f(r, g, b, a);

    // Set the starting screen position
    if( ( fOriginX < 0.0f ) || ( ( dwFlags & ATGFONT_RIGHT ) && ( fOriginX <= 0.0f ) ) )
    {
        fOriginX += ( m_rcWindow.x2 - m_rcWindow.x1 );
    }
	// 4J-PB - not sure what this code was intending to do, but it removed a line of text that is slightly off the top of the control, rather than having it partially render
//     if( fOriginY < 0.0f )
//     {
//         fOriginY += ( m_rcWindow.y2 - m_rcWindow.y1 );
//     }

    m_fCursorX = floorf( fOriginX );
    m_fCursorY = floorf( fOriginY );

    // Adjust for padding
    fOriginY -= m_fontData->getFontTopPadding();
	
	XUI_FontData::SChar sChar = m_fontData->getChar(L'.');
    FLOAT fEllipsesPixelWidth = m_fXScaleFactor * 3.0f * (sChar.getOffset() + sChar.getWAdvance());

    if( dwFlags & ATGFONT_TRUNCATED )
    {
        // Check if we will really need to truncate the string
        if( fMaxPixelWidth <= 0.0f )
        {
            dwFlags &= ( ~ATGFONT_TRUNCATED );
        }
        else
        {
            FLOAT w, h;
            GetTextExtent( strText, &w, &h, TRUE );

            // If not, then clear the flag
            if( w <= fMaxPixelWidth )
                dwFlags &= ( ~ATGFONT_TRUNCATED );
        }
    }

    // If vertically centered, offset the starting m_fCursorY value
    if( dwFlags & ATGFONT_CENTER_Y )
    {
        FLOAT w, h;
        GetTextExtent( strText, &w, &h );
        m_fCursorY = floorf( m_fCursorY - (h * 0.5f) );
    }

    // Add window offsets
    FLOAT Winx = static_cast<FLOAT>(m_rcWindow.x1);
    FLOAT Winy = static_cast<FLOAT>(m_rcWindow.y1);
    fOriginX += Winx;
    fOriginY += Winy;
    m_fCursorX += Winx;
    m_fCursorY += Winy;

    // Set a flag so we can determine initial justification effects
    BOOL bStartingNewLine = TRUE;

    DWORD dwNumEllipsesToDraw = 0;

    // Begin drawing the vertices


    DWORD dwNumChars = wcslen( strText ) + ( dwFlags & ATGFONT_TRUNCATED ? 3 : 0 );

    bStartingNewLine = TRUE;

    // Draw four vertices for each glyph
    while( *strText )
    {
        WCHAR letter;

        if( dwNumEllipsesToDraw )
        {
            letter = L'.';
        }
        else
        {
            // If starting text on a new line, determine justification effects
            if( bStartingNewLine )
            {
                if( dwFlags & ( ATGFONT_RIGHT | ATGFONT_CENTER_X ) )
                {
                    // Get the extent of this line
                    FLOAT w, h;
                    GetTextExtent( strText, &w, &h, TRUE );

                    // Offset this line's starting m_fCursorX value
                    if( dwFlags & ATGFONT_RIGHT )
                        m_fCursorX = floorf( fOriginX - w );
                    if( dwFlags & ATGFONT_CENTER_X )
                        m_fCursorX = floorf( fOriginX - w * 0.5f );
                }
                bStartingNewLine = FALSE;
            }

            // Get the current letter in the string
            letter = *strText++;

            // Handle the newline character
            if( letter == L'\n' )
            {
                m_fCursorX = fOriginX;
                m_fCursorY += m_fontData->getFontYAdvance() * m_fYScaleFactor;
                bStartingNewLine = TRUE;

                continue;
            }

            // Handle carriage return characters by ignoring them. This helps when
            // displaying text from a file.
            if( letter == L'\r' )
                continue;
        }

        // Translate unprintable characters
        XUI_FontData::SChar sChar = m_fontData->getChar( letter );

        FLOAT fOffset = m_fXScaleFactor * ( FLOAT )sChar.getOffset();
        FLOAT fAdvance = m_fXScaleFactor * ( FLOAT )sChar.getWAdvance();
		// 4J Use the font max width otherwise scaling doesnt look right
        FLOAT fWidth = m_fXScaleFactor * (sChar.tu2() - sChar.tu1());//( FLOAT )pGlyph->wWidth;
		FLOAT fHeight = m_fYScaleFactor * m_fontData->getFontHeight();

        if( 0 == dwNumEllipsesToDraw )
        {
            if( dwFlags & ATGFONT_TRUNCATED )
            {
                // Check if we will be exceeded the max allowed width
                if( m_fCursorX + fOffset + fWidth + fEllipsesPixelWidth + m_fSlantFactor > fOriginX + fMaxPixelWidth )
                {
                    // Yup, draw the three ellipses dots instead
                    dwNumEllipsesToDraw = 3;
                    continue;
                }
            }
        }

        // Setup the screen coordinates
        m_fCursorX += fOffset;
        FLOAT X4 = m_fCursorX;
        FLOAT X1 = X4 + m_fSlantFactor;
        FLOAT X3 = X4 + fWidth;
        FLOAT X2 = X1 + fWidth;
        FLOAT Y1 = m_fCursorY;
        FLOAT Y3 = Y1 + fHeight;
        FLOAT Y2 = Y1;
        FLOAT Y4 = Y3;

        m_fCursorX += fAdvance;

        // Add the vertices to draw this glyph

        FLOAT tu1 = sChar.tu1() / (float)m_fontData->getImageWidth();
        FLOAT tv1 = sChar.tv1() / (float)m_fontData->getImageHeight();
        FLOAT tu2 = sChar.tu2() / (float)m_fontData->getImageWidth();
        FLOAT tv2 = sChar.tv2() / (float)m_fontData->getImageHeight();

		Tesselator *t = Tesselator::getInstance();
		t->begin();
		t->vertexUV(X1, Y1, 0.0f, tu1, tv1);
		t->vertexUV(X2, Y2, 0.0f, tu2, tv1);
		t->vertexUV(X3, Y3, 0.0f, tu2, tv2);
		t->vertexUV(X4, Y4, 0.0f, tu1, tv2);
		t->end();


        // If drawing ellipses, exit when they're all drawn
        if( dwNumEllipsesToDraw )
        {
            if( --dwNumEllipsesToDraw == 0 )
                break;
        }

        dwNumChars--;
    }

    // Undo window offsets
    m_fCursorX -= Winx;
    m_fCursorY -= Winy;

    // Call End() to complete the begin/end pair for drawing text
    End();

    // Close off the user-defined event opened with PIXBeginNamedEvent.
    PIXEndNamedEvent();
}


//--------------------------------------------------------------------------------------
// Name: End()
// Desc: Paired call that restores state set in the Begin() call.
//--------------------------------------------------------------------------------------
VOID XUI_Font::End()
{
    assert( m_dwNestedBeginCount > 0 );
    if( --m_dwNestedBeginCount > 0 )
    {
        PIXEndNamedEvent();
        return;
    }

    PIXEndNamedEvent();
}