//
// Asura_Wii_ROMFont.cpp
//
// Created by KOR - 09 October 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "Rendering/Asura_Wii_ROMFont.h"

// Asura headers
#include "Core/Asura_String.h"
#include "Debugging/Asura_Assert.h"

// Platform headers
#include "Core/Asura_Wii_MEM2.h"
#include "Rendering/Asura_GX.h"
#include "Rendering/Asura_GX_TextureManagement.h"

#include <stdio.h>
#include <wchar.h>
#include <revolution/gx.h>

//============================================================================
//
//  ROM Font data and functions
//
//============================================================================

OSFontHeader* 	Asura_Wii_ROMFont::s_pxFontData = NULL;
void*         	Asura_Wii_ROMFont::s_pvLastSheet = NULL;
short           Asura_Wii_ROMFont::s_sFontSize = -1;      // GX_S16, 4
short           Asura_Wii_ROMFont::s_sFontSpace = -1;     // GX_S16, 4

/*---------------------------------------------------------------------------*
    Name:           Initialise

    Description:    Allocates font buffer from the heap and initializes ROM
                    font

    Arguments:      None

    Returns:        None
 *---------------------------------------------------------------------------*/
void Asura_Wii_ROMFont::Initialise()
{
    if (OSGetFontEncode() == OS_FONT_ENCODE_SJIS)
    {
        s_pxFontData = static_cast<OSFontHeader*>(ASURA_WII_MEM2_ALLOC(OS_FONT_SIZE_SJIS));
    }
    else
    {
        s_pxFontData = static_cast<OSFontHeader*>(ASURA_WII_MEM2_ALLOC(OS_FONT_SIZE_ANSI));
    }
    if (!s_pxFontData)
    {
        OSHalt("Ins. memory to load ROM font.");
    }
    if (!OSInitFont(s_pxFontData))
    {
        OSHalt("ROM font is available in boot ROM ver 0.8 or later.");
    }

    s_sFontSize  = (s16) (s_pxFontData->cellWidth * 16);
    s_sFontSpace = -16;

    return;
}

void Asura_Wii_ROMFont::Shutdown()
{
	ASURA_WII_MEM2_FREE(s_pxFontData);
	s_pxFontData = 0;
}

/*---------------------------------------------------------------------------*
    Name:           SetSize

    Description:    Specifies the font size

    Arguments:      size        font size
                    space       character space

    Returns:        None
 *---------------------------------------------------------------------------*/
void Asura_Wii_ROMFont::SetSize( short sSize, short sSpace )
{
    s_sFontSize  = (sSize  * 16);
    s_sFontSpace = (sSpace * 16);
}

/*---------------------------------------------------------------------------*
    Name:           DEMOGetROMFontSize

    Description:    Gets the current font size

    Arguments:      size        font size
                    space       character space

    Returns:        None
 *---------------------------------------------------------------------------*/
void Asura_Wii_ROMFont::GetSize( short* psSize, short* psSpace )
{
    if (psSize)
    {
        *psSize  = (s_sFontSize  / 16);
    }
    if (psSpace)
    {
        *psSpace = (s_sFontSpace / 16);
    }
}

void Asura_Wii_ROMFont::DrawFontChar(int iX, int iY, int iZ, int iXChar, int iYChar)
{
    s16 posLeft   = (s16) iX;
    s16 posRight  = (s16) (posLeft + s_sFontSize);
    s16 posTop    = (s16) (iY - (s_pxFontData->ascent  * s_sFontSize / s_pxFontData->cellWidth));
    s16 posBottom = (s16) (iY + (s_pxFontData->descent * s_sFontSize / s_pxFontData->cellWidth));

    s16 texLeft   = (s16) iXChar;
    s16 texRight  = (s16) (iXChar + s_pxFontData->cellWidth);
    s16 texTop    = (s16) iYChar;
    s16 texBottom = (s16) (iYChar + s_pxFontData->cellHeight);

    GXBegin(GX_QUADS, GX_VTXFMT7, 4);
        GXPosition3s16(posLeft , posTop   , (s16) iZ);
        GXTexCoord2s16(texLeft , texTop      );

        GXPosition3s16(posRight, posTop   , (s16) iZ);
        GXTexCoord2s16(texRight, texTop      );

        GXPosition3s16(posRight, posBottom, (s16) iZ);
        GXTexCoord2s16(texRight, texBottom   );

        GXPosition3s16(posLeft , posBottom, (s16) iZ);
        GXTexCoord2s16(texLeft , texBottom   );
    GXEnd();
}

