/*---------------------------------------------------------------------------*
  Project:   NITRO-System - build - demos - g2d - samples - CellAnimation
  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.15  06/29/2005 05:59:56  kitani_toshikazu
  Cleaned up source code and added comments.

  
  Revision 1.2  06/14/2004 08:44:55  hatamoto_minoru
  Added sample that uses callback functions.

  Revision 1.1  06/14/2004 06:23:49  hatamoto_minoru
  Registered the initial version.

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

// ============================================================================
//  Explanation of the demo:
//      Example of rendering and updating cell animation
//
//  Operation Method
//      +Control Pad key, up and down: Change playback speed
//      Crosskey left/right   Change playback sequence
//      A               Toggle between play and stop
//      B               Toggle between forward and reverse playback
//      X               Switch callback type
//  Description
//      Plays back cell animation. Also indicates various types of playback control features and callback calling features.
//       
// ============================================================================


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

#include "g2d_demolib.h"


//------------------------------------------------------------------------------
// Constants
#define NUM_OF_OAM              48                      // Number of OAMs controlled by OAM Manager

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

#define CALLBACK_PARAM          0x12345678              // Callback function parameter
#define CALL_CALLBACK_FRAME     3                       // The frame number at which the callback function is to be called
                                                        // when NNS_G2D_ANMCALLBACKTYPE_SPEC_FRM has been set
                                                        
#define TEX_BASE                0x0                     // Texture base address
#define TEX_PLTT_BASE           0x0                     // Texture palette base address

//------------------------------------------------------------------------------
// Global members
static NNSG2dOamManagerInstance objOamManager;          // OAM manager for OBJ output

static NNSG2dImageProxy         sImageProxy;            // Character/texture proxy for cell
static NNSG2dImagePaletteProxy  sPaletteProxy;          // Palette proxy for cell

// Callback type names
static const char* CALL_BACK_TYPE_NAME[] = {
    "NONE    ",     // NNS_G2D_ANMCALLBACKTYPE_NONE
    "LAST_FRM",     // NNS_G2D_ANMCALLBACKTYPE_LAST_FRM
    "SPEC_FRM",     // NNS_G2D_ANMCALLBACKTYPE_SPEC_FRM
    "EVER_FRM",     // NNS_G2D_ANMCALLBACKTYPE_EVER_FRM
};

// Cell display location
static const NNSG2dFVec2        CELL_POSITION = {120 << FX32_SHIFT, 120 << FX32_SHIFT};

// Number of times the animation callback has been called
static int                      callCount = 0;

// Animation callback type
static NNSG2dAnmCallbackType    callBackType = NNS_G2D_ANMCALLBACKTYPE_NONE;


/*---------------------------------------------------------------------------*
  Name:         CallBackFunction

  Description:  This function is called back from the animation controller.

  Arguments:    param:          This user parameter is specified when the callback
                                function was registered.
                currentFrame:   If the callback type is
                                NNS_G2D_ANMCALLBACKTYPE_LAST_FRM,
                                currentTime of the animation controller
                                will be passed as fx32 type.
                                In all other cases, the current frame number will be passed as a u16 type variable.
                                 

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void CallBackFunction( u32 param, fx32 currentFrame )
{
#pragma unused( param )
#pragma unused( currentFrame )

    // Updates the number of times the callback has been called
    callCount++;
    
    OS_Printf("CallBackFunction is called: param=0x%p, frame=0x%p\n", param, currentFrame);
}

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

  Description:  Initializes the OAM manager system.

  Arguments:    pOamManager:    Pointer to the OAM manager to be initialized for OBJ output.
                                 
  Returns:      None.
 *---------------------------------------------------------------------------*/
