/*---------------------------------------------------------------------------*
  Project:  NitroSDK - OS - demos - favorite-color
  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.2  2004/10/08 00:40:48  yosizaki
  fix V-BlankIntr during initialization.
  small fix around symbols' name, comments, and so on.

  Revision 1.1  2004/10/07 10:20:14  yosizaki
  (none)

  $NoKeywords: $
 *---------------------------------------------------------------------------*/
#include <nitro.h>
#include "DEMO.h"


/*****************************************************************************/
/* prototype */

/* Information for displaying the color name */
typedef	struct	ColorProp_tag
{
	int index;
	const char *name;
	GXRgb color;
	u8	padding[4 - sizeof(GXRgb)];
} ColorProp;


/*****************************************************************************/
/* macro */

#define	COLOR_PROP_DEFINE(name)       \
{                                     \
    OS_FAVORITE_COLOR_ ## name,       \
    "OS_FAVORITE_COLOR_" #name,       \
    OS_FAVORITE_COLOR_VALUE_ ## name, \
}


/*****************************************************************************/
/* const */

extern	const u32	d_CharData[8 * 256];
extern	const u32	d_PaletteData[8 * 16];

/* Each type of constant for displaying the screen */
enum {
	ICON_MAX_X = 4,
	ICON_MAX_Y = OS_FAVORITE_COLOR_MAX / 4,
	ICON_WX = 32, ICON_WY = 12,
	SPACE_X = 16, SPACE_Y = 16,
	BORDER = 2,
	MARGIN = 16,
	FRAME_OX = 24, FRAME_OY = 48,
	FRAME_WX =
		ICON_WX * ICON_MAX_X +
		SPACE_X * (ICON_MAX_X - 1) +
		MARGIN * 2,
	FRAME_WY =
		ICON_WY * ICON_MAX_Y +
		SPACE_Y * (ICON_MAX_Y - 1) +
		MARGIN * 2
} ;

/* Color table information */
static const	ColorProp	prop[OS_FAVORITE_COLOR_MAX] = {
	COLOR_PROP_DEFINE(GRAY),
	COLOR_PROP_DEFINE(BROWN),
	COLOR_PROP_DEFINE(RED),
	COLOR_PROP_DEFINE(PINK),
	COLOR_PROP_DEFINE(ORANGE),
	COLOR_PROP_DEFINE(YELLOW),
	COLOR_PROP_DEFINE(LIME_GREEN),
	COLOR_PROP_DEFINE(GREEN),
	COLOR_PROP_DEFINE(DARK_GREEN),
	COLOR_PROP_DEFINE(SEA_GREEN),
	COLOR_PROP_DEFINE(TURQUOISE),
	COLOR_PROP_DEFINE(BLUE),
	COLOR_PROP_DEFINE(DARK_BLUE),
	COLOR_PROP_DEFINE(PURPLE),
	COLOR_PROP_DEFINE(VIOLET),
	COLOR_PROP_DEFINE(MAGENTA),
} ;


/*****************************************************************************/
/* variable */

static u16	gScreen[ 32 * 32 ];     /* Virtual screen */


/*****************************************************************************/
/* function */

