//
// Asura_Wii_Main.cpp
//
// Created by KOR - 09 October 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// Asura headers
#include "Core/Asura_Core.h"
#include "Core/Asura_String.h"
#include "GUIMenu/Asura_GUIMenu_System.h"
#include "Debugging/Asura_Version.h"
#include "Rendering/Asura_Viewport.h"


// Wii
#include "Rendering/Asura_GX.h"
#include "Rendering/Asura_Wii_ROMFont.h"

#include <dolphin/base/PPCArch.h>
#include <revolution/sc.h>



// Project headers
#include "Main/Asura_Project_Main.h"

#define FPSCR_VOUZXE    (FPSCR_XE | FPSCR_ZE | FPSCR_UE | FPSCR_OE | FPSCR_VE)
#define FPSCR_VXALL     (FPSCR_VXCVI | FPSCR_VXSQRT | FPSCR_VXSOFT |    \
	FPSCR_VXVC | FPSCR_VXIMZ | FPSCR_VXZDZ |       \
	FPSCR_VXIDI | FPSCR_VXISI | FPSCR_VXSNAN)
// KOR - 17/09/2003 12:56:30 - Exception handler taken from Jack Mathews <jack@retrostudios.com>
// post "Re: NR Reader crashing" to gamecube.tools, 24 June 2003 23:01
namespace
{
	GXColor const skFatalBg = { 128, 0, 0, 0 };
	GXColor const skFatalFg = { 255, 255, 255, 0 };

	void MyErrorHandler(OSError error, OSContext* context, u32 dsisr, u32 dar)
	{
		//context->srr0 += 4;
		//return; 

		const u_int uBUFFER_SIZE = 2048;
		char szDebuggerBuf[uBUFFER_SIZE];

		//		if ( error == OS_ERROR_PROTECTION && ( ( dsisr & OS_PROTECT_ADDRERR_BIT ) || dar > 0x01800000 ) )
		//			return;

		//		OSProtectRange( OS_PROTECT_CHAN3, NULL, 0, OS_PROTECT_CONTROL_RDWR );

		u32  i;
		u32* p;

		OSReport("Unhandled exception %d\n", error );
		OSReport("------------------------- Context 0x%08x -------------------------\n", context);

		for ( i = 0; i < 16; i++ )
		{
			OSReport("r%-2d  = 0x%08x (%14d)  r%-2d  = 0x%08x (%14d)\n",
				i,      context->gpr[i],      context->gpr[i],
				i + 16, context->gpr[i + 16], context->gpr[i + 16]);
		}

		OSReport("LR   = 0x%08x                   CR   = 0x%08x\n", context->lr,   context->cr);
		OSReport("SRR0 = 0x%08x                   SRR1 = 0x%08x\n", context->srr0, context->srr1);
		OSReport("DSISR= 0x%08x                   DAR  = 0x%08x\n", dsisr, dar);

		int len = 0;

		// MGH 15/8/2007: Write out the exception number and build time and date.
		const char *szBuild = Asura_BuildTimeAndDate ? Asura_BuildTimeAndDate : "---";
		len += Asura_PrintfToBfr( szDebuggerBuf, "Exception %d   [%s]\n", uBUFFER_SIZE-len, true, error, szBuild);

		switch(error)
		{
		case OS_ERROR_DSI:			len += Asura_PrintfToBfr( szDebuggerBuf + len, "DSI (memory access) Error\n");		break;
		case OS_ERROR_ISI:			len += Asura_PrintfToBfr( szDebuggerBuf + len, "ISI (instruction access) Error\n");	break;
		case OS_ERROR_ALIGNMENT:	len += Asura_PrintfToBfr( szDebuggerBuf + len, "Alignment Error\n");	break;
		case OS_ERROR_PROTECTION:	len += Asura_PrintfToBfr( szDebuggerBuf + len, "Protection Error\n");	break;
		case OS_ERROR_FPE:
			{
				// Mask out FPSCR_*X bit of no interest
				u32 fpscr = context->fpscr;
				fpscr &= ((fpscr & FPSCR_VOUZXE) << (FPSCR_VE_BIT - FPSCR_VX_BIT)) | FPSCR_VXALL;

				if (fpscr & FPSCR_VX)
				{
					len += Asura_PrintfToBfr( szDebuggerBuf + len, "FPE: Invalid op: ");
					if (fpscr & FPSCR_VXSNAN)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "SNaN\n");
					if (fpscr & FPSCR_VXISI)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "Infinity - Infinity\n");
					if (fpscr & FPSCR_VXIDI)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "Infinity / Infinity\n");
					if (fpscr & FPSCR_VXZDZ)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "0 / 0\n");
					if (fpscr & FPSCR_VXIMZ)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "Infinity * 0\n");
					if (fpscr & FPSCR_VXVC)		len += Asura_PrintfToBfr( szDebuggerBuf + len, "Invalid compare\n");
					if (fpscr & FPSCR_VXSOFT)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "Software request\n");
					if (fpscr & FPSCR_VXSQRT)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "Invalid square root\n");
					if (fpscr & FPSCR_VXCVI)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "Invalid integer convert\n");
				}
				if (fpscr & FPSCR_OX)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "FPE: Overflow\n");
				if (fpscr & FPSCR_UX)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "FPE: Underflow\n");
				if (fpscr & FPSCR_ZX)	len += Asura_PrintfToBfr( szDebuggerBuf + len, "FPE: Zero division\n");
				if (fpscr & FPSCR_XX)	{ len += Asura_PrintfToBfr( szDebuggerBuf + len, "FPE: Inexact result\n"); }
			}
			break;
		}
		len += Asura_PrintfToBfr( szDebuggerBuf + len, "IP: 0x%8.8x  Mem: 0x%8.8x", uBUFFER_SIZE-len, true, (unsigned int)context->srr0, (unsigned int)dar);

		if ( error == OS_ERROR_PROTECTION )
		{
			len += Asura_PrintfToBfr( szDebuggerBuf + len, " - %d", uBUFFER_SIZE-len, true, (int)dsisr );
		}

		len += Asura_PrintfToBfr( szDebuggerBuf + len, "\n\n", uBUFFER_SIZE-len);

		// Dump stack crawl (at most 16 levels)
		//len += Asura_PrintfToBfr( szDebuggerBuf + len, "Address:      Back Chain    LR Save\n", uBUFFER_SIZE-len);
		len += Asura_PrintfToBfr( szDebuggerBuf + len, "Stack crawl:\n", uBUFFER_SIZE-len);
		OSReport("\nAddress:      Back Chain    LR Save\n");
		for ( i = 0, p = (u32*) context->gpr[1]; // get current sp
			p && (u32) p != 0xffffffff && i++ < 16;
			p = (u32*) *p )                     // get caller sp
		{
			if ( p > (u32 *)(0x80000400) && ( p < (u32 *) 0x81800000 ) )
			{
				OSReport("0x%08x:   0x%08x    0x%08x\n", p, p[0], p[1]);
				//len += Asura_PrintfToBfr( szDebuggerBuf + len, "0x%08x: 0x%08x 0x%08x\n", uBUFFER_SIZE-len, true, (unsigned int)p, (unsigned int)p[0], (unsigned int)p[1]);
				len += Asura_PrintfToBfr( szDebuggerBuf + len, "0x%08x ", uBUFFER_SIZE-len, true, (unsigned int)p[1]);
			}
			else
			{
				OSReport("0x%08x:   BAD PTR\n", p);
				//len += Asura_PrintfToBfr( szDebuggerBuf + len, "0x%08x: BAD PTR\n", uBUFFER_SIZE-len, true, (unsigned int)p );
				len += Asura_PrintfToBfr( szDebuggerBuf + len, "BAD PTR", uBUFFER_SIZE-len, true);
				break;
			}
			if(!(i % 3))
			{
				len += Asura_PrintfToBfr( szDebuggerBuf + len, "\n", uBUFFER_SIZE-len, true);
			}
		}

		OSReport("\nInstruction at 0x%x (read from SRR0) attempted to access invalid address 0x%x (read from DAR)\n", context->srr0, dar);

		//if ( !sNoOnscreenError )
		OSFatal( skFatalFg, skFatalBg, szDebuggerBuf );
	}
}
// for OS_ERROR_FPE. OR-ed FPSCR_*E bits
u32 __OSFpscrEnableBits = FPSCR_VE | FPSCR_OE | FPSCR_ZE; // don't care about underflow or inexact ops.

