/*---------------------------------------------------------------------------*
  Project:   NITRO-System - build - demos - g2d - samples - MultiCell_UILayout
  File:     main.c

  Copyright 2004,2005 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.

  $Log: main.c,v $
  Revision 1.2  09/01/2005 05:22:28  kitani_toshikazu
  Corrected comment

  Revision 1.1  08/31/2005 06:25:11  kitani_toshikazu
  Added initial issue.

  $NoKeywords: $
 *---------------------------------------------------------------------------*/

// ============================================================================
//  Explanation of the demo:
//      This sample performs UI (user-interface) processing for the game application by applying multicells.
//       
//
//  Operation Method
//      +Control pad   Cursor position
//      
// ============================================================================

#include <nitro.h>
#include <nnsys/g2d.h>

// Header file defining the different names of animation 
// sequence numbers output by the binary converter (with -lbl option specified)
#include "../data/win_pnl_NANR_LBLDEFS.h"
#include "g2d_demolib.h"


#define NUM_OF_OAM              128                 // Number of OAMs controlled by OAM Manager
#define NUM_OF_AFFINE           (NUM_OF_OAM/4)      // Number of affine parameters controlled by OAM Manager

// Multicell position
#define MY_MC_BASE_POS_X        128
#define MY_MC_BASE_POS_Y        96
// MultiCell display position
static const NNSG2dFVec2           MULTICELL_POSITION 
            = {MY_MC_BASE_POS_X << FX32_SHIFT, MY_MC_BASE_POS_Y << FX32_SHIFT};
            
            
// Meaning of the animation frame number used in a cell animation as defined by the user
#define APP_UI_ANIMFRAME_IDX_NORMAL 0   // Normal status
#define APP_UI_ANIMFRAME_IDX_PUSHED 1   // Button being held down


//
// MultiCellAnimation
// Initializes instances using two different methods.
// 
static NNSG2dMultiCellAnimation   mcAnim;


// Temporary OAM attribute buffer
static GXOamAttr                tempOamBuffer[NUM_OF_OAM];

//
// Multicell bank
//
static NNSG2dMultiCellDataBank*        pMCBank = NULL;     // Multi cell data
static NNSG2dCellDataBank*             pCellBank = NULL;   // Cell data
static NNSG2dCellAnimBankData*         pAnimBank = NULL;   // Cell animation
static NNSG2dMultiCellAnimBankData*    pMCABank = NULL;    // Multi-cell animation

// Pointer position
static NNSG2dSVec2          pointerPos_ = { 0, 0 };
static BOOL                 bHit_       = FALSE;


//------------------------------------------------------------------------------
// Prototype declarations

static void InitOamManager(NNSG2dOamManagerInstance* pOamManager);
static void LoadResources();
static void ProcessInput(fx32* pAnimSpeed, NNSG2dAnimController* pAnimCtrl);
void VBlankIntr(void);



/*---------------------------------------------------------------------------*
  Name:         InitOamManager

  Description:  Initializes the OAM manager system and OAM manager.

  Arguments:    pOamManager:    Pointer to the OAM manager to be initialized.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void InitOamManager(NNSG2dOamManagerInstance* pOamManager)
{
    BOOL    result;

    // Initializes the Oam Manager system
    NNS_G2dInitOamManagerModule();

    result = NNS_G2dGetNewManagerInstance(
                pOamManager,
                0,
                NUM_OF_OAM - 1,
                NNS_G2D_OAMTYPE_MAIN );
    SDK_ASSERT( result );

    result = NNS_G2dInitManagerInstanceAffine( pOamManager, 0, NUM_OF_AFFINE - 1 );
    SDK_ASSERT( result );
}



/*---------------------------------------------------------------------------*
  Name:         InitMultiCellInstance_

  Description:  Initializes instance
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
static void InitMultiCellInstance_
(
    NNSG2dMultiCellAnimation*       pMCAnim,
    NNSG2dCellAnimBankData*         pAnimBank,
    NNSG2dCellDataBank*             pCellBank,
    NNSG2dMultiCellDataBank*        pMCBank,
    NNSG2dMultiCellAnimBankData*    pMCABank,
    NNSG2dMCType                    mcType
)
{
    // Get work memory of the size needed for initialization
    const u32 szWork = NNS_G2dGetMCWorkAreaSize( pMCBank, mcType );
    void* pWorkMem   = G2DDemo_Alloc( szWork );
    
    // Initialization
    NNS_G2dInitMCAnimationInstance( pMCAnim, 
                                    pWorkMem, 
                                    pAnimBank, 
                                    pCellBank, 
                                    pMCBank, 
                                    mcType );
    
    SDK_NULL_ASSERT( pMCAnim );
    
    // Sets the sequence to play back to the multicell animation.
    {
        const NNSG2dMultiCellAnimSequence* pSequence = NULL;    
        // Obtains the sequence to play back
        pSequence = NNS_G2dGetAnimSequenceByIdx( pMCABank, 0 );
        SDK_ASSERT( pSequence );

        NNS_G2dSetAnimSequenceToMCAnimation( pMCAnim, pSequence);
    }
}

/*---------------------------------------------------------------------------*
  Name:         IsRectIntersect_

  Description:  Determine whether or not the rectangle has collided with the point.

  Arguments:    pRect:          Rectangle for which the determination is to be made
                
                pPointerPos:   Point for which the determination is to be made
                
                pRectPos:     Rectangle position

  Returns:      Have they collided? Returns TRUE if they have collided.
 *---------------------------------------------------------------------------*/
