/*---------------------------------------------------------------------------*
  Project:   NITRO-System - build - demos - g2d - samples - RendererCore
  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  08/18/2005 00:07:11  kitani_toshikazu
  Made revisions to support changes to parameters of multi-cell callback functions.

  Revision 1.1  07/07/2005 07:04:00  kitani_toshikazu
  Registered initial issue

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

// ============================================================================
//  Explanation of the demo:
//      This sample shows how to use user-extended attribute data.
//      Output user-extended attribute data using the the new -oua option for the converter.
//       
//      
//      It is necessary to add extended comments to cells and animation frames in order to use user-extended attribute data.
//       
//      For this reason, it is necessary to create data using the new version of NITRO=CHARACTER which allows the addition of extended comments.
//       
//
//      In this sample, multiple types of attribute values are retreived for each cell animation in the multi-cell and the results are displayed using the debug string.
//       
//      
//      There are many conceivable applications in an actual game project, including:
//          - Trigger for sound playback
//          - Storing the size used for collision detection
//       
//
//  Operation Method
//      +Control Pad key, up and down: Change playback speed
//      Y                Changes the method of getting the user-extended attribute
// ============================================================================

#include <nitro.h>
#include <nnsys/g2d.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

#define ANIM_SPEED_UNIT         (FX32_ONE >> 3)     // Change difference of animation speed

#define NUM_CELL_ANIMATION      10

//
// Used when passing data to the callback parameter of the animation controller, a pointer to this structure is passed to the data structure controller.
//  
//  
//
typedef struct MyAnimCtrlData
{
    NNSG2dAnimController*       pAnmCtrl;       // Animation controller
    u16                         idx;            // Cell animation number
    u16                         pad16;          // The padding
    BOOL                        bAttributeIsNotZero;// Has a non-zero value attribute been detected?
    
}MyAnimCtrlData;
static MyAnimCtrlData              animDataArray[NUM_CELL_ANIMATION];


// Cell display location
static const NNSG2dFVec2           MULTICELL_POSITION = {120 << FX32_SHIFT, 150 << FX32_SHIFT};
//
// MultiCellAnimation
// Initializes instances using two different methods.
// 
static NNSG2dMultiCellAnimation   mcAnim;


// Buffer when using OAM attributes
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

//
// User extended attributes
//
static const NNSG2dUserExCellAttrBank*  pCellAttrBank = NULL;
static const NNSG2dUserExAnimAttrBank*  pAnimAttrBank = NULL;

//
// Methods of retrieving extended attributes
//
typedef enum MY_GET_ATTRIBUTE_MODE
{
    MY_GET_ATTRIBUTE_MODE_CELL,    // Acess extended attributes of a cell
    MY_GET_ATTRIBUTE_MODE_FRAME,   // Animation Frame
                                   // Access extended attributes
    MY_GET_ATTRIBUTE_MODE_MAX

}MY_GET_ATTRIBUTE_MODE;
   
static MY_GET_ATTRIBUTE_MODE    attributeMode = MY_GET_ATTRIBUTE_MODE_CELL;


//------------------------------------------------------------------------------
// 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);
    }
}

//------------------------------------------------------------------------------
// Callback function for setting cell animation during multi-cell
// Gets user-extended attributes according to the execution mode of the application and save whether or not attribute values are outside the specified value range as data.
//  
//  
//
static void CellAnimCallBackFunction( u32 param, fx32 currentFrame )
{
#pragma unused(currentFrame)
    
    MyAnimCtrlData*             pAnmCtrlData    = (MyAnimCtrlData*)param;
    const NNSG2dAnimController* pAnmCtrl        = pAnmCtrlData->pAnmCtrl;
     
    u32 attrVal;
    
    //
    // Depending on the method used to get attributes...
    //
    switch( attributeMode )
    {
    // Access cell extended attributes
    case MY_GET_ATTRIBUTE_MODE_CELL:
        {
            // Get the current cell number
            const u16 cellIdx = NNS_G2dGetAnimCtrlCurrentElemIdxVal( pAnmCtrl );
            // Get the attribute corresponding to the cell number
            const NNSG2dUserExCellAttr* pAttr
                = NNS_G2dGetUserExCellAttr( pCellAttrBank, cellIdx );
            attrVal = NNS_G2dGetUserExCellAttrValue( pAttr );
            
            break;
        }
    // Access animation frame extended attribute
    case MY_GET_ATTRIBUTE_MODE_FRAME:
        {
            // Get the current sequence number
            const NNSG2dAnimSequence* pSeq 
                = NNS_G2dGetAnimCtrlCurrentAnimSequence( pAnmCtrl );
            const u16 seqIdx 
                = NNS_G2dGetAnimSequenceIndex( pAnimBank, pSeq );
            // Get the sequence extended attribute corresponding to the animation sequence
            // (Although not used in this sample, attribute values can be retrieved even from
            // sequence extended attributes.)
            const NNSG2dUserExAnimSequenceAttr* pSeqAttr
                = NNS_G2dGetUserExAnimSequenceAttr( pAnimAttrBank, seqIdx );
                
            // Get animation frame number during playback
            const u16 currenFrmIdx = NNS_G2dGetAnimCtrlCurrentFrame( pAnmCtrl );
            // Get the sequence extended attribute and frame extended attribute from the frame number
            //  
            const NNSG2dUserExAnimFrameAttr*  pFrmAttr
                = NNS_G2dGetUserExAnimFrameAttr( pSeqAttr, currenFrmIdx );
            attrVal = NNS_G2dGetUserExAnimFrmAttrValue( pFrmAttr );
            
            break;
        }
    }
        
    //
    // Record the fact that the attribute value retrieved is not zero.
    //  
    //
    if( attrVal != 0 )
    { 
        pAnmCtrlData->bAttributeIsNotZero = TRUE;
    }else{
        pAnmCtrlData->bAttributeIsNotZero = FALSE;
    }
}

//------------------------------------------------------------------------------
// 
/*---------------------------------------------------------------------------*
  Name:         SetupMCCellAnimCallBackFunc

  Description:  This function is called for the animation of each cell passed as parameters to the callback function NNS_G2dTraverseMCCell (function for traversing multicell animation).
                 
                 
                 
                
                The callback function to use for an animation controller that has cell animation is set inside this function.
                 
                 

  Arguments:    userParamater : User parameters
                                (In this sample, a pointer to animDataArray is used.)
                pCellAnim             :      Cell animation during multi-cell animation
                idx         :           Cell animation number

  Returns:      Halt callback call?
                If the callback invocation is cancelled, returns FALSE. 
 *---------------------------------------------------------------------------*/