/*---------------------------------------------------------------------------*
  Name:         ClearString

  Description:  Initializes the virtual screen for display.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void     ClearString( void )
{
    MI_CpuClearFast(gScreen, sizeof(gScreen));
}


/*---------------------------------------------------------------------------*
  Name:         PrintString

  Description:  Displays a character string of up to 32 characters on the virtual screen

  Arguments:    x       - The x coordinate that positions the start of the character string / 8 dot
                y       - The y coordinate that positions the start of the character string / 8 dot
                palette - The palette number that designates the character color
                text    - The displayed character string
                ...     - Subsequent variable arguments

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void     PrintString(int x, int y, int palette, const char *text, ...)
{
	va_list	vlist;
	static char	temp[64 + 2];
	u8	d;
	int	i;

	va_start(vlist , text);
	(void)OS_VSNPrintf(temp, sizeof(temp) - 1, text, vlist);
	va_end(vlist);
	*(u16*)&temp[32] = 0x0000;
	for( i = 0 ; (d = MI_ReadByte(temp + i)) != '\0' ; ++i )
	{
		if((d == '\r') || (d == '\n'))
			break;
		gScreen[((y * 32) + x + i) % (32 * 32)] =
			(u16)((palette << 12) | d);
	}
}


/* Draw a rectangle in the designated position */
static void FillRect(int x, int y, int wx, int wy, GXRgb col)
{
    G3_PolygonAttr(GX_LIGHTMASK_NONE,                      // no lights
                   GX_POLYGONMODE_MODULATE,                // modulation mode
                   GX_CULL_NONE,                           // cull none
                   0,                                      // polygon ID(0 - 63)
                   31,                                     // alpha(0 - 31)
                   0                                       // OR of GXPolygonAttrMisc's value
                   );

    G3_Begin(GX_BEGIN_QUADS);
    {
		fx16	ox = (fx16)(-FX16_ONE + (x * FX16_ONE * 2) / GX_LCD_SIZE_X);
		fx16	oy = (fx16)(+FX16_ONE - (y * FX16_ONE * 2) / GX_LCD_SIZE_Y);
		fx16	tx = (fx16)(ox + (wx * FX16_ONE * 2) / GX_LCD_SIZE_X);
		fx16	ty = (fx16)(oy - (wy * FX16_ONE * 2) / GX_LCD_SIZE_Y);

        G3_Color(col);
        G3_Vtx(ox, oy, 0);
        G3_Color(col);
        G3_Vtx(ox, ty, 0);
        G3_Color(col);
        G3_Vtx(tx, ty, 0);
        G3_Color(col);
        G3_Vtx(tx, oy, 0);
    }
    G3_End();
}