static BOOL IsRectIntersect_
( 
    const NNSG2dCellBoundingRectS16*    pRect,
    const NNSG2dSVec2*                  pPointerPos, 
    const NNSG2dSVec2*                  pRectPos 
)
{
    NNSG2dSVec2 posPtr = (*pPointerPos);
    
    posPtr.x -= pRectPos->x; 
    posPtr.y -= pRectPos->y; 
    
    if( pRect->maxX >= posPtr.x && 
        pRect->minX <= posPtr.x )
    {
        if( pRect->maxY >= posPtr.y && 
            pRect->minY <= posPtr.y )
        {
            // Objects collided
            return TRUE;        
        }
    }
    // Objects did not collide
    return FALSE;
}

/*---------------------------------------------------------------------------*
  Name:         OnButtonHitCallBack_

  Description:  This callback function performs collision detection for the UI buttons.
                The result is used as an argument to NNS_G2dTraverseMCNodes().
                
                This function is used as a callback that is called for all of the nodes in a multicell.
                 

  Arguments:    userParamater :   User parameters
                                 In this sample, this stores the pointer value to the flag used to record whether or not there was a collision.
                                  
                
                Node data.       Stores information such as the position of cell animation during cell animation.
                                  
                
                pCellAnim:      Cell animation (used during cell animation)
                
                nodeIdx:        node number

  Returns:      Should calling further callbacks be stopped?
                Returns FALSE if subsequent callback calls are stopped.
 *---------------------------------------------------------------------------*/
static BOOL OnButtonHitCallBack_
( 
    u32                                 userParamater,
    const NNSG2dMultiCellHierarchyData* pNodeData,
    NNSG2dCellAnimation*                pCellAnim, 
    u16                                 nodeIdx 
)
{
#pragma unused( nodeIdx )

    NNSG2dSVec2       rectPos;
    
    const NNSG2dCellData*       pCD      = NNS_G2dGetCellAnimationCurrentCell( pCellAnim );
    const NNSG2dAnimController* pAnmCtrl = NNS_G2dGetCellAnimationAnimCtrl( pCellAnim );
    const NNSG2dAnimSequence*   pAnmSeq  = NNS_G2dGetAnimCtrlCurrentAnimSequence( pAnmCtrl );
    const u16                   seqIdx   = NNS_G2dGetAnimSequenceIndex( pAnimBank, pAnmSeq );
    
    const NNSG2dCellBoundingRectS16* pRect = NNS_G2dGetCellBoundingRect( pCD );
    
    rectPos.x = (s16)(pNodeData->posX + MY_MC_BASE_POS_X);
    rectPos.y = (s16)(pNodeData->posY + MY_MC_BASE_POS_Y);
    
    // Does this cell animation need collisiion detection?
    // 
    // define used for the animation sequence number listed in ../data/win_pnl_NANR_LBLDEFS.h 
    //  
    // Specify the -lbl option to the converter to output this data.
    if( seqIdx != NANR_win_pnl_Win_bse )
    {
        if( IsRectIntersect_( pRect, &pointerPos_, &rectPos ) )
        {
            // A pointer to the flag is passed as a user parameter, and the flag is updated here.
            //  
            BOOL* pHitFlag = (BOOL*)userParamater;
            OS_Printf("hit index = %d\n", seqIdx );
            *pHitFlag = TRUE;
            
            // Set pressed status for the cell animation image
            NNS_G2dSetCellAnimationCurrentFrame( pCellAnim, APP_UI_ANIMFRAME_IDX_PUSHED );
            
            
            // Stop calling further callbacks.
            return FALSE;            
        }else{
            // Set normal status for the cell animation image
            NNS_G2dSetCellAnimationCurrentFrame( pCellAnim, APP_UI_ANIMFRAME_IDX_NORMAL );
        }
    }
    
    return TRUE;
}

