/*--------------------------------------------------------------------------
  Project:  Manual 2D Library - demos - m2d - explicitLang
  File:     main.c

  Copyright (C)2007-2009 Nintendo. All Rights Reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law. They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Revision: 1982 $
 *-------------------------------------------------------------------------*/

// ============================================================================
//  Explanation of the demo:
//      This demo uses an e-manual with two languages, English and Dutch, and displays it by explicitly specifying the language for display.
//      
//
//      When handling multiple languages including the one that is not in the System Settings, such as Dutch, it is necessary for the application to select the language used for displaying the e-manual.
//      
//      
//
//  Using the Demo
//      Press the A Button to display the English-language e-manual.
//      Press the A Button to display the Dutch-language e-manual.
//      Program execution returns to the first screen when the e-manual has finished.
//
// ============================================================================

#include <twl.h>
#include <twl/na.h>

// Set the characters handled by the render string functions to Unicode
#define NNS_G2D_UNICODE

#include <nnsys.h>
#include <ntmv/m2d.h>
#include <string.h>

#include "loader.h"


// Manual archive filename
#define MANUAL_ARCHIVE_FILENAME     "/data/" NTMV_M2D_MANUALFILENAME

// Manual UI resource archive filename
#define MANUAL_UIRESARC_FILENAME    "/data/" NTMV_M2D_UIRESFILENAME

#define CANVAS_WIDTH        32      // Width of character rendering area (in characters)
#define CANVAS_HEIGHT       24      // Height of character rendering area (in characters)
#define CANVAS_LEFT         0       // X position of character drawing area (in characters)
#define CANVAS_TOP          0       // Y position of character rendering area (in characters)

#define TEXT_HSPACE         0       // Amount of space between characters when rendering strings (in pixels)
#define TEXT_VSPACE         0       // Line spacing when rendering strings (in pixels)

#define CHARACTER_OFFSET    0       // Starting address for the string to use

// DrawText rendering setting
#define DRAWTEXT_FLAG       (NNS_G2D_VERTICALORIGIN_MIDDLE | NNS_G2D_HORIZONTALORIGIN_CENTER | NNS_G2D_HORIZONTALALIGN_LEFT)

/*
    Font size
*/
enum
{
    FONTSIZE_L,
    FONTSIZE_M,
    FONTSIZE_S,

    FONTSIZE_MAX
};

// Text color
enum
{
    TXT_COLOR_FG    =  1,
    TXT_COLOR_BG    = 15
};

// Structure that stores GamePad information
typedef struct GamePad
{
    u16     trigger;
    u16     button;
}
GamePad;


//------------------------------------------------------------------------------
// static variables

static NNSFndAllocator  sAllocator;         // Memory allocator

static GamePad          sGamePad;           // GamePad

static NNSG2dCharCanvas sMainCharCanvas;    // CharCanvas for main screen
static NNSG2dTextCanvas sMainTextCanvas;    // TextCanvas
static NNSG2dCharCanvas sSubCharCanvas;     // CharCanvas for sub-screen
static NNSG2dTextCanvas sSubTextCanvas;     // TextCanvas

// Color palette shared by demos
static GXRgb TXTColorPalette[16] =
{
    0x0000, 0x5AD6, 0x39CE, 0x14A5,
    0x0000, 0x0000, 0x0000, 0x0000,
    0x0000, 0x0000, 0x0000, 0x0000,
    0x0000, 0x0000, 0x0000, 0x7FFF
};

//****************************************************************************
// Initialization, etc.
//****************************************************************************