void rs_register_error_handlers()
{
	OSSetErrorHandler( OS_ERROR_DSI, (OSErrorHandler) MyErrorHandler );
	OSSetErrorHandler( OS_ERROR_ISI, (OSErrorHandler) MyErrorHandler );
	OSSetErrorHandler( OS_ERROR_ALIGNMENT, (OSErrorHandler) MyErrorHandler );
	OSSetErrorHandler( OS_ERROR_PROTECTION, (OSErrorHandler) MyErrorHandler );
	OSSetErrorHandler( OS_ERROR_FPE, (OSErrorHandler) MyErrorHandler );
	OSReport("Registered error handlers\n");
}

int main(void)
{
#ifndef ASURA_SUBMISSION_BUILD
	rs_register_error_handlers();
#endif
	OSReport("Build timestamp: %s\n", Asura_BuildTimeAndDate);
	Asura_Wii_ROMFont::Initialise();

#if ASURA_GX_PAL
	Asura_Viewport::Set(640, ((SCGetProgressiveMode() == SC_PROGRESSIVE_MODE_ON && VIGetDTVStatus()) || SCGetEuRgb60Mode() == SC_EURGB60_MODE_ON) ? 480 : 528);
#else
	Asura_Viewport::Set(640,480);
#endif

#ifdef ASURA_USE_GUI_MENU_SYSTEM
	if (SCGetAspectRatio() ==  SC_ASPECT_RATIO_16x9)
	{
		Asura_GUIMenu_System::SetVirtualResolution(852.0f, 480.0f);
	}
	else
	{
		Asura_GUIMenu_System::SetVirtualResolution(640.0f, 480.0f);
	}
#endif

	Asura_Core::EarlyInitialise();
	Asura_Project::EarlyInitialise();

	Asura_Core::LateInitialise();
	Asura_Project::LateInitialise();

	while (Asura_Project::MainLoop())
	{
		continue;
	}

	Asura_Project::Shutdown();
	Asura_Core::Shutdown();

	OSHalt("Finished\n");
	return 0;
}
