/*--------------------------------------------------------------------------
  Project:  Manual 2D Library - demos - m2d - simple
  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:
//      Displays a sample e-manual.
//
//  Using the Demo
//      Press the A Button to display the e-manual.
//      Program execution returns to the first screen when the e-manual has finished.
//
// ============================================================================

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

#include "loader.h"


#define BG_DATA_DIR         "/data/nsc/"
#define PALETTE_FILENAME    BG_DATA_DIR "nlv_sample"
#define BG_MAIN_FILENAME    BG_DATA_DIR "nlv_sample_up"
#define BG_SUB_FILENAME     BG_DATA_DIR "nlv_sample_down"

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

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

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

    FONTSIZE_MAX
};

// Structure that stores data to display in the background
typedef struct BGData
{
    void*                   pPltFile;
    void*                   pChrFile;
    void*                   pScnFile;
    NNSG2dPaletteData*      pPltData;
    NNSG2dCharacterData*    pChrData;
    NNSG2dScreenData*       pScnData;
}
BGData;

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


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

static NNSFndAllocator  sAllocator;         // Memory allocator

static GamePad          sGamePad;           // GamePad


//****************************************************************************
// 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);
    NNS_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_256_AB);                         // VRAM-AB for BGs
    GX_SetBankForBGExtPltt(GX_VRAM_BGEXTPLTT_0123_E);           // VRAM-E for BG Ext Plt

    GX_SetBankForSubBG(GX_VRAM_SUB_BG_128_C);                   // VRAM-C for BGs
    GX_SetBankForSubBGExtPltt(GX_VRAM_SUB_BGEXTPLTT_0123_H);    // VRAM-H for BG Ext Plt
}

/*---------------------------------------------------------------------------*
  Name:         LoadBGData

  Description:  Loads color palette, characters, and screens from files to the main memory.
                

  Arguments:    pData: Pointer to a structure that stores data to display in the background
                nclFileBase: Color palette filename
                ncgFileBase: Character filename
                nscFileBase: Screen filename

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
LoadBGData(
    BGData*     pData,
    const char* nclFileBase,
    const char* ncgFileBase,
    const char* nscFileBase
)
{
    NNS_NULL_ASSERT(pData);
    NNS_NULL_ASSERT(nclFileBase);
    NNS_NULL_ASSERT(ncgFileBase);
    NNS_NULL_ASSERT(nscFileBase);

    // load from file
    {
        pData->pPltFile = LoadNCLR( &pData->pPltData, nclFileBase, &sAllocator);
        NNS_NULL_ASSERT( pData->pPltFile );

        pData->pChrFile = LoadNCGR( &pData->pChrData, ncgFileBase, &sAllocator);
        NNS_NULL_ASSERT( pData->pChrFile );

        pData->pScnFile = LoadNSCR( &pData->pScnData, nscFileBase, &sAllocator);
        NNS_NULL_ASSERT( pData->pScnFile );
    }
}

/*---------------------------------------------------------------------------*
  Name:         UnloadBGData

  Description:  Releases file data from main memory.

  Arguments:    pData: Pointer to a structure that stores data to display in the background

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
UnloadBGData(BGData* pData)
{
    NNS_NULL_ASSERT( pData );
    NNS_NULL_ASSERT( pData->pPltFile );
    NNS_NULL_ASSERT( pData->pChrFile );
    NNS_NULL_ASSERT( pData->pScnFile );
    NNS_FndFreeToAllocator( &sAllocator, pData->pPltFile );
    NNS_FndFreeToAllocator( &sAllocator, pData->pChrFile );
    NNS_FndFreeToAllocator( &sAllocator, pData->pScnFile );
}

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

  Description:  Configures the background.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static
void
BGSetup(void)
{
    // Set up registers
    {
        GX_SetVisiblePlane(GX_PLANEMASK_BG3);
        GXS_SetVisiblePlane(GX_PLANEMASK_BG2);
    }

    // Load bg
    {
        BGData bg;

        LoadBGData(&bg, PALETTE_FILENAME ".NCLR", BG_MAIN_FILENAME ".NCGR", BG_MAIN_FILENAME ".NSCR");
        NNS_G2dBGSetup(
            NNS_G2D_BGSELECT_MAIN3,
            bg.pScnData,
            bg.pChrData,
            bg.pPltData,
            GX_BG_SCRBASE_0x0000,
            GX_BG_CHARBASE_0x08000
        );
        UnloadBGData(&bg);

        LoadBGData(&bg, PALETTE_FILENAME ".NCLR", BG_SUB_FILENAME ".NCGR", BG_SUB_FILENAME ".NSCR");
        NNS_G2dBGSetup(
            NNS_G2D_BGSELECT_SUB2,
            bg.pScnData,
            bg.pChrData,
            bg.pPltData,
            GX_BG_SCRBASE_0x0000,
            GX_BG_CHARBASE_0x08000
        );
        UnloadBGData(&bg);
    }
}

/*---------------------------------------------------------------------------*
  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)
    {
        const int faderFrequency =  1;  // Synchronization for the fader

        const int FaderBrightnessMax = 16;  // Maximum fader brightness

        // The following settings are changed by the Manual Viewer library.
        // * Graphics
        // * Touch Panel
        // * Sound
        ClearVram();
        AssignVramBanks();
        BGSetup();

        GX_SetMasterBrightness(FaderBrightnessMax);
        GXS_SetMasterBrightness(FaderBrightnessMax);
        OS_WaitVBlankIntr();    // V-Blank wait
        GX_DispOn();
        GXS_DispOn();

        // Decrease the brightness from pure white
        {
            int cnt;
            for (cnt = 0; cnt / faderFrequency <= FaderBrightnessMax; ++cnt)
            {
                const int bright = FaderBrightnessMax - cnt / faderFrequency;

                OS_WaitVBlankIntr();    // V-Blank wait

                GX_SetMasterBrightness(bright);
                GXS_SetMasterBrightness(bright);
            }
        }

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

            OS_WaitVBlankIntr();    // V-Blank wait

            if (IsGamePadPress(PAD_BUTTON_A))
            {
                break;
            }
        }

        // Change to pure white
        {
            int cnt;

            GX_SetMasterBrightness(0);
            GXS_SetMasterBrightness(0);
            for (cnt = 0; cnt / faderFrequency <= FaderBrightnessMax; ++cnt)
            {
                const int bright = cnt / faderFrequency;

                OS_WaitVBlankIntr();    // V-Blank wait

                GX_SetMasterBrightness(bright);
                GXS_SetMasterBrightness(bright);
            }
            GX_DispOff();
            GXS_DispOff();
        }

        // Display the e-manual
        {
            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_M2dInitForFilePath(&manViewInfo, &sAllocator, MANUAL_ARCHIVE_FILENAME, MANUAL_UIRESARC_FILENAME, pFonts);

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

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