/*---------------------------------------------------------------------------*
  Name:         VBlankIntr

  Description:  The V-Blank interrupt handler.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
VBlankIntr(void)
{
    OS_SetIrqCheckFlag(OS_IE_V_BLANK); // Sets a V-Blank interrupt confirmation flag
}

/*---------------------------------------------------------------------------*
  Name:         InitInterrupt

  Description:  Performs the initialization related to interrupts.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
InitInterrupt(void)
{
    (void)OS_DisableIrq();

    // Enable V-Blank interrupts
    OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
    (void)OS_EnableIrqMask(OS_IE_V_BLANK);
    (void)GX_VBlankIntr(TRUE);

    // Enables interrupts for the communications FIFO with the ARM7
    (void)OS_EnableIrqMask(OS_IE_SPFIFO_RECV);

    (void)OS_EnableIrq();
}

/*---------------------------------------------------------------------------*
  Name:         InitAllocator

  Description:  Initializes a memory allocator.

  Arguments:    pAllocator: Pointer to the NNS allocator to initialize.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
InitAllocator(NNSFndAllocator* pAllocator)
{
    u32   arenaLow      = (u32)OS_GetMainArenaLo();
    u32   arenaHigh     = (u32)OS_GetMainArenaHi();
    u32   heapSize      = arenaHigh - arenaLow;
    void* heapMemory    = OS_AllocFromMainArenaLo(heapSize, 4);
    NNSFndHeapHandle       heapHandle;
    NNS_NULL_ASSERT( pAllocator );

    heapHandle = NNS_FndCreateExpHeap(heapMemory, heapSize);
    SDK_ASSERT( heapHandle != NNS_FND_HEAP_INVALID_HANDLE );

    NNS_FndInitAllocatorForExpHeap(pAllocator, heapHandle, 4);
}

/*---------------------------------------------------------------------------*
  Name:         Init

  Description:  Performs initialization.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
Init(void)
{
    OS_Init();

    GX_Init();
    GX_DispOff();
    GXS_DispOff();

    InitInterrupt();

    FS_Init(FS_DMA_NOT_USE);    // Initialize file system

    InitAllocator( &sAllocator );
}


/*---------------------------------------------------------------------------*
  Name:         ClearVram

  Description:  Clears the VRAM.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
ClearVram(void)
{
    //---------------------------------------------------------------------------
    // All VRAM banks to LCDC
    //---------------------------------------------------------------------------
    GX_SetBankForLCDC(GX_VRAM_LCDC_ALL);

    //---------------------------------------------------------------------------
    // Clear all LCDC space
    //---------------------------------------------------------------------------
    MI_CpuClearFast((void *)HW_LCDC_VRAM, HW_LCDC_VRAM_SIZE);

    //---------------------------------------------------------------------------
    // Disable the banks on LCDC
    //---------------------------------------------------------------------------
    (void)GX_DisableBankForLCDC();

    MI_CpuFillFast((void *)HW_OAM, 192, HW_OAM_SIZE);      // Clear OAM
    MI_CpuClearFast((void *)HW_PLTT, HW_PLTT_SIZE);        // Clear the standard palette

    MI_CpuFillFast((void*)HW_DB_OAM, 192, HW_DB_OAM_SIZE); // Clear OAM
    MI_CpuClearFast((void *)HW_DB_PLTT, HW_DB_PLTT_SIZE);  // Clear the standard palette
}

/*---------------------------------------------------------------------------*
  Name:         AssignVramBanks

  Description:  Allocates VRAM.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
AssignVramBanks(void)
{
    GX_SetBankForBG(GX_VRAM_BG_128_A);

    GX_SetBankForSubBG(GX_VRAM_SUB_BG_128_C);
}

/*---------------------------------------------------------------------------*
  Name:         BGSetup

  Description:  Configures the BG screen.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
BGSetup(void)
{
    // Main screen setting

    GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_4, GX_BG0_AS_2D);
    GX_SetBGScrOffset(GX_BGSCROFFSET_0x10000);
    GX_SetBGCharOffset(GX_BGCHAROFFSET_0x00000);

    GXS_SetGraphicsMode(GX_BGMODE_4);

    //     Set BG1
    G2_SetBG1Control(
        GX_BG_SCRSIZE_TEXT_256x256,     // Screen size: 256x256
        GX_BG_COLORMODE_16,             // 16-color color mode
        GX_BG_SCRBASE_0x0000,           // Screen base
        GX_BG_CHARBASE_0x00000,         // Character base
        GX_BG_EXTPLTT_01                // Extended palette slot
    );

    //     Make BG1 visible
    GX_SetVisiblePlane(GX_PLANEMASK_BG1);

    //     Configure color palette
    GX_LoadBGPltt(TXTColorPalette, 0, sizeof(TXTColorPalette));


    // Sub-screen setting

    //     Set BG1
    G2S_SetBG1Control(
        GX_BG_SCRSIZE_TEXT_256x256,
        GX_BG_COLORMODE_16,
        GX_BG_SCRBASE_0x0000,
        GX_BG_CHARBASE_0x10000,
        GX_BG_EXTPLTT_01);

    //     Make BG1 visible
    GXS_SetVisiblePlane(GX_PLANEMASK_BG1);

    //     Configure color palette
    GXS_LoadBGPltt(TXTColorPalette, 0, sizeof(TXTColorPalette));
}

/*---------------------------------------------------------------------------*
  Name:         InitCanvas

  Description:  Initializes the string render.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
InitCanvas(NNSG2dFont *pFont)
{
    // Main screen setting
    {
        GXCharFmt16* pCharBase = (GXCharFmt16*)G2_GetBG1CharPtr();
        int cOffset = CHARACTER_OFFSET;

        // CharCanvas initialization
        NNS_G2dCharCanvasInitForBG(
            &sMainCharCanvas,
            pCharBase + cOffset,
            CANVAS_WIDTH,
            CANVAS_HEIGHT,
            NNS_G2D_CHARA_COLORMODE_16
        );

        // Set the screen
        NNS_G2dMapScrToCharText(
            G2_GetBG1ScrPtr(),
            CANVAS_WIDTH,
            CANVAS_HEIGHT,
            CANVAS_LEFT,
            CANVAS_TOP,
            NNS_G2D_TEXT_BG_WIDTH_256,
            cOffset,
            0
        );

        // TextCanvas initialization
        NNS_G2dTextCanvasInit(
            &sMainTextCanvas,
            &sMainCharCanvas,
            pFont,
            TEXT_HSPACE,
            TEXT_VSPACE
        );
    }

    // Sub-screen setting
    {
        GXCharFmt16* pCharBase = (GXCharFmt16*)G2S_GetBG1CharPtr();
        int cOffset = CHARACTER_OFFSET;

        // CharCanvas initialization
        NNS_G2dCharCanvasInitForBG(
            &sSubCharCanvas,
            pCharBase + cOffset,
            CANVAS_WIDTH,
            CANVAS_HEIGHT,
            NNS_G2D_CHARA_COLORMODE_16
        );

        // Set the screen
        NNS_G2dMapScrToCharText(
            G2S_GetBG1ScrPtr(),
            CANVAS_WIDTH,
            CANVAS_HEIGHT,
            CANVAS_LEFT,
            CANVAS_TOP,
            NNS_G2D_TEXT_BG_WIDTH_256,
            cOffset,
            0
        );

        // TextCanvas initialization
        NNS_G2dTextCanvasInit(
            &sSubTextCanvas,
            &sSubCharCanvas,
            pFont,
            TEXT_HSPACE,
            TEXT_VSPACE
        );
    }
}

/*---------------------------------------------------------------------------*
  Name:         LoadSharedFont

  Description:  Loads three types (large, medium, and small) of internal system fonts (TWL bitmap fonts) into memory and initializes them.
                

  Arguments:    fonts: NNSG2dFont variable string
                pSharedFontTable: Store the pointer to the buffer for the font table.
                                  Pointer to the variable
                nftrDatas: Array of pointers to the loaded font data
                pAllocator: Pointer to an NNSFnd allocator

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
LoadSharedFont(
    NNSG2dFont          fonts[],
    void**              pSharedFontTable,
    void*               nftrDatas[],
    NNSFndAllocator*    pAllocator
)
{
    // Font index
    static const NASharedFontIndex sSharedFontIdxes[][FONTSIZE_MAX] =
    {
        {   // Japan/North America/Europe/Australia 
            NA_SHARED_FONT_WW_L,
            NA_SHARED_FONT_WW_M,
            NA_SHARED_FONT_WW_S,
        },
        {   // China
            NA_SHARED_FONT_CN_L,
            NA_SHARED_FONT_CN_M,
            NA_SHARED_FONT_CN_S,
        },
        {   // Korea
            NA_SHARED_FONT_KR_L,
            NA_SHARED_FONT_KR_M,
            NA_SHARED_FONT_KR_S,
        },
    };

    BOOL bSuccess;
    int sizeTable;
    int i;
    int region;

    NNS_NULL_ASSERT(fonts);
    NNS_NULL_ASSERT(nftrDatas);
    NNS_NULL_ASSERT(pSharedFontTable);
    NNS_NULL_ASSERT(pAllocator);

    // Initialize the system internal font API
    bSuccess = NA_InitSharedFont();
    NNS_ASSERT(bSuccess);

    // Get the management information buffer size
    sizeTable = NA_GetSharedFontTableSize();
    NNS_ASSERT(sizeTable >= 0);

    *pSharedFontTable = NNS_FndAllocFromAllocator(pAllocator, (u32)sizeTable);
    NNS_NULL_ASSERT(*pSharedFontTable);

    // Load the management information
    bSuccess = NA_LoadSharedFontTable(*pSharedFontTable);
    NNS_ASSERT(bSuccess);

    // Determine the internal font index value using the system region. 
    // 
    region = 0; // Japan/North America/Europe/Australia 
    if (OS_TWL_REGION_CHINA == OS_GetRegion())
    {
        region = 1; // China
    }
    else if (OS_TWL_REGION_KOREA == OS_GetRegion())
    {
        region = 2; // Korea
    }

    for (i = 0; i < FONTSIZE_MAX; ++i)
    {
        int sizeFont;

        // Get the font size
        sizeFont = NA_GetSharedFontSize(sSharedFontIdxes[region][i]);
        NNS_ASSERT(sizeFont >= 0);

        nftrDatas[i] = NNS_FndAllocFromAllocator(pAllocator, (u32)sizeFont);
        NNS_NULL_ASSERT(nftrDatas[i]);

        // Load the font
        bSuccess = NA_LoadSharedFont(sSharedFontIdxes[region][i], nftrDatas[i]);
        NNS_ASSERT(bSuccess);

        NNS_G2dFontInitAuto(&fonts[i], nftrDatas[i]);
    }
}

/*---------------------------------------------------------------------------*
  Name:         ReadGamePad

  Description:  Reads GamePad input and stores it in an internal buffer.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
ReadGamePad(void)
{
    u16 status = PAD_Read();

    sGamePad.trigger = (u16)(status                  & (status ^ sGamePad.button));
    sGamePad.button  = status;
}

/*---------------------------------------------------------------------------*
  Name:         IsGamePadPress

  Description:  Determines whether the specified key is pressed on the GamePad.

  Arguments:    key: Key to check

  Returns:      Returns a nonzero value if a button has been pressed.
 *---------------------------------------------------------------------------*/