/* Main entry */
void NitroMain(void)
{
    //---------------------------------------------------------------------------
    // Initialization:
    // This sample set the basic 3D settings, DEMOInit() not called.
    //---------------------------------------------------------------------------
    DEMOInitCommon();

	(void)OS_DisableIrq();

    DEMOInitVRAM();
	DEMOInitDisplay3D();

    //---------------------------------------------------------------------------
    // 2D text settings
    //---------------------------------------------------------------------------
	GX_LoadBG1Char(d_CharData, 0 , sizeof(d_CharData));
	GX_LoadBGPltt(d_PaletteData, 0 , sizeof(d_PaletteData));

    //---------------------------------------------------------------------------
    // Start display
    //---------------------------------------------------------------------------
	(void)OS_EnableIrq();
    DEMOStartDisplay();


	{	/*
		 * Determine if the defined value and the displayed character string are really in proper correspondence
		 * (This process is used to confirm the validity of the SDK implementation)
		 */
		const GXRgb	*p_col = OS_GetFavoriteColorTable();
		int	i;
		for( i = 0 ; i < OS_FAVORITE_COLOR_MAX ; ++i ) {
			if(prop[i].index != i) {
				OS_TPanic("ColorProp[%d] definition is wrong!", i);
			}
			else if(prop[i].color != p_col[i]) {
				OS_TPanic("ColorProp[%d] definition or OS_GetFavoriteColorTable() is wrong!", i);
			}
			else if(prop[i].color != OS_GetFavoriteColor(i)) {
				OS_TPanic("OS_GetFavoriteColor(i) is wrong!", i);
			}
		}
	}

	{	/*
		 * Display each color value
		 * Move the selection with the directional keys and display the color definition name
		 */
		int	i;
		int	cur_x = 0, cur_y = 0;
		int	frame = 0;

		DEMOReadKey();
		for( ; ; ++frame )
		{
			//---------------------------------------------------------------------------
			// Reset the states of 3D graphics
			//---------------------------------------------------------------------------
			G3X_Reset();

			//---------------------------------------------------------------------------
			// In this sample, the camera matrix and the projection matrix are set as
			// an identity matrices for simplicity.
			//---------------------------------------------------------------------------
			G3_MtxMode(GX_MTXMODE_PROJECTION);
			G3_Identity();
			G3_MtxMode(GX_MTXMODE_POSITION_VECTOR);
			G3_Identity();

			ClearString();

			DEMOReadKey();

			/* Render the background */
			FillRect(
				FRAME_OX, FRAME_OY,
				FRAME_WX, FRAME_WY,
				GX_RGB(16, 16, 16));
			FillRect(
				FRAME_OX + BORDER, FRAME_OY + BORDER,
				FRAME_WX - BORDER * 2, FRAME_WY - BORDER * 2,
				GX_RGB(31, 31, 31));

			/* Move the cursor with the directional keys */
			if(DEMO_IS_TRIG(PAD_KEY_RIGHT)) {
				if(++cur_x >= ICON_MAX_X)
					cur_x -= ICON_MAX_X;
			}
			if(DEMO_IS_TRIG(PAD_KEY_LEFT)) {
				if(--cur_x < 0)
					cur_x += ICON_MAX_X;
			}
			if(DEMO_IS_TRIG(PAD_KEY_DOWN)) {
				if(++cur_y >= ICON_MAX_Y)
					cur_y -= ICON_MAX_Y;
			}
			if(DEMO_IS_TRIG(PAD_KEY_UP)) {
				if(--cur_y < 0)
					cur_y += ICON_MAX_Y;
			}

			{	/* Display the selected color and a summary */
				const int	index = cur_x + cur_y * ICON_MAX_X;

				PrintString(1, 1, 0xF, "index : %d", index);
				PrintString(1, 3, 0xF, "%s", prop[index].name);

				for( i = 0 ; i < OS_FAVORITE_COLOR_MAX ; ++i ) {
					const int	x = FRAME_OX + MARGIN + (i % ICON_MAX_X) * (ICON_WX + SPACE_X);
					const int	y = FRAME_OY + MARGIN + (i / ICON_MAX_X) * (ICON_WY + SPACE_Y);
					GXRgb	gnd;
					if(i == index) {
						const int	grad = ((frame & 32) == 0) ?
							(frame & 31) : 31 - (frame & 31);
						gnd = GX_RGB(grad, grad, grad);
					} else {
						gnd = GX_RGB(31, 31, 31);
					}
					FillRect(x - MARGIN / 2, y - MARGIN / 2,
						ICON_WX + MARGIN, ICON_WY + MARGIN, gnd);
					FillRect(x - MARGIN / 4, y - MARGIN / 4,
						ICON_WX + MARGIN / 2, ICON_WY + MARGIN / 2, GX_RGB(31, 31, 31));
					FillRect(x, y, ICON_WX, ICON_WY, prop[i].color);
				}
			}

			// swapping the polygon list RAM, the vertex RAM, etc.
			G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_W);

			SVC_WaitVBlankIntr();                 // Waiting the end of VBlank interrupt
		}
	}

}

//---------------------------------------------------------------------------
// VBlank interrupt function:
//
// Interrupt handlers are registered on the interrupt table by OS_SetIRQFunction.
// OS_EnableIrqMask selects IRQ interrupts to enable, and
// OS_EnableIrq enables IRQ interrupts.
// Notice that you have to call 'OS_SetIrqCheckFlag' to check a VBlank interrupt.
//---------------------------------------------------------------------------
void VBlankIntr(void)
{
	/* Apply the virtual screen in VRAM */
	DC_FlushRange(gScreen, sizeof(gScreen));
	DC_WaitWriteBufferEmpty();
	GX_LoadBG1Scr(gScreen, 0, sizeof(gScreen));

    OS_SetIrqCheckFlag( OS_IE_V_BLANK );                   // checking VBlank interrupt
}
