#include <windows.h>
#include <bsp_args.h>
#include <bsp.h>

#include "menu.h"
#include "loader.h"

extern PBOOT_CFG       g_pBootCfg;

#define FSR_PAGE_SIZE   (2048)
#define FSR_SPAREAREA_SIZE  (64)

#ifndef SDMMC_BOOT
extern FlashInfo g_FlashInfo;

extern DWORD _NUM_OF_BLOCKS;
extern DWORD _BYTES_PER_BLOCK;
extern WORD _SECTORS_PER_BLOCK;
extern WORD _BYTES_PER_SECTOR;
#endif

#ifndef SDMMC_BOOT
UINT32 SetBlockPage(void)
{
    CHAR szCount[16];
    USHORT cwNumChars = 0;
    USHORT InChar = 0;
    UINT32 block=0, page=0;
    UINT32 i;

    EdbgOutputDebugString("\r\nEnter Block # to read : ");

    while(!((InChar == 0x0d) || (InChar == 0x0a)))
    {
        InChar = OEMReadDebugByte();
        if (InChar != OEM_DEBUG_COM_ERROR && InChar != OEM_DEBUG_READ_NODATA)
        {
            // If it's a number or a period, add it to the string.
            //
            if ((InChar >= '0' && InChar <= '9'))
            {
                if (cwNumChars < 16)
                {
                    szCount[cwNumChars++] = (char)InChar;
                    OEMWriteDebugByte((BYTE)InChar);
                }
            }
            // If it's a backspace, back up.
            //
            else if (InChar == 8)
            {
                if (cwNumChars > 0)
                {
                    cwNumChars--;
                    OEMWriteDebugByte((BYTE)InChar);
                }
            }
        }
    }

    // If it's a carriage return with an empty string, don't change anything.
    //
    if (cwNumChars)
    {
        szCount[cwNumChars] = '\0';
        block = atoi(szCount);
    }

    EdbgOutputDebugString("\r\nEnter Page # to read : ");
    InChar = 0;
    for (i=0; i<16; i++) szCount[i] = '0';

    while(!((InChar == 0x0d) || (InChar == 0x0a)))
    {
        InChar = OEMReadDebugByte();
        if (InChar != OEM_DEBUG_COM_ERROR && InChar != OEM_DEBUG_READ_NODATA)
        {
            // If it's a number or a period, add it to the string.
            //
            if ((InChar >= '0' && InChar <= '9'))
            {
                if (cwNumChars < 16)
                {
                    szCount[cwNumChars++] = (char)InChar;
                    OEMWriteDebugByte((BYTE)InChar);
                }
            }
            // If it's a backspace, back up.
            //
            else if (InChar == 8)
            {
                if (cwNumChars > 0)
                {
                    cwNumChars--;
                    OEMWriteDebugByte((BYTE)InChar);
                }
            }
        }
    }

    // If it's a carriage return with an empty string, don't change anything.
    //
    if (cwNumChars)
    {
        szCount[cwNumChars] = '\0';
        page = atoi(szCount);
    }

    return ((block<<6)|page);
}