static void InitOamManager( NNSG2dOamManagerInstance* pObjManager )
{
    BOOL    result;

    // Initializes the Oam Manager system
    NNS_G2dInitOamManagerModule();

    
    result = NNS_G2dGetNewOamManagerInstanceAsFastTransferMode( pObjManager, 
                                                                0, 
                                                                NUM_OF_OAM, 
                                                                NNS_G2D_OAMTYPE_MAIN );    
    SDK_ASSERT( result );
}

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

  Description:  This function carries out the following initializations:
                  Reading of cell animation data
                  Initialization of cell animation
                  Reading of character data and palette data

  Arguments:    ppAnimBank: Pointer that recieves a pointer to cell animation data.
                             
                ppCellAnim: Pointer for receiving the pointer to the cell animation.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void LoadResources(NNSG2dAnimBankData** ppAnimBank, NNSG2dCellAnimation** ppCellAnim)
{
    void* pBuf;

    NNS_G2dInitImageProxy( &sImageProxy );
    NNS_G2dInitImagePaletteProxy( &sPaletteProxy );

    //
    // Initialization of cell related resources.
    //
    {
        NNSG2dCellDataBank*          pCellBank = NULL;

        // Load cell data and cell animation data.
        // Cell data and cell animation data is used until the end by the
        // main memory so these pBufs are not released.
        pBuf = G2DDemo_LoadNCER( &pCellBank, "data/CellAnimation.NCER" );
        SDK_NULL_ASSERT( pBuf );
        pBuf = G2DDemo_LoadNANR( ppAnimBank, "data/CellAnimation.NANR" );
        SDK_NULL_ASSERT( pBuf );

        //
        // Initialize cell animation entity
        //
        {
            *ppCellAnim = G2DDemo_GetNewCellAnimation(1);
            SDK_NULL_ASSERT( *ppCellAnim );

            SDK_NULL_ASSERT( NNS_G2dGetAnimSequenceByIdx(*ppAnimBank, 0) );

            NNS_G2dInitCellAnimation(
                *ppCellAnim,
                NNS_G2dGetAnimSequenceByIdx(*ppAnimBank, 0),
                pCellBank );
        }
    }

    //
    // Initialization related to VRAM
    //
    {
        //------------------------------------------------------------------------------
        // load character data for 2D
        {
            NNSG2dCharacterData*        pCharData;

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

            // Loading For 2D Graphics Engine.
            DC_FlushRange( pCharData->pRawData, pCharData->szByte );
            GX_LoadOBJ( (void*)pCharData->pRawData, TEX_BASE, pCharData->szByte );
            
            // Character data were copied into VRAM,
            // this pBuf is freed. Same below.
            G2DDemo_Free( pBuf );
        }

        //------------------------------------------------------------------------------
        // load palette data
        {
            NNSG2dPaletteData*        pPlttData;

            pBuf = G2DDemo_LoadNCLR( &pPlttData, "data/CellAnimation.NCLR" );
            SDK_NULL_ASSERT( pBuf );

            DC_FlushRange( pPlttData->pRawData, pPlttData->szByte );
            GX_LoadOBJPltt( (void*)pPlttData->pRawData, TEX_PLTT_BASE, pPlttData->szByte );
            
            G2DDemo_Free( pBuf );

        }
    }
}