/*---------------------------------------------------------------------------*
  Name:         LoadResources

  Description:  This function carries out the building of the multi-cell animation
                and the reading of character data and palette data.

  Arguments:    ppMCAnim:   Pointer that receives the pointer to the multi-cell
                             

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void LoadResources()
{
    void* pBuf;

    //
    // Initialization related to MultiCell
    //
    {
        
        // Loads the cell data, cell animation, multicell data,
        // and multicell animation.
        // They are used on main memory until the end, so pBuf is not released.
        pBuf = G2DDemo_LoadNCER( &pCellBank, "data/win_pnl.NCER" );
        SDK_NULL_ASSERT( pBuf );
        pBuf = G2DDemo_LoadNANR( &pAnimBank, "data/win_pnl.NANR" );
        SDK_NULL_ASSERT( pBuf );
        pBuf = G2DDemo_LoadNMCR( &pMCBank, "data/win_pnl.NMCR" );
        SDK_NULL_ASSERT( pBuf );
        pBuf = G2DDemo_LoadNMAR( &pMCABank, "data/win_pnl.NMAR" );
        SDK_NULL_ASSERT( pBuf );

        //
        // Initializes the multicell animation entity.
        // Initializes two entities using different methods.
        //
        InitMultiCellInstance_( &mcAnim,
                                pAnimBank,
                                pCellBank,
                                pMCBank,
                                pMCABank,
                                NNS_G2D_MCTYPE_SHARE_CELLANIM  );
    }

    //
    // Initialization related to VRAM
    //
    {
        // Loading NCG (Character Data)
        {
            NNSG2dCharacterData*        pCharData = NULL;

            pBuf = G2DDemo_LoadNCGR( &pCharData, "data/win_pnl.NCGR" );
            SDK_NULL_ASSERT( pBuf );

            // Loading For 2D Graphics Engine.
            DC_FlushRange( pCharData->pRawData, pCharData->szByte );
            GX_LoadOBJ( (void*)pCharData->pRawData, 0, pCharData->szByte );

            // Character data were copied into VRAM,
            // this pBuf is freed. Same below.
            G2DDemo_Free( pBuf );
        }

        // Loading NCL (Palette Data)
        {
            NNSG2dPaletteData*        pPltData = NULL;

            pBuf = G2DDemo_LoadNCLR( &pPltData, "data/win_pnl.NCLR" );
            SDK_NULL_ASSERT( pBuf );

            // Loading For 2D Graphics Engine.
            DC_FlushRange( pPltData->pRawData, pPltData->szByte );
            GX_LoadOBJPltt( (void*)pPltData->pRawData, 0, pPltData->szByte );

            G2DDemo_Free( pBuf );
        }
    }
}


//--------------------------------------------------------------------------------------------------------
// Render application
static void AppDraw
( 
    const NNSG2dMultiCellAnimation* pMcAnim, 
    NNSG2dOamManagerInstance*       pOamMgr 
)
{
    u16                             numOamDrawn = 0;                  
    // Render pointer
    {
        const NNSG2dCellData* pCD = NNS_G2dGetCellDataByIdx( pCellBank, 0xd );
        NNSG2dFVec2           pointerPos; 
        
        pointerPos.x = pointerPos_.x << FX32_SHIFT;
        pointerPos.y = pointerPos_.y << FX32_SHIFT;
        
        //
        // Writes an OBJ list equivalent to MultiCell to temporary.
        //
        numOamDrawn = NNS_G2dMakeCellToOams(
                            tempOamBuffer,              // output destination Oam buffer
                            (u16)(NUM_OF_OAM - numOamDrawn),   // output destination buffer length
                            pCD,                        // output Cell
                            NULL,                       // affine conversion
                            &pointerPos,                // offset position
                            NULL,                       // Affine Index
                            FALSE );                    // double affine?

        SDK_ASSERT( numOamDrawn < NUM_OF_OAM );

        // Registers OBJ list to OAM Manager
        (void)NNS_G2dEntryOamManagerOam( pOamMgr, tempOamBuffer, numOamDrawn );
    }
    
    // Render multicell
    //
    // Draws MultiCell in the low level drawing method.
    // The drawing method that uses G2dRenderer of the upper level module.
    //
    //
    {
        
        
        const NNSG2dMultiCellInstance*  pMultiCell = &(pMcAnim->multiCellInstance); // MultiCell to display
        SDK_NULL_ASSERT( pMultiCell );

        //
        // Writes an OBJ list equivalent to MultiCell to temporary.
        //
        numOamDrawn = NNS_G2dMakeSimpleMultiCellToOams(
                            tempOamBuffer,       // output destination Oam buffer
                            NUM_OF_OAM,          // output destination buffer length
                            pMultiCell,          // output MultiCell
                            NULL,                // affine conversion
                            &MULTICELL_POSITION, // offset position
                            NULL,                // Affine Index
                            FALSE );             // double affine?

        SDK_ASSERT( numOamDrawn < NUM_OF_OAM );

        // Registers OBJ list to OAM Manager
        (void)NNS_G2dEntryOamManagerOam( pOamMgr, tempOamBuffer, numOamDrawn );
    }
    
    
}



   

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

  Description:  This is the main function.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void NitroMain()
{
    NNSG2dOamManagerInstance    oamManager;     // Oam manager entity
    
        
    // Initialize App.
    {
        G2DDemo_CommonInit();
        G2DDemo_PrintInit();
        InitOamManager( &oamManager );
        LoadResources();
        
        // start display
        {
            SVC_WaitVBlankIntr();
            GX_DispOn();
            GXS_DispOn();
        }
    }

    //----------------------------------------------------
    // Main loop
    while( TRUE )
    {
        NNSG2dMultiCellAnimation*      pMcAnim      = &mcAnim;
        
        //
        //  ------ Pad handling.
        //
        {
            G2DDemo_ReadGamePad();

            // change animation speed.
            {
                if (G2DDEMO_IS_PRESS(PAD_KEY_UP))
                {
                    pointerPos_.y -= 1;
                }
                
                if (G2DDEMO_IS_PRESS(PAD_KEY_DOWN))
                {
                    pointerPos_.y += 1;
                }
                
                if (G2DDEMO_IS_PRESS(PAD_KEY_LEFT))
                {
                    pointerPos_.x -= 1;
                }
                
                if (G2DDEMO_IS_PRESS(PAD_KEY_RIGHT))
                {
                    pointerPos_.x += 1;
                }
            }
        }
        
        //  Collision detection
        // Call the callback function used to perform collision detection for each node in a multicell and reaction processing when there is a collision.
        //  
        // Pass a pointer to the flag as a user parameter and update the flag if a collision has been detected within the callback.
        // Updates the flag
        bHit_       = FALSE;
        NNS_G2dTraverseMCNodes( &pMcAnim->multiCellInstance, OnButtonHitCallBack_, (u32)&bHit_ );

        //  ------ Drawing
        AppDraw( pMcAnim, &oamManager );
        
        
        
        // Draw the debug text string
        {
            if( bHit_ )
            {
                G2DDemo_PrintOutf(0, 0, "Hit");
            }else{
                G2DDemo_PrintOutf(0, 0, "   ");
            }
        }

        // ------ Wait for V-Blank
        SVC_WaitVBlankIntr();
        G2DDemo_PrintApplyToHW();   // Writes the debug character string data to HW.

        // ------ Write manager contents to HW.
        {
            NNS_G2dApplyOamManagerToHW          ( &oamManager );
            NNS_G2dResetOamManagerBuffer        ( &oamManager );
        }
    }
}


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

  Description:  Handles V-blank interrupts.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void VBlankIntr(void)
{
    OS_SetIrqCheckFlag( OS_IE_V_BLANK );                   // checking VBlank interrupt
}


