/*--------------------------------------------------------------------------
  Project:  Manual 2D Library - m2d
  File:     m2d_objG2d.cpp

  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: 1758 $
 *-------------------------------------------------------------------------*/

#include <nnsys/g2d.h>
#include <nnsys/misc.h>
#include <ntmv/m2d/objManager.h>
#include <ntmv/m2d/common.h>
#include <ntmv/m2d/objectConstructor.h>
#include <ntmv/m2d/resG2dLoader.h>
#include <ntmv/m2d/uiResDefines.h>
#include <cstring>

// Filename for the OBJ graphics resource read from ROM
#define RESOURCE_MAIN_FILE_TITLE    "nlv_skin"

#define RESOURCE_SUB_FILE_TITLE     "nlv_skin_up_obj"

#define NTMV_M2D_OBJROOTDIR         NTMV_M2D_UIROOTDIR "/obj/"

namespace ntmv
{
namespace m2d
{

/*---------------------------------------------------------------------------*
  Name:         ObjManager::ObjManager

  Description:  Constructor.
                
  Arguments:    None.
    
  Returns:      None.
 *---------------------------------------------------------------------------*/
ObjManager::ObjManager()
:   mpAllocator(0),
    mpCellFile(0),
    mpAnimFile(0),
    mpCellBank(0),
    mpAnimBank(0),
    mNumCellAnimationUsed(0)
{   
    std::memset(mCellAnimationSet, 0, sizeof(mCellAnimationSet));
    std::memset(&mOamManager, 0, sizeof(mOamManager));
    std::memset(mTempOamBuffer, 0, sizeof(mTempOamBuffer));
}

void
ObjManager::LoadCommonRes()
{
    NNSG2dPaletteData* pPlttData = 0;
    void* pPlttFile = 0;

    //--------------------------------------------
    // load palette data
    {
        //   When loading a palette, set a specific palette entry as a favorite in the System Settings.
        //   
        char nclrFileNameBuf[64];
        STD_TSNPrintf(
            nclrFileNameBuf,
            sizeof(nclrFileNameBuf),
            NTMV_M2D_OBJROOTDIR "change_color_%02u" ".NCLR",
            detail::GetOwnerFavoriteColor());

        pPlttFile = detail::LoadNCLR(&pPlttData, nclrFileNameBuf/* , mpAllocator */);
        NNS_NULL_ASSERT( pPlttFile );
    }
    {
        NNSG2dImagePaletteProxy paletteProxy;
        NNS_G2dInitImagePaletteProxy(&paletteProxy);
        
        // Main
        NNS_G2dLoadPalette(
            pPlttData,
            0x0,
            NNS_G2D_VRAM_TYPE_2DMAIN,
            &paletteProxy);

        // Sub
        NNS_G2dLoadPalette(
            pPlttData,
            0x0,
            NNS_G2D_VRAM_TYPE_2DSUB,
            &paletteProxy);
    }

    // BG data
    // Load characters and screens from the file into main memory
    // Palettes are common
    {
        NNSG2dCharacterData*    pChrData;
        void *const pChrFile = detail::LoadNCGR(&pChrData, NTMV_M2D_OBJROOTDIR "nlv_skin_bg" ".NCGR"/* , mpAllocator */);
        NNS_NULL_ASSERT(pChrFile);

        // Load into the VRAM for main BG0
        {
            NNSG2dScreenData* pScnData;
            void *const pScnFile = detail::LoadNSCR(&pScnData, NTMV_M2D_OBJROOTDIR "nlv_skin_bg_03" ".NSCR"/* , mpAllocator */);
            NNS_NULL_ASSERT(pScnFile);
            NNS_G2dBGLoadElementsEx(NNS_G2D_BGSELECT_MAIN0, pScnData, pChrData, pPlttData, NULL, NULL);
            // NNS_FndFreeToAllocator(mpAllocator, pScnFile);
        }

        // Load into the VRAM for main BG1
        {
            NNSG2dScreenData* pScnData;
            void *const pScnFile = detail::LoadNSCR(&pScnData, NTMV_M2D_OBJROOTDIR "nlv_skin_bg_02" ".NSCR"/* , mpAllocator */);
            NNS_NULL_ASSERT(pScnFile);
            NNS_G2dBGLoadElementsEx(NNS_G2D_BGSELECT_MAIN1, pScnData, NULL, NULL, NULL, NULL);
            // NNS_FndFreeToAllocator(mpAllocator, pScnFile);
        }

        // Load into the VRAM for main BG2
        {
            NNSG2dScreenData* pScnData;
            void *const pScnFile = detail::LoadNSCR(&pScnData, NTMV_M2D_OBJROOTDIR "nlv_skin_bg_00" ".NSCR"/* , mpAllocator */);
            NNS_NULL_ASSERT(pScnFile);
            NNS_G2dBGLoadElementsEx(NNS_G2D_BGSELECT_MAIN2, pScnData, NULL, NULL, NULL, NULL);
            // NNS_FndFreeToAllocator(mpAllocator, pScnFile);
        }

        // NNS_FndFreeToAllocator(mpAllocator, pChrFile); // unload character data
    }

    // NNS_FndFreeToAllocator(mpAllocator, pPlttFile);    // unload palette data
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::Initialize()

  Description:  Initializes the module.
                Performs internal module initialization and loads resources.
                Call this after VRAM has been configured.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void
ObjManager::InitializeMain(NNSFndAllocator* pAllocator)
{
    mpAllocator = pAllocator;

    BOOL result;

    result = NNS_G2dGetNewOamManagerInstanceAsFastTransferMode( &mOamManager, 
                                                                0, 
                                                                OBJMANAGER_NUM_OAM, 
                                                                NNS_G2D_OAMTYPE_MAIN );    

    // Load resources

    //----------------------------------------------
    // load cell data
    mpCellFile = detail::LoadNCER( &mpCellBank, NTMV_M2D_OBJROOTDIR RESOURCE_MAIN_FILE_TITLE".NCER"/* , mpAllocator */);
    NNS_NULL_ASSERT(mpCellFile);

   
    //--------------------------------------------
    // load animation data
    mpAnimFile = detail::LoadNANR( &mpAnimBank, NTMV_M2D_OBJROOTDIR RESOURCE_MAIN_FILE_TITLE".NANR"/* , mpAllocator */);
    NNS_NULL_ASSERT(mpAnimFile);

    //----------------------------------------------
    // load character data for 2D
    {
        NNSG2dCharacterData* pCharData;

        void *const pBuf = detail::LoadNCGR( &pCharData, NTMV_M2D_OBJROOTDIR RESOURCE_MAIN_FILE_TITLE".NCGR"/* , mpAllocator */);
        NNS_NULL_ASSERT(pBuf);

        // Loading For 2D Graphics Engine
        NNSG2dImageProxy             imageProxy;
        // Main
        NNS_G2dInitImageProxy( &imageProxy );
        NNS_G2dLoadImage1DMapping( pCharData, 0x0, NNS_G2D_VRAM_TYPE_2DMAIN, &imageProxy );

        // Because character data was copied into VRAM,
        // this pBuf is freed. Same below.
        // NNS_FndFreeToAllocator(mpAllocator, pBuf);
    }

}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::InitializeSub()

  Description:  Initializes the module.
                Performs internal module initialization and loads resources.
                Call this after VRAM has been configured.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void
ObjManager::InitializeSub(NNSFndAllocator* pAllocator)
{
    mpAllocator = pAllocator;

    BOOL result;

    result = NNS_G2dGetNewOamManagerInstanceAsFastTransferMode( &mOamManager, 
                                                                0, 
                                                                OBJMANAGER_NUM_OAM, 
                                                                NNS_G2D_OAMTYPE_SUB );

    // Load resources

    //----------------------------------------------
    // load cell data
    mpCellFile = detail::LoadNCER( &mpCellBank, NTMV_M2D_OBJROOTDIR RESOURCE_SUB_FILE_TITLE".NCER"/* , mpAllocator */);
    NNS_NULL_ASSERT(mpCellFile);

    //--------------------------------------------
    // load animation data
    mpAnimFile = detail::LoadNANR( &mpAnimBank, NTMV_M2D_OBJROOTDIR RESOURCE_SUB_FILE_TITLE".NANR"/* , mpAllocator */);
    NNS_NULL_ASSERT(mpAnimFile);

    //----------------------------------------------
    // load character data for 2D
    {
        NNSG2dCharacterData* pCharData;

        void *const pBuf = detail::LoadNCGR( &pCharData, NTMV_M2D_OBJROOTDIR RESOURCE_SUB_FILE_TITLE".NCGR"/* , mpAllocator */);
        NNS_NULL_ASSERT(pBuf);

        // Loading For 2D Graphics Engine
        NNSG2dImageProxy             imageProxy;
        // Sub
        NNS_G2dInitImageProxy( &imageProxy );
        NNS_G2dLoadImage1DMapping( pCharData, 0x0, NNS_G2D_VRAM_TYPE_2DSUB, &imageProxy );

        // NNS_FndFreeToAllocator(mpAllocator, pBuf);
    }
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::Finalize

  Description:  Performs shutdown.

  Arguments:    objectConstructor:  A reference to an ObjectConstructor object.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void
ObjManager::Finalize(const ObjectConstructor& objectConstructor)
{
    NNSFndAllocator *const pAllocator = objectConstructor.GetAllocator();

    if (mpAnimFile)
    {
        // NNS_FndFreeToAllocator(pAllocator, mpAnimFile);
    }

    if (mpCellFile)
    {
        // NNS_FndFreeToAllocator(pAllocator, mpCellFile);
    }
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::DisposeCellAnimAll()

  Description:  Deletes all managed cell animations.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void            ObjManager::DisposeCellAnimAll()
{
    mNumCellAnimationUsed = 0;
}
    
/*---------------------------------------------------------------------------*
  Name:         ObjManager::Dispose()

  Description:  Deletes internal resources.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void            ObjManager::Dispose()
{
    DisposeCellAnimAll();
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::CreateCellAnim()

  Description:  Specify the animation sequence to playback and generate the cell animation. 
                

  Arguments:    sequenceID       Animation sequence number
                

  Returns:      Cell animation handle.
 *---------------------------------------------------------------------------*/
ObjManager::CellHandle      
ObjManager::CreateCellAnim(u16 sequenceID)
{
    CellHandle     handle = ObjManager::InvalidCellHandle;

    if( mNumCellAnimationUsed + 1 < OBJMANAGER_MAX_CELLANIM_NUM )
    {

        NNSG2dCellAnimation*              pCellAnim = &mCellAnimationSet[mNumCellAnimationUsed];
        const NNSG2dAnimSequenceData*    pSequence = NNS_G2dGetAnimSequenceByIdx( mpAnimBank, sequenceID );
        
        if( pSequence != NULL )
        {
            NNS_G2dInitCellAnimation( pCellAnim, pSequence, mpCellBank );
            
            
            handle = mNumCellAnimationUsed;
            mNumCellAnimationUsed++;
        }
    }

    return handle;
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::GetCellAnimationByHandle_()

  Description:  Gets a cell animation from a cell animation handle.

  Arguments:    handle          Cell handle
                

  Returns:      Cell animation instance.
 *---------------------------------------------------------------------------*/
NNSG2dCellAnimation* 
ObjManager::GetCellAnimationByHandle_( CellHandle handle )
{
    if( handle < mNumCellAnimationUsed )
    {
        return &mCellAnimationSet[handle];
    }else{
        return NULL;
    }
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::SetCellAnimSequenceToCell()

  Description:  Sets the animation sequence to play for a cell animation.

  Arguments:    handle          Cell handle
                sequenceID       Animation sequence number

  Returns:      Whether processing succeeded or failed.
 *---------------------------------------------------------------------------*/
bool            
ObjManager::SetCellAnimSequenceToCell( CellHandle handle, u16 sequenceID )
{
    NNSG2dCellAnimation*              pCellAnim = GetCellAnimationByHandle_( handle );
    const NNSG2dAnimSequenceData*    pSequence = NNS_G2dGetAnimSequenceByIdx( mpAnimBank, sequenceID );

    if( pCellAnim != NULL && pSequence != NULL )
    {
        NNS_G2dSetCellAnimationSequence( pCellAnim, pSequence );
        return true;
    }

    return false;
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::TickCellAnim()

  Description:  Advances the cell animation playback state.

  Arguments:    handle          Cell handle
                step            Steps to advance.

  Returns:      Whether processing succeeded or failed.
 *---------------------------------------------------------------------------*/
bool            
ObjManager::TickCellAnim( CellHandle handle, fx32 step )
{
    NNSG2dCellAnimation*    pCellAnim = GetCellAnimationByHandle_( handle );
    if( pCellAnim != NULL )
    {
        NNS_G2dTickCellAnimation( pCellAnim, step );    
        return true;
    }

    return false;
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::RestartCellAnim()

  Description:  Restarts cell animation playback.

  Arguments:    handle          Cell handle

  Returns:      Whether processing succeeded or failed.
 *---------------------------------------------------------------------------*/
bool            
ObjManager::RestartCellAnim( CellHandle handle )
{
    NNSG2dCellAnimation*    pCellAnim = GetCellAnimationByHandle_( handle );
    if( pCellAnim != NULL )
    {
        NNS_G2dRestartCellAnimation( pCellAnim );    
        return true;
    }

    return false;
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::SetCellAnimFrame()

  Description:  Sets the playback animation frame for a cell animation.

  Arguments:    handle          Cell handle.
                index           Animation frame number.

  Returns:      Whether processing succeeded or failed.
 *---------------------------------------------------------------------------*/
bool
ObjManager::SetCellAnimFrame( CellHandle handle, u16 index )
{
    NNSG2dCellAnimation* pCellAnim = GetCellAnimationByHandle_( handle );
    if( pCellAnim != NULL )
    {
        NNS_G2dSetCellAnimationCurrentFrame(pCellAnim, index);
        return true;
    }
    return false;
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::DrawCellAnim

  Description:  Renders a cell at the specified position.
                
  Arguments:    handle          Cell handle
                x               x-coordinate
                y               y-coordinate

  Returns:      Whether rendering succeeded or failed.
 *---------------------------------------------------------------------------*/
bool            
ObjManager::DrawCellAnim( CellHandle handle, fx32 x, fx32 y )
{
    const NNSG2dCellAnimation*  pCellAnim = GetCellAnimationByHandle_( handle );
    if( pCellAnim != NULL )
    {    
        const NNSG2dCellData*   pCell = NNS_G2dGetCellAnimationCurrentCell( pCellAnim );
        if( pCell != NULL )
        {
            u16                 numOamDrawn = 0;
            NNSG2dFVec2        pos = { x, y };
            //
            // Writes an OBJ list equivalent to the cell to the temporary buffer.
            //
            numOamDrawn = NNS_G2dMakeCellToOams(
                                mTempOamBuffer,      // output Oam buffer
                                OBJMANAGER_NUM_OAM,   // output buffer length
                                pCell,                  // Cell to output
                                NULL,                   // affine conversion
                                &pos,                   // offset position
                                0,                      // Affine Index
                                FALSE );               // double affine?

            if( numOamDrawn < NNS_G2dGetOamManagerOamCapacity( &mOamManager ) )
            {
                // Registers OBJ list to OAM Manager
                (void)NNS_G2dEntryOamManagerOam( &mOamManager, mTempOamBuffer, numOamDrawn );
                return true;
            }
        }
    }

    return false;
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::TransferDataToHW()

  Description:  Transfers OAM manager content to the hardware and applies it to the actual display.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void 
ObjManager::TransferDataToHW()
{
    NNS_G2dApplyOamManagerToHW( &mOamManager );
    NNS_G2dResetOamManagerBuffer( &mOamManager );
}

/*---------------------------------------------------------------------------*
  Name:         ObjManager::GetOBJVRamMappingMode()

  Description:  Gets the VRAM mapping mode for OBJs.
                This is called from the part of the application that configures VRAM.

  Arguments:    None.

  Returns:      VRAM mapping mode for OBJ's.
 *---------------------------------------------------------------------------*/
GXOBJVRamModeChar ObjManager::GetOBJVRamMappingMode()
{
    // Return a value corresponding to the OBJ character resource format.
    // Currently, resources are statically kept in ROM, so this function returns 1D 64k consequent to those resources. 
    // 
    return GX_OBJVRAMMODE_CHAR_1D_64K;
}

}   // namespace m2d
}   // namespace ntmv
