/*---------------------------------------------------------------------------*
  Project:  NitroSDK - CARD - demos - card-1
  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.1  2004/09/09 12:50:30  yosizaki
  (none)

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


#include <nitro.h>
#include <nitro/card.h>
#include <nitro/fs.h>


/* Routine for screen drawing */
#include "draw.h"


/* Variables for the CARD library ******************************************************/

/*
 * ID for locking CARD backup.
 * For exclusive use of CARD hardware resources with FS library, etc.
 * Used with CARD_LockBackup() / CARD_UnlockBackup().
 */
static u16	card_lock_id;

/*
 * The result value when there is a CARD access function error.
 * The return value of CARD_GetResultCode() gets changed by CARD_UnlockBackup(), etc., so
 * be careful when access processes and error handling are spaced far apart.
 */
static CARDResult	last_result = CARD_RESULT_SUCCESS;

/*
 * Flag indicating whether write-test has been executed.
 */
static BOOL	is_test_run;


/* Sample demo functions **********************************************************/

/* Get press-down timing key input */
static u32	GetPadDown(void);

/* Initialize OS and other basic application components */
static void	InitApp(void);

/* Select Device screen */
static void	SelectDevice(void);

/* Write-test screen */
static void	TestWriteAndVerify(void);

/* Screen that shows test results */
static void	ShowResult(void);


/******************************************************************************/

/* Get press-down timing key input */
static u32	GetPadDown(void)
{
	static u32	btn = 0;
	static u32	btn_bak = 0;

	btn_bak = btn;
	btn = PAD_Read();
	return	(u32)(~btn_bak & btn);
}

/* V-blank callback */
static void	VBlankIntr(void)
{
	OS_SetIrqCheckFlag(OS_IE_V_BLANK);
}

/* Initialize OS and other basic application components */
static void	InitApp(void)
{
	void	*tmp;
	OSHeapHandle	hh;

	/* Initialize OS */
	OS_InitPrintServer();
	OS_Init();
	OS_InitThread();
	OS_InitTick();
	/* Initialize arena */
	tmp = OS_InitAlloc(OS_ARENA_MAIN,
		OS_GetMainArenaLo(), OS_GetMainArenaHi(), 1);
	OS_SetArenaLo(OS_ARENA_MAIN, tmp);
	hh = OS_CreateHeap(OS_ARENA_MAIN, 
		OS_GetMainArenaLo(), OS_GetMainArenaHi());
	if(hh < 0)
		OS_Panic("ARM9: Fail to create heap...\n");
	hh = OS_SetCurrentHeap(OS_ARENA_MAIN, hh);
	/* Enable interrupts  */
	OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
    (void)OS_EnableIrqMask(OS_IE_V_BLANK);
    (void)OS_EnableIrq();
}

/* The Select Device screen */
static void	SelectDevice(void)
{
	static const CARDBackupType	types[] = {
		CARD_BACKUP_TYPE_EEPROM_4KBITS,
		CARD_BACKUP_TYPE_EEPROM_64KBITS,
		CARD_BACKUP_TYPE_FLASH_2MBITS,
	} ;

	int	cur = 0;

	BOOL	end = FALSE;

	while(!end) {

		u32	btn_down = GetPadDown();

		DrawBegin();
		ClearFrame(RGB555_BLACK);

		/* Move cursor with UP/DOWN keys */
		if(btn_down & PAD_KEY_DOWN) {
			if(++cur > sizeof(types) / sizeof(*types) - 1)
				cur = 0;
		}
		if(btn_down & PAD_KEY_UP) {
			if(--cur < 0)
				cur = sizeof(types) / sizeof(*types) - 1;
		}

		/*
		 * Choose currently selected device with A Button.
		 *
		 * Be sure to set this correctly, because the library cannot determine
		 * whether the specified device is actually mounted.
		 */
		if(btn_down & PAD_BUTTON_A) {
			CARD_IdentifyBackup(types[cur]);
			end = TRUE;
		}

		/* Show character string */
		SetTextColor(RGB555_GREEN);
		DrawText(10, 40, "select device!");
		DrawText(20, 60, "EEPROM   4 kb");
		DrawText(20, 70, "EEPROM  64 kb");
		DrawText(20, 80, "FLASH    2 Mb");
		FillRect(10, 60, 10, 50, RGB555_BLACK);
		DrawText(10, 60 + 10 * cur, ">");

		DrawEnd();
		OS_PrintServer();
	}
}