static BOOL SetupMCCellAnimCallBackFunc
(
    u32                  userParamaters,
    NNSG2dCellAnimation* pCellAnim,
    u16                  idx
)
{
#pragma unused( userParamaters )
    
    //
    // In this sample demo, a pointer to animDataArray is used in place of userParameters.
    //  
    //
    MyAnimCtrlData* pAnmDataArray = (MyAnimCtrlData*)userParamaters;
    
    // Gets the animation controller from cell animation.
    //  
    NNSG2dAnimController* pAnmCtrl
        = NNS_G2dGetCellAnimationAnimCtrl( pCellAnim );

    // Initialize the animation controller functor
    NNS_G2dInitAnimCtrlCallBackFunctor( pAnmCtrl );
    
    // Store data in the user-defined structure used as a parameter of the callback functor.
    //  
    pAnmDataArray[idx].pAnmCtrl = pAnmCtrl;
    pAnmDataArray[idx].idx      = idx;
    
    // Set the parameters and callback function for the animation controller functor.
    //  
    NNS_G2dSetAnimCtrlCallBackFunctor
    ( 
        pAnmCtrl,
        NNS_G2D_ANMCALLBACKTYPE_EVER_FRM,
        (u32)&pAnmDataArray[idx],
        CellAnimCallBackFunction
    );
    
    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
                            animation built.

  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/MultiCellAnimation.NCER" );
        SDK_NULL_ASSERT( pBuf );
        pBuf = G2DDemo_LoadNANR( &pAnimBank, "data/MultiCellAnimation.NANR" );
        SDK_NULL_ASSERT( pBuf );
        pBuf = G2DDemo_LoadNMCR( &pMCBank, "data/MultiCellAnimation.NMCR" );
        SDK_NULL_ASSERT( pBuf );
        pBuf = G2DDemo_LoadNMAR( &pMCABank, "data/MultiCellAnimation.NMAR" );
        SDK_NULL_ASSERT( pBuf );

        
        //
        // Get extended attribute bank
        // Convert data using the converter option -oua.
        //   
        //
        // Although data is retrieved at this point from the cell bank and animation bank,
        // data can also be retrieved in the same manner from the multi-cell bank and multi-animation bank.
        //  
        //
        // Get extended attribute cell bank
        pCellAttrBank = NNS_G2dGetUserExCellAttrBankFromCellBank( pCellBank ); 
        // Get extended attribute animation bank
        pAnimAttrBank = NNS_G2dGetUserExAnimAttrBank( pAnimBank );

        //
        // Initializes the multicell animation entity.
        // Initializes two entities using different methods.
        //
        InitMultiCellInstance_( &mcAnim,
                                pAnimBank,
                                pCellBank,
                                pMCBank,
                                pMCABank,
                                NNS_G2D_MCTYPE_SHARE_CELLANIM  );
        
        //
        // Call this callback function to traverse cell animation consisting of multi-cells and to perform user-specific initialization for each cell animation.
        //  
        //  
        //
        // A pointer to animDataArray is passed as a user parameter.
        // The data in animDataArray is overwritten inside the callback using this pointer.
        NNS_G2dTraverseMCCellAnims( &mcAnim.multiCellInstance,
                                    SetupMCCellAnimCallBackFunc,
                                    (u32)(&animDataArray) );
    }

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

            pBuf = G2DDemo_LoadNCGR( &pCharData, "data/MultiCellAnimation.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/MultiCellAnimation.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 );
        }
    }
}