/*---------------------------------------------------------------------------*
  Name:         ChangeCallBackType

  Description:  Changes the callback type.

  Arguments:    pAnimCtrl:      Pointer to the animation controller for which the callback type is to be changed.
                                 
                callBackType:   New callback type.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void ChangeCallBackType
(
    NNSG2dAnimController*   pAnimCtrl,
    NNSG2dAnmCallbackType   callBackType
)
{
    //
    // Initialize = reset
    //
    NNS_G2dInitAnimCtrlCallBackFunctor( pAnimCtrl );


    switch( callBackType )
    {
    case NNS_G2D_ANMCALLBACKTYPE_NONE:
        break;

    case NNS_G2D_ANMCALLBACKTYPE_LAST_FRM:
    case NNS_G2D_ANMCALLBACKTYPE_EVER_FRM:
        NNS_G2dSetAnimCtrlCallBackFunctor(
            pAnimCtrl,
            callBackType,
            CALLBACK_PARAM,
            CallBackFunction );
        break;

    case NNS_G2D_ANMCALLBACKTYPE_SPEC_FRM:
        NNS_G2dSetAnimCtrlCallBackFunctorAtAnimFrame(
            pAnimCtrl,
            CALLBACK_PARAM,
            CallBackFunction,
            CALL_CALLBACK_FRAME );
        break;

    default:
        SDK_ASSERT(FALSE);
        break;
    }
}

/*---------------------------------------------------------------------------*
  Name:         ProcessInput

  Description:  Loads the pad input and changes animation parameters
                according to the input.

  Arguments:    pIndex:         Pointer to the sequence index.
                                Use the left/right keys to switch the sequence.
                pAnimSpeed:     Pointer to the animation speed
                                Changes the speed with up and down keys.
                pAnimCtrl:      Pointer to the animation controller.
                                Stop/reverse with A/B Buttons

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void ProcessInput(u16* pIndex, fx32* pAnimSpeed, NNSG2dAnimController* pAnimCtrl )
{
    G2DDemo_ReadGamePad();

    // change animation sequence.
    {
        if(G2DDEMO_IS_TRIGGER(PAD_KEY_RIGHT))
        {
            (*pIndex)++;
        }

        if(G2DDEMO_IS_TRIGGER(PAD_KEY_LEFT))
        {
            (*pIndex)--;
        }
    }

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

        if (G2DDEMO_IS_TRIGGER(PAD_KEY_DOWN))
        {
            *pAnimSpeed -= ANIM_SPEED_UNIT;

            if( *pAnimSpeed < 0 )
            {
                *pAnimSpeed = 0;
            }
        }
    }

    // Playback control
    {
        if (G2DDEMO_IS_TRIGGER(PAD_BUTTON_B ))
        {
            pAnimCtrl->bReverse = ! pAnimCtrl->bReverse;
        }

        if (G2DDEMO_IS_TRIGGER(PAD_BUTTON_A ))
        {
            pAnimCtrl->bActive = ! pAnimCtrl->bActive;
        }
    }

    // Change the callback type
    if( G2DDEMO_IS_TRIGGER(PAD_BUTTON_X) )
    {
        callBackType++;
        if( callBackType >= AnmCallbackType_MAX )
        {
            callBackType = NNS_G2D_ANMCALLBACKTYPE_NONE;
        }
        ChangeCallBackType(pAnimCtrl, callBackType);
    }
}

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

  Description:  This is the main function.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void NitroMain()
{
    NNSG2dAnimBankData*         pAnimBank = NULL;   // Cell animation data
    NNSG2dCellAnimation*        pCellAnim = NULL;   // CellAnimation instance
    

    // Initialize App.
    {
        G2DDemo_CommonInit();
        G2DDemo_PrintInit();
        G2DDemo_CameraSetup();
        G2DDemo_MaterialSetup();
        InitOamManager( &objOamManager );
        LoadResources( &pAnimBank, &pCellAnim );
        
        // start display
        SVC_WaitVBlankIntr();
        GX_DispOn();
        GXS_DispOn();
    }

    //----------------------------------------------------
    // Main loop
    while( TRUE )
    {
        static u16      currentSeqIdx = 0;       // Rendering sequence number
        static fx32     animSpeed     = FX32_ONE;// Animation speed

        //-------------------------------------------------
        // Pad input
        //
        {
            const u16 oldSeqIndex = currentSeqIdx;
            const u16 numSequence = NNS_G2dGetNumAnimSequence( pAnimBank );

            ProcessInput(
                &currentSeqIdx,
                &animSpeed,
                NNS_G2dGetCellAnimationAnimCtrl(pCellAnim) );

            if( (s16)currentSeqIdx >= numSequence )
            {
                currentSeqIdx= 0;
            }

            if( (s16)currentSeqIdx < 0 )
            {
                currentSeqIdx = (u16)(numSequence - 1);
            }
             
            //
            // Will respond appropriately if there are changes
            //
            if( oldSeqIndex != currentSeqIdx )
            {
                const NNSG2dAnimSequence* pSeq = NULL;

                pSeq = NNS_G2dGetAnimSequenceByIdx( pAnimBank, currentSeqIdx );
                SDK_NULL_ASSERT( pSeq );
                NNS_G2dSetCellAnimationSequence( pCellAnim, pSeq );
                NNS_G2dStartAnimCtrl( NNS_G2dGetCellAnimationAnimCtrl( pCellAnim ) );
            }
        }
        
        
        //-------------------------------------------------
        // Drawing
        //
        {
            u16                     numOamDrawn = 0;  // Number of OAMs that have been rendered
            static GXOamAttr        temp[NUM_OF_OAM]; // temporary OAM buffer
            const NNSG2dCellData*   pCell = NULL;     // Cell displayed

            pCell = NNS_G2dGetCellAnimationCurrentCell( pCellAnim );
            SDK_NULL_ASSERT( pCell );

            //
            // Writes an OBJ list equivalent to the cell to the temporary buffer.
            //
            numOamDrawn = NNS_G2dMakeCellToOams(
                                temp,           // output destination Oam buffer
                                NUM_OF_OAM,     // output destination buffer length
                                pCell,          // output Cell
                                NULL,           // affine conversion
                                &CELL_POSITION, // offset position
                                NULL,           // Affine Index
                                FALSE );        // double affine?

            SDK_ASSERT( numOamDrawn < NUM_OF_OAM);

            // Registers OBJ list to OAM Manager
            (void)NNS_G2dEntryOamManagerOam( &objOamManager, temp, numOamDrawn );
        }

        //-------------------------------------------------
        // Debug character string drawing
        //
        {
            const NNSG2dAnimController* pAnimCtrl = NNS_G2dGetCellAnimationAnimCtrl(pCellAnim);
            
            G2DDemo_PrintOutf(0, 0, "sequence: %3d / %3d", currentSeqIdx, NNS_G2dGetNumAnimSequence( pAnimBank ));
            G2DDemo_PrintOutf(0, 1, "speed:    %7.3f", (float)animSpeed / FX32_ONE );
            G2DDemo_PrintOutf(0, 2, "frame:    %3d / %3d",
                pAnimCtrl->pCurrent - pAnimCtrl->pAnimSequence->pAnmFrameArray,
                pAnimCtrl->pAnimSequence->numFrames );
            G2DDemo_PrintOutf(0, 3, "time:     %3d / %3d",
                pAnimCtrl->currentTime >> FX32_SHIFT,
                pAnimCtrl->pCurrent->frames);
            G2DDemo_PrintOut(22, 0, "Active:");
            G2DDemo_PrintOut(29, 1, NNS_G2dIsAnimCtrlActive(pAnimCtrl) ? "yes": " no");
            G2DDemo_PrintOut(22, 2, "Reverse:");
            G2DDemo_PrintOut(29, 3, pAnimCtrl->bReverse ? "yes": " no");
            
            G2DDemo_PrintOutf(0, 21, "callback type:  %s",
                CALL_BACK_TYPE_NAME[callBackType]);
            G2DDemo_PrintOutf(0, 22, "callback count: %4d", 0);
            
            G2DDemo_PrintOutf(16, 22, "%5d", callCount);
        }
        
        //-------------------------------------------------
        // Wait for V-Blank
        G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_Z);
        SVC_WaitVBlankIntr();
            
        //-------------------------------------------------
        // Write manager contents to HW.
        //            
        {
            NNS_G2dApplyOamManagerToHW( &objOamManager );
            NNS_G2dResetOamManagerBuffer( &objOamManager );
        }

        //-------------------------------------------------
        // Writes the debug character string to HW.
        //
        G2DDemo_PrintApplyToHW();
        
        //-------------------------------------------------
        // Update animation.
        // 
        {
            NNS_G2dTickCellAnimation( pCellAnim, animSpeed );
        }
    }
}


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

  Description:  Handles V-blank interrupts.

  Arguments:    None.

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