static
BOOL
IsGamePadPress(u16 key)
{
    return 0 != (sGamePad.trigger & key);
}

/*---------------------------------------------------------------------------*
  Name:         NitroMain

  Description:  Main function.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void
NitroMain(void)
{
    void* sharedFontTable;
    void* nftrDatas[FONTSIZE_MAX];
    NNSG2dFont fonts[FONTSIZE_MAX];

    Init();

    LoadSharedFont(fonts, &sharedFontTable, nftrDatas, &sAllocator);    // Load and initialize fonts

    while (TRUE)
    {
        // Display the language selection screen
        u16 lang = 0;
        {
            ClearVram();
            AssignVramBanks();
            BGSetup();

            InitCanvas(&fonts[0]);

            NNS_G2dCharCanvasClear(&sMainCharCanvas, TXT_COLOR_BG);
            NNS_G2dTextCanvasDrawText(&sMainTextCanvas, GX_LCD_SIZE_X / 2, GX_LCD_SIZE_Y / 2,
                TXT_COLOR_FG, DRAWTEXT_FLAG,
                L"M2d explicitLang sample"
            );

            NNS_G2dCharCanvasClear(&sSubCharCanvas, TXT_COLOR_BG);
            NNS_G2dTextCanvasDrawText(&sSubTextCanvas, GX_LCD_SIZE_X / 2, GX_LCD_SIZE_Y / 2,
                TXT_COLOR_FG, DRAWTEXT_FLAG,
                L"English  ... A button\n"
                L"Dutch    ... B button"
            );

            OS_WaitVBlankIntr();    // V-Blank wait
            GX_DispOn();
            GXS_DispOn();

            // Main loop
            while (TRUE)
            {
                ReadGamePad();

                OS_WaitVBlankIntr();    // V-Blank wait

                if (IsGamePadPress(PAD_BUTTON_A))
                {
                    lang = 'en';    // Select English language
                    break;
                }
                else if (IsGamePadPress(PAD_BUTTON_B))
                {
                    lang = 'nl';    // Select Dutch language
                    break;
                }
            }
        }

        // Display the e-manual using the specified language
        {
            NNSG2dFont *pFonts[FONTSIZE_MAX];
            int i = 0;

            NTMVM2dManualViewerInfo manViewInfo;    // Information structure for the NTMV manual viewer

            // Set the addresses of the NNSG2dFont variables to the NNSG2dFont pointer array
            for (i = 0; i < FONTSIZE_MAX; ++i)
            {
                pFonts[i] = &fonts[i];
            }

            // Initialize the Manual Viewer
            NTMV_M2dInitForFilePathEx(&manViewInfo, &sAllocator, MANUAL_ARCHIVE_FILENAME, MANUAL_UIRESARC_FILENAME, pFonts, lang);

            // Run the Manual Viewer
            NTMV_M2dRun(&manViewInfo);

            // Shut down the Manual Viewer
            NTMV_M2dFinalize(&manViewInfo, &sAllocator);
        }
    }
}