void Asura_Wii_ROMFont::LoadSheet(void* pvImage)
{
    Mtx          mtx;
    GXTexObj     texObj;

    if (s_pvLastSheet == pvImage)
    {
        return;
    }
    s_pvLastSheet = pvImage;

    // Set up and load texture object
    GXInitTexObj(&texObj,                           // obj
                 pvImage,                             // image_ptr
                 s_pxFontData->sheetWidth,              // wight
                 s_pxFontData->sheetHeight,             // height
                 (GXTexFmt) s_pxFontData->sheetFormat,  // format
                 GX_CLAMP,                          // wrap_s (don't care)
                 GX_CLAMP,                          // wrap_t (don't care)
                 GX_FALSE);                         // mipmap (don't care)

    GXInitTexObjLOD(&texObj,
                    GX_LINEAR,     // min_filt
                    GX_LINEAR,     // max_filt
                    0.0f,          // min_lod     (don't care)
                    0.0f,          // max_lod     (don't care)
                    0.0f,          // lod_bias    (don't care)
                    GX_DISABLE,    // bias_clamp  (don't care)
                    GX_FALSE,      // do_edge_lod (don't care)
                    GX_ANISO_1);   // max_aniso   (don't care)
    GXLoadTexObj(&texObj, GX_TEXMAP0);
    MTXScale(mtx, 1.0f / s_pxFontData->sheetWidth,
                  1.0f / s_pxFontData->sheetHeight,
                  1.0f);
    GXLoadTexMtxImm(mtx, GX_TEXMTX0, GX_MTX2x4);
    GXSetNumTexGens(1);
    GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX0);
	GXSetNumTevStages(1);
	GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
}

/*---------------------------------------------------------------------------*
    Name:           RenderString

    Description:    Draw the specified string with ROM font

    Arguments:      x       base line horizontal position
                    y       base line vertical position
                    string  output strings

    Returns:        total string width in texels
 *---------------------------------------------------------------------------*/
int Asura_Wii_ROMFont::RenderString( short sX, short sY, short sZ, const char* szString )
{
	if (!Asura_GX::ReadyToDraw())
		return 0;
	Asura_GX::Set3DProjection(false);
    s32   cx;
    void* image;
    s32   xChar;
    s32   yChar;
    s32   width;

    Asura_Assert(s_pxFontData, "Font data not loaded yet");

    s_pvLastSheet = 0;

    // Set up vertex descriptors
    GXClearVtxDesc();
    GXSetVtxDesc(GX_VA_POS,  GX_DIRECT);
    GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
    GXSetVtxAttrFmt(GX_VTXFMT7, GX_VA_POS,  GX_POS_XYZ, GX_S16, 4);
    GXSetVtxAttrFmt(GX_VTXFMT7, GX_VA_TEX0, GX_TEX_ST,  GX_S16, 0);

	// Reset render states
	GXSetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
	GXSetTevDirect( GX_TEVSTAGE0 );
	GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
	GXSetNumChans( 0 );
	GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);

    // Convert to GX_S16, 4 format
    sX *= 16;
    sY *= 16;
    sZ *= 16;

    width = 0;
    while (*szString)
    {
        if (*szString == '\n')
        {
            width = 0;
            sY += s_pxFontData->leading * s_sFontSize / s_pxFontData->cellWidth;
            ++szString;
            continue;
        }

        if (*szString == '\t')
        {
            width += 8 * (s_sFontSize + s_sFontSpace);
            width -= width % (8 * (s_sFontSize + s_sFontSpace));
            ++szString;
            continue;
        }

		szString = OSGetFontTexture(szString, &image, &xChar, &yChar, &cx);

		LoadSheet(image);
		DrawFontChar(sX + width, sY, sZ, xChar, yChar);
		width += s_sFontSize * cx / s_pxFontData->cellWidth + s_sFontSpace;
    }
    Mtx          mtx;
	MTXIdentity(mtx);
    GXLoadTexMtxImm(mtx, GX_TEXMTX0, GX_MTX2x4);
    GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);

	// Invalidate cached texture.
	Asura_GX_Texture_Management::SetTextureIndex(0, ASURA_TEXTURE_NONE);

    return (width + 15) / 16;
}

int Asura_Wii_ROMFont::RenderString( short sX, short sY, short sZ, const Asura_UnicodeChar* wszString )
{
	const u_int uLength = wcslen(wszString) + 1;
	char* szBuffer = new char[uLength];

	Asura_PrintfToBfr(szBuffer, "%S", uLength, true, wszString);

	const int iRetVal = RenderString(sX, sY, sZ, szBuffer);

	delete [] szBuffer;

	return iRetVal;
}