/* The write-test screen */
static void	TestWriteAndVerify(void)
{
	const int	page_size = CARD_GetBackupPageSize();
	const int	total_size = CARD_GetBackupTotalSize();

	int	pos = 0;

	BOOL	end = FALSE;

	/* Display various kinds of information */
	DrawBegin();
	ClearFrame(RGB555_BLACK);
	SetTextColor(RGB555_WHITE);
	if(CARD_IsBackupEeprom()) {
		DrawText(10, 10, "EEPROM");
	}
	else if(CARD_IsBackupFlash()) {
		DrawText(10, 10, "FLASH");
	}
	DrawText(10, 20, "page:%d  total:%d", page_size, total_size);
	FillRect(10, 40, 256, 100, RGB555_BLACK);
	SetTextColor(RGB555_BLUE);
	MoveTo(  0 + 8, 35);
	LineTo(256 - 8, 35);
	LineTo(256 - 8, 182 - 8);
	LineTo(  0 + 8, 182 - 8);
	LineTo(  0 + 8, 35);
	DrawEnd();

	while(!end) {

		u32	btn_down = GetPadDown();

		DrawBegin();

		/* If not started yet, start using A Button */
		if(!is_test_run) {
			if(btn_down & PAD_BUTTON_A) {
				is_test_run = TRUE;
			}
			SetTextColor(RGB555_WHITE);
			DrawText(10, 40, "press A-BUTTON to test");
		}

		/* Stop the test using the B Button */
		if(btn_down & PAD_BUTTON_B) {
			end = TRUE;
		}

		/* Testing */
		if(is_test_run) {

			u8	tmp_buf[8];

			FillRect(10, 40, 256 - 10 * 2, 100, RGB555_BLACK);

			SetTextColor(RGB555_GREEN);
			DrawText(10, 40, "now testing...");
			DrawText(10, 50, "address:%d-%d", pos, pos + sizeof(tmp_buf));

			/*
			 * Lock CARD resources so backup can be accessed from here.
			 *
			 * This blocks card access from the FS and other modules
			 * until CARD_UnlockBackup() is called. But beware of
			 * process deadlock.
			 */
			CARD_LockBackup((u16)card_lock_id);
			{
				OSTick	tick;

				int	i;

				for( i = 0 ; i < sizeof(tmp_buf) ; ++i )
					tmp_buf[i] = (u8)(pos * 3 + i);

				tick = OS_GetTick();

				/* Assynchronous write + content verification check */
				if(CARD_IsBackupEeprom()) {
					CARD_WriteAndVerifyEepromAsync(
						pos, tmp_buf, sizeof(tmp_buf), NULL, (void*)0);
				}
				else if(CARD_IsBackupFlash()) {
					CARD_WriteAndVerifyFlashAsync(
						pos, tmp_buf, sizeof(tmp_buf), NULL, (void*)0);
				}

				/*
				 * Waits for process to end.
				 *
				 * Depending on the type of device and the specified size,
				 * the blocking can go on for a very long time. For this 
				 * reason,you should modify the wait procedure to match
				 * the framework of the user application.
				 * (For example, judge based on one CARD_TryWaitBackupAsync() in each frame)
				 */
				(void)CARD_WaitBackupAsync();
				last_result = CARD_GetResultCode();

				if(last_result != CARD_RESULT_SUCCESS) {
					/* If error, quit for now */
					end = TRUE;
				}
				else {
					/* If success, show the time */
					tick = OS_GetTick() - tick;
					DrawText(10, 60, "%6d[ms]", OS_TicksToMilliSeconds(tick));
					/* Move to the next test address */
					pos += sizeof(tmp_buf);
					if(pos + sizeof(tmp_buf) > total_size) {
						end = TRUE;
					}
				}
			}
			CARD_UnlockBackup(card_lock_id);

		}

		DrawEnd();
		OS_PrintServer();
	}
}

/* The screen that shows the test retults */
static void	ShowResult(void)
{
	static const char* const	err_string[] = {
		"success",
		"failure",
		"invalid param",
		"unsupported",
		"timeout",
		"error",
	} ;

	BOOL	end = FALSE;

    while(!end) {

		u32	btn_down = GetPadDown();

		DrawBegin();

		/* Return on press of A / B / START */
		if(btn_down & (PAD_BUTTON_A | PAD_BUTTON_B | PAD_BUTTON_START)) {
			end = TRUE;
		}

		if(last_result == CARD_RESULT_SUCCESS) {
			/* Success display */
			SetTextColor(RGB555_WHITE);
			DrawText(10, 70, "done! (success)");
		}
		else {
			/* Error display */
			SetTextColor(RGB555_RED);
			DrawText(10, 70, "error!");
			DrawText(10, 80, "result:\"%s\"", err_string[last_result]);
		}

		DrawEnd();
		OS_PrintServer();
	}
}

/* Main entry point */
void	NitroMain(void)
{

	{	/* Initialize the sample application */
		InitApp();
		InitDraw();
		SetClearColor(RGB555_CLEAR);
	}

	{	/* Initialize the CARD library */
		s32	ret;

		CARD_Init();
		ret = OS_GetLockID();
		if(ret == OS_LOCK_ID_ERROR) {
			OS_Panic("demo fatal error! OS_GetLockID() failed");
		}
		card_lock_id = (u16)ret;
	}

	/* Screen transitions */
	for( ; ; )
	{
		/* Select Device screen */
		SelectDevice();
		/* Start test */
		is_test_run = FALSE;
		TestWriteAndVerify();
		/* Show results */
		if(is_test_run)
			ShowResult();
	}

}