//--------------------------------------------------------------------------------------------------------
//Multi-cell rendering
static void AppDraw
(
    const NNSG2dMultiCellAnimation* pMcAnim,
    NNSG2dOamManagerInstance*       pOamMgr
)
{
    // Drawing
    //
    // Draws MultiCell in the low level drawing method.
    // The drawing method that uses G2dRenderer of the upper level module.
    //
    //
    {
        u16                             numOamDrawn = 0;
        
        // MultiCell to display
        const NNSG2dMultiCellInstance*  pMultiCell = &(pMcAnim->multiCellInstance);
        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 )
    {
        static fx32                    animSpeed    = FX32_ONE; // Animation speed
        NNSG2dMultiCellAnimation*      pMcAnim      = &mcAnim;
        
        //
        //  Pad handling.
        //
        {
            G2DDemo_ReadGamePad();

            // change animation speed.
            {
                if (G2DDEMO_IS_TRIGGER(PAD_KEY_UP))
                {
                    animSpeed +=ANIM_SPEED_UNIT;
                }

                if (G2DDEMO_IS_TRIGGER(PAD_KEY_DOWN))
                {
                    animSpeed -= ANIM_SPEED_UNIT;

                    if( animSpeed < 0 )
                    {
                        animSpeed = 0;
                    }
                }
            }
            
            // Change the method of retrieving extended attributes.
            if (G2DDEMO_IS_TRIGGER(PAD_BUTTON_Y ))
            {
                attributeMode
                    = (MY_GET_ATTRIBUTE_MODE)((++attributeMode) % MY_GET_ATTRIBUTE_MODE_MAX);
            }
        }

        //  Drawing
        AppDraw( pMcAnim, &oamManager );
        //  Update the animation.
        NNS_G2dTickMCAnimation( &mcAnim, animSpeed );
        
        
        
        // Draw the debug text string
        {
            const NNSG2dAnimController* pAnimCtrl = NNS_G2dGetMCAnimAnimCtrl(pMcAnim);

            G2DDemo_PrintOutf(0, 0, "speed: %7.3f", (float)animSpeed / FX32_ONE );
            G2DDemo_PrintOutf(0, 1, "frame: %3d / %3d",
                pAnimCtrl->pCurrent - pAnimCtrl->pAnimSequence->pAnmFrameArray,
                pAnimCtrl->pAnimSequence->numFrames );
            
            //
            // If extended attributes other than the specified value of zero for cell animation is being recognized, o is displayed, otherwise, x is displayed
            //  
            //  
            //
            {
                int i;
                G2DDemo_PrintOutf( 0, 3,      "-----------------" );
                G2DDemo_PrintOutf( 0, 4,      "attribute " );
                G2DDemo_PrintOutf( 0, 5,      "-----------------" );
                
                for( i = 0; i < NUM_CELL_ANIMATION; i++ )
                {
                    if( animDataArray[i].bAttributeIsNotZero == TRUE )
                    {
                        G2DDemo_PrintOutf( 0, 6+i, "%02d:o", i );
                    }else{
                        G2DDemo_PrintOutf( 0, 6+i, "%02d:x", i );
                    }
                }
                G2DDemo_PrintOutf( 0, 6 + NUM_CELL_ANIMATION, "----- " );
            }
        }

        // Wait for V-Blank
        SVC_WaitVBlankIntr();
        G2DDemo_PrintApplyToHW();   // Writes the debug text 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
}