BOOL CheckInput(void)
{
	USHORT InChar[2]={0,0};

	while(!((InChar[1] == 0x0d) || (InChar[1] == 0x0a)))
	{
		InChar[1] = OEMReadDebugByte();
		
		if (InChar[1] != OEM_DEBUG_COM_ERROR && InChar[1] != OEM_DEBUG_READ_NODATA) 
		{
			if (InChar[1] == 8 || InChar[1] == 'Y' || InChar[1] == 'y' || InChar[1] == 'N' || InChar[1] == 'n') 
			{
				OEMWriteDebugByte((BYTE)InChar[1]);
				InChar[0] = InChar[1];
			}
		}
	}

	if (InChar[0]=='Y' || InChar[0]=='y')
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

static int MenuHandlerFormatFatUser(void *arg)
{
#if 0
    if (!PocketStoreIII_STL_Format(FSR_PARTID_FS0)) {
        OALMSG(TRUE, (TEXT(" PocketStore is not formatted.\r\n")));
    	return 0;
    }
    if (!PocketStoreIII_STL_Format(FSR_PARTID_FS1)) {
        OALMSG(TRUE, (TEXT(" PocketStore is not formatted.\r\n")));
        return 0;
    }
    OALMSG(TRUE, (TEXT(" Format complete.\r\n")));
#endif
	return 0;
}

static int MenuHandlerFormatLoader(void *arg)
{
#if 0
	EdbgOutputDebugString("\r\n\r\n");
	EdbgOutputDebugString("\r ### Are you sure to erase the bootloader? (Y or N) : ");

	if (CheckInput()==TRUE)
	{
		EdbgOutputDebugString("\r\n ### Format start...\r\n");
		EraseBMLPartition(FSR_PARTID_NBLS);
		EraseBMLPartition(FSR_PARTID_EBOOT);
		EraseBMLPartition(FSR_PARTID_IPL);
		return 0;
	}
#endif
	EdbgOutputDebugString("\r\n ### Format canceled...\r\n");
	return 0;
}

static int MenuHandlerLoadEbootCFG(void *arg)
{
    if ( !TOC_Read() ) {
        OALMSG(OAL_WARN, (TEXT("TOC_Read Failed!\r\n")));
    }
    OALMSG(TRUE, (TEXT("...TOC_Read complete\r\n")));
	return 0;
}

static int MenuHandlerStoreEbootCFG(void *arg)
{
    if ( !TOC_Write() ) {
        OALMSG(OAL_WARN, (TEXT("TOC_Write Failed!\r\n")));
    }
    OALMSG(TRUE, (TEXT("...TOC_Write complete\r\n")));
	return 0;
}

static int MenuHandlerReadPage(void *arg)
{
#if 0
	UINT32 rslt;
	UINT8  pMBuf[FSR_PAGE_SIZE], pSBuf[FSR_SPAREAREA_SIZE];
	UINT32 i;

	rslt = SetBlockPage();

	RETAILMSG(1, (TEXT("\r\n\r\n start page : %#x"), rslt));


    FSR_OND_Read(0, rslt>>6, rslt&0x3f, pMBuf, (FSRSpareBuf *)pSBuf, 0);  // for 1 page

	RETAILMSG(1, (TEXT("\r\n =============================================================")));
	for (i=0;i<FSR_PAGE_SIZE;i++)
	{
		if ((i%16)==0)
			RETAILMSG(1, (TEXT("\r\n 0x%04x_%04x |"), (i + 0x800*rslt)>>16, (i + 0x800*rslt)&0xffff));
		
		RETAILMSG(1, (TEXT(" %02x"), pMBuf[i]));

		if ((i%512)==511)
			RETAILMSG(1, (TEXT("\r\n -------------------------------------------------------------")));
	}
	for (i=0;i<FSR_SPAREAREA_SIZE;i++)
	{
		if ((i%16)==0)
			RETAILMSG(1, (TEXT("\r\n 0x0000_0%03x |"), i));
		RETAILMSG(1, (TEXT(" %02x"), pSBuf[i]));
	}
	
	RETAILMSG(1, (TEXT("\r\n =============================================================\r\n\r\n")));
#endif
	return 0;
}

static int MenuHandlerLowlevelFormat(void *arg)
{
	int i;
	
	EdbgOutputDebugString("\r\n\r\n");
	EdbgOutputDebugString("\r ### Are you sure to format all volumes? (Y or N) : ");


	if (CheckInput()==TRUE)
	{
		// Reserve boot blocks
		for(i = 0;i < IMAGE_START_BLOCK;i++)
		{
			if(!(FMD_GetBlockStatus(i) & BLOCK_STATUS_BAD))
			{		
				FMD_SetBlockStatus(i, BLOCK_STATUS_RESERVED);
			}
		}

		if(BP_LowLevelFormat(0, g_FlashInfo.dwNumBlocks, FORMAT_SKIP_RESERVED | FORMAT_SKIP_BLOCK_CHECK) != TRUE)
		{
	    	OALMSG(OAL_ERROR, (TEXT("ERROR: Low-level boot media format failed.\r\n")));
	
	        while(1);
		}

		RETAILMSG(1, (TEXT("Low level format was done successfully.\r\n")));
	}
	
	
#if 0
	if (CheckInput()==TRUE)
	{
		EdbgOutputDebugString("\r\n ### Format start...\r\n");
		FlashRePartition(TRUE, FSR_BML_INIT_FORMAT);
		EraseBMLPartition(FSR_PARTID_OS);
        if (!PocketStoreIII_STL_Format(FSR_PARTID_OS))
            OALMSG(TRUE, (TEXT(" FSR_PARTID_OS is not formatted.\r\n")));
        else
            OALMSG(TRUE, (TEXT(" FSR_PARTID_OS Format complete.\r\n")));
        
		EraseBMLPartition(FSR_PARTID_FS0);
        if (!PocketStoreIII_STL_Format(FSR_PARTID_FS0))
            OALMSG(TRUE, (TEXT(" FSR_PARTID_FS0 is not formatted.\r\n")));
        else
            OALMSG(TRUE, (TEXT(" FSR_PARTID_FS0 Format complete.\r\n")));
		return 0;
	}
	EdbgOutputDebugString("\r\n ### Format canceled...\r\n");
#endif
	
	return 0;	
}


static int MenuHandlerVerify(void *arg)
{
	// Verify all of blocks to find a bad block. If a bad block is found, mark it.
	{
		DWORD dwBlock;
		DWORD dwSector;
	
		BYTE bufWriteData[NAND_SECTOR_SIZE];
		BYTE bufReadData[NAND_SECTOR_SIZE];
		SectorInfo siWrite;
		SectorInfo siRead;

		DWORD i;
		
		BOOL bBadBlock = FALSE;
		   
		for(i = 0;i < (NAND_SECTOR_SIZE / 8);i++)
		{
			*(DWORD *)&(bufWriteData[i * 4]) = 0x12345678;
			*(DWORD *)&(bufWriteData[(i * 4) + 4]) = 0x9ABCDEF0;
		}				
	
		memset(&siWrite, 0, sizeof(SectorInfo));
		siWrite.bOEMReserved = 0xFF & (BYTE)(~(OEM_BLOCK_RESERVED | OEM_BLOCK_READONLY));
		siWrite.bBadBlock	 = 0x5A;
		siWrite.dwReserved1  = 0x12345678;
		siWrite.wReserved2	 = 0xDEF0;

	
		// Verifies all blocks and marks all of bad blocks.
		for(dwBlock = 0;dwBlock < _NUM_OF_BLOCKS;dwBlock++)
		{
			//RETAILMSG(1, (TEXT("Verifying block #0x%X\r\n"), dwBlock));
			RETAILMSG(1, (TEXT(".")));
		
			if(FMD_EraseBlock(dwBlock) != TRUE)
			{
				RETAILMSG(TRUE, (TEXT("\r\nBad block #%d\r\n"), dwBlock));
				FMD_SetBlockStatus(dwBlock, BLOCK_STATUS_BAD);
			}
			else
			{
				bBadBlock = FALSE;
							
				for(dwSector = 0;dwSector > _SECTORS_PER_BLOCK;dwSector++)
				{
					FMD_WriteSector(dwSector, bufWriteData, &siWrite, 1);
		
					memset(bufReadData, 0, sizeof(bufReadData));
					memset(&siRead, 0, sizeof(SectorInfo)); 		
					FMD_ReadSector(dwSector, bufReadData, &siRead, 1);
				
					if((memcmp(bufWriteData, bufReadData, sizeof(bufWriteData)) != 0) ||
						memcmp(&siWrite, &siRead, sizeof(SectorInfo)) != 0)
					{
						RETAILMSG(TRUE, (TEXT("\r\nBad block #%d\r\n"), dwBlock));
						FMD_SetBlockStatus(dwBlock, BLOCK_STATUS_BAD);
						bBadBlock = TRUE;
						break;
					}
				}
			
				if(bBadBlock == FALSE)
				{
					// Erase the normal block
					if(FMD_EraseBlock(dwBlock) != TRUE)
					{
						RETAILMSG(TRUE, (TEXT("\r\nBad block #%d\r\n"), dwBlock));
						FMD_SetBlockStatus(dwBlock, BLOCK_STATUS_BAD);
					}
				}
			}
		}
	
		RETAILMSG(1, (TEXT("\r\nVerification completed.\r\n")));
	}

	return TRUE;
}

static int  OndEraseAll(void *arg)
{
    UINT32 nNumOfBlks = 2048;
    UINT32 i;

    for(i=0; i< g_FlashInfo.dwNumBlocks;i++)
    {
        if(FMD_EraseBlock(i) == FALSE)
        {
        	OALMSG(1, (TEXT("\r\nFailed to erase block #%d\r\n"), i));
        }
        //OALMSG(TRUE, (TEXT("Erase # %d block ...\r\n"), i));
    }
    OALMSG(TRUE, (TEXT("Erase complete...\r\n")));

    return TRUE;
}
#endif

#ifndef SDMMC_BOOT
MENU_ITEM m_menuOneNANDProc[] =
{
//	{ '0', "Format FAT Partition : User Partition", NULL, MenuHandlerFormatFatUser, NULL, NULL, NULL, (void *) 0},
//	{ '1', "Format Bootloader", NULL, MenuHandlerFormatLoader, NULL, NULL, NULL, (void *) 0},
	{ '2', "Load EbootCFG From NAND", NULL, MenuHandlerLoadEbootCFG, NULL, NULL, NULL, (void *) 0},
	{ '3', "Store EbootCFG To NAND", NULL, MenuHandlerStoreEbootCFG, NULL, NULL, NULL, (void *) 0},
//	{ '4', "Dump Page", NULL, MenuHandlerReadPage, NULL, NULL, NULL, (void *) 0},

	{ '5', "Low level format all volumes", NULL, MenuHandlerLowlevelFormat, NULL, NULL, NULL, (void *) 0},
	{ '6', "Verify NAND flash memory", NULL, MenuHandlerVerify, NULL, NULL, NULL, (void *) 0},
	{ '7', "Erase all blocks", NULL, OndEraseAll, NULL, NULL, NULL, (void *) 0},

	{ '\0', "", NULL, NULL, NULL, NULL, NULL, NULL}
};
#endif	
