/*---------------------------------------------------------------------------*
  Project:  NitroSDK - GX - demos - UnitTours/2D_Oam_Bmp1D
  File:     main.c

  Copyright 2003,2004 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.4  2004/05/27 09:23:04  takano_makoto
  added Autotest code.

  Revision 1.3  2004/04/07 01:23:27  yada
  fixed header comment

  Revision 1.2  2004/04/06 12:48:06  yada
  fixed header comment

  Revision 1.1  2004/03/01 09:01:11  takano_makoto
  Initial check-in.

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

//---------------------------------------------------------------------------
// A sample that use OBJ in 1D mapped bitmap mode.
//
// This sample displays a object on the display in 1D bitmap format.
//
// USAGE:
//   UP, DOWN, LEFT, RIGHT      : Slide the OBJ
//   A, B                       : Scaling the OBJ
//   L, R                       : Rotate the OBJ
//   START                      : Initialize status of the OBJ
//
// HOWTO:
// 1. Set VRAM size and mapping mode of OBJ by GX_SetOBJVRamModeBmp()
// 2. Transfer the bitmap data by GX_LoadOBJ().
// 3. Set GX_OAM_MODE_BITMAPOBJ to OAM attribute to use the bitmap OBJ.
//---------------------------------------------------------------------------


#include <nitro.h>
#include "DEMO.h"
#include "data.h"
//---------------------------------------------------------------------------
//  Overview:
//        OAM buffer region
//---------------------------------------------------------------------------

static GXOamAttr   s_OAMBuffer[128];

//---------------------------------------------------------------------------
//  Overview:
//        V-Blank interrupt process
//  Description:
//        Sets up a flag that confirms that a V-Blank interrupt has been performed.
//
//        Initialize common (DEMOInitCommon()) performs the following:
//        * Select IRQ interrupt (OS_SetIrqMask()).
//        * Register this function that performs IRQ interrupt (OS_SetIRQFunction()).
//        * Enable IRQ interrupt (OS_EnableIrq()).
//        This function is called during V-Blank.
//---------------------------------------------------------------------------
void VBlankIntr(void)
{
    OS_SetIrqCheckFlag(OS_IE_V_BLANK);    // Set a flag to confirm V-Blank interrupt
}

//---------------------------------------------------------------------------
//  Overview:
//        Display a 1D mapping bitmap OBJ
//  Description:
//        Affine converts (rotate, enlarge, reduce) an OBJ that has 1D mapping bitmap data, and displays it.
//  Operation:
//        UP,DOWN    : Manipulate display position
//        BUTTON_A,B : Enlarge, reduce
//        BUTTON_L,R : Rotate
//        START      : Initialize settings
//---------------------------------------------------------------------------
void NitroMain()
{
    int  base_pos_x = 0, base_pos_y = 0;
    int  base_pat = 0;
    fx32 scale = FX32_ONE;
    u16  rotate = 0;

    //---------------------
    // Initialize
    //---------------------
    DEMOInitCommon();
    DEMOInitVRAM();

    /* OAM setup */
    GX_SetVisiblePlane(GX_PLANEMASK_OBJ);       // Set OBJ to visible
    GX_SetBankForOBJ(GX_VRAM_OBJ_128_A);        // Allocate VRAM-A to OBJ

    GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS,    // 2D/3D mode
                       GX_BGMODE_0,             // Set BGMODE to 0
                       GX_BG0_AS_2D);           // Use o BG 0 as 2D
                      // Set OBJ-VRAM capacity for bitmap OBJ, and set mapping mode.
    GX_SetOBJVRamModeBmp(GX_OBJVRAMMODE_BMP_1D_128K);   // 1D mapping

    /* Load character data */
    GX_LoadOBJ((const void *)IMAGE_DATA, 0, IMAGE_DATA_SIZE);

    DEMOStartDisplay();

    //---------------------
    //  Main loop
    //---------------------
    while (1) {
        int pos_x = base_pos_x;
        int pos_y = base_pos_y;

        /* Pad information acquisition and manipulation settings */
        DEMOReadKey();
        #ifdef SDK_AUTOTEST    // code for auto test
        {
            const EXTKeys keys[8] = {{PAD_BUTTON_A,10}, {PAD_BUTTON_L,10}, {PAD_KEY_DOWN | PAD_KEY_RIGHT, 20}, {0, 0}};
            EXT_AutoKeys( keys, &gKeyWork.press, &gKeyWork.trigger);
        }
        #endif

        if (DEMO_IS_PRESS(PAD_KEY_UP)) {
            if (--base_pos_y < 0)       base_pos_y = 255;
            OS_Printf("pos=%d:%d rotate=%d scale=%d\n",
                      base_pos_x, base_pos_y, rotate, scale);
        } else if (DEMO_IS_PRESS(PAD_KEY_DOWN)) {
            if (++base_pos_y > 255)     base_pos_y = 0;
            OS_Printf("pos=%d:%d rotate=%d scale=%d\n",
                      base_pos_x, base_pos_y, rotate, scale);
        }
        if (DEMO_IS_PRESS(PAD_KEY_RIGHT)) {
            if (++base_pos_x > 511)     base_pos_x = 0;
            OS_Printf("pos=%d:%d rotate=%d scale=%d\n",
                      base_pos_x, base_pos_y, rotate, scale);
        } else if (DEMO_IS_PRESS(PAD_KEY_LEFT)) {
            if (--base_pos_x < 0)       base_pos_x = 511;
            OS_Printf("pos=%d:%d rotate=%d scale=%d\n",
                      base_pos_x, base_pos_y, rotate, scale);
        }
        if (DEMO_IS_PRESS(PAD_BUTTON_L)) {
            rotate -= 512;
            OS_Printf("pos=%d:%d rotate=%d scale=%d\n",
                      base_pos_x, base_pos_y, rotate, scale);
        } else if (DEMO_IS_PRESS(PAD_BUTTON_R)) {
            rotate += 512;
            OS_Printf("pos=%d:%d rotate=%d scale=%d\n",
                      base_pos_x, base_pos_y, rotate, scale);
        }
        if (DEMO_IS_PRESS(PAD_BUTTON_A)) {
            scale += 128;
            OS_Printf("pos=%d:%d rotate=%d scale=%d\n",
                      base_pos_x, base_pos_y, rotate, scale);
        } else if (DEMO_IS_PRESS(PAD_BUTTON_B)) {
            scale -= 128;
            if (scale < 0)      scale = 0;
            OS_Printf("pos=%d:%d rotate=%d scale=%d\n",
                      base_pos_x, base_pos_y, rotate, scale);
        }

        if (DEMO_IS_TRIG(PAD_BUTTON_START)) {
            base_pos_x = 0;
            base_pos_y = 0;
            rotate = 0;
            scale = FX32_ONE;
            OS_Printf("pos=%d:%d rotate=%d scale=%d\n",
                      base_pos_x, base_pos_y, rotate, scale);
        }

        /* Generate and set affine conversion matrix */
        {
            MtxFx22     mtx;
            fx16        sinVal = FX_SinIdx(rotate);
            fx16        cosVal = FX_CosIdx(rotate);
            fx32        rScale = FX_Inv(scale);
            GXOamAffine *oamBuffp = (GXOamAffine *)&s_OAMBuffer[0];

            mtx._00 =  (fx32)((cosVal * rScale) >> FX32_SHIFT);
            mtx._01 =  (fx32)((sinVal * rScale) >> FX32_SHIFT);
            mtx._10 = -(fx32)((sinVal * rScale) >> FX32_SHIFT);
            mtx._11 =  (fx32)((cosVal * rScale) >> FX32_SHIFT);
                                        // Set OBJ affine conversion
            G2_SetOBJAffine(oamBuffp,      // OAM buffer pointer
                            &mtx);         // 2x2 matrix for affine conversion
        }

        /* OAM settings */
        G2_SetOBJAttr(&s_OAMBuffer[0],          // Specify OAM position
                      pos_x,                    // X position
                      pos_y,                    // Y position
                      0,                        // Priority level
                      GX_OAM_MODE_BITMAPOBJ,    // Bitmap mode
                      FALSE,                    // Disable mosaic
                      GX_OAM_EFFECT_AFFINE,     // Affine effect
                      GX_OAM_SHAPE_32x32,       // OBJ size
                      GX_OAM_COLOR_16,          // 16 color
                      0,                        // Character name
                      15,                       // Alpha value
                      0);

        #ifdef SDK_AUTOTEST // code for auto test
        GX_SetBankForLCDC( GX_VRAM_LCDC_C );
        EXT_TestSetVRAMForScreenShot( GX_VRAM_LCDC_C );
        EXT_TestScreenShot( 100, 0xC8135726 );
        EXT_TestTickCounter();
        #endif //SDK_AUTOTEST

        /* Flush cache to main memory */
        DC_FlushRange(s_OAMBuffer, sizeof(s_OAMBuffer));

        /* V-Blank wait */
        SVC_WaitVBlankIntr();

        /* Transfer to OAM*/
        GX_LoadOAM(s_OAMBuffer,             // Transfer OAM buffer to OAM
                   0,
                   sizeof(s_OAMBuffer));
        MI_DmaFill32(3,                     // Clear OAM buffer
                     s_OAMBuffer,
                     192,
                     sizeof(s_OAMBuffer));
    }
}
