//
// Asura_Wii_StrapReminder.cpp
//
// Created by KOR - 18 December 2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "Rendering/Asura_Wii_StrapReminder.h"

// Asura Wii
#include "Core/Asura_PlatformBasics.h"
#include "FileAccess/Asura_PlatformFileAccessUtils.h"
#include "Input/Asura_Wii_Input.h"
#include "Rendering/Asura_GX.h"

// Lib
#include <string.h>
#include <revolution/gx.h>
#include <revolution/sc.h>
#include <revolution/tpl.h>

static OSThread g_xWiiStrapThread;
static const u_int uASURA_WII_STRAP_THREAD_STACK_SIZE = 64 * 1024;

bool Asura_Wii_StrapReminder::s_bInsideStrapReminder = false;
bool Asura_Wii_StrapReminder::s_bStrapReminderShown = false;
Asura_Wii_StrapReminder::StrapReminderMode Asura_Wii_StrapReminder::s_eWiiStrapState = Asura_Wii_StrapReminder::MODE_START;
char* Asura_Wii_StrapReminder::s_pcWiiStrapThreadStack = NULL;

void Asura_Wii_StrapReminder::Initialise()
{
	// Initial update
 	Update();
 	s_pcWiiStrapThreadStack = new char[uASURA_WII_STRAP_THREAD_STACK_SIZE];
 	OSCreateThread(&g_xWiiStrapThread, ThreadFunc, NULL, s_pcWiiStrapThreadStack + uASURA_WII_STRAP_THREAD_STACK_SIZE, uASURA_WII_STRAP_THREAD_STACK_SIZE, 15, 0);
 	OSResumeThread(&g_xWiiStrapThread);
}

void Asura_Wii_StrapReminder::Update()
{
	static GXTexObj s_xScreenATex;
	static TPLPalette *s_pTPLScreenA;
	static GXTexObj s_xScreenBTex;
	static TPLPalette *s_pTPLScreenB;
	static OSTick uStrapReminderStart;
	static struct {
		u_int uDuration;
		bool bSkippable;
		GXTexObj *pScreen;
		bool bLoadScreenB;
		bool bFadeIn;
		bool bFadeOut;
	} xModes[] =
	{
		// Mode Num, Duration, CanSkip, Texture
		{   0, false, &s_xScreenATex, false, false, false}, // MODE_START
		{ 500, false, &s_xScreenATex, false,  true, false}, // MODE_FADE_IN
		{   0, false, &s_xScreenATex, false, false, false}, // MODE_SCREENA_NOSKIP
		{   0, false, &s_xScreenATex,  true, false, false}, // MODE_SCREENA_LOADB
		{5000,  true, &s_xScreenATex, false, false, false}, // MODE_SCREENA_AFTERLOADB
		{1000,  true, &s_xScreenBTex, false, false, false}, // MODE_SCREENB_NOSKIP
		{4000,  true, &s_xScreenBTex, false, false, false}, // MODE_SCREENB_SKIP
		{5000,  true, &s_xScreenATex, false, false, false}, // MODE_SCREENA_REPEAT
		{5000,  true, &s_xScreenBTex, false, false, false}, // MODE_SCREENB_REPEAT

		{ 500, false,           NULL, false, false,  true}, // MODE_FADE_OUT
		{   0,  true,           NULL, false, false, false}, // MODE_QUIT
	};

	Asura_GX::CheckForRestart();

	SetInsideStrapReminder(true);
	Asura_PlatformFileAccessUtils::CheckDiscStatus();
	SetInsideStrapReminder(false);

	const u_int uTimeDiff = OSTicksToMilliseconds(OSDiffTick(OSGetTick(), uStrapReminderStart));
	const u_int uElapsed = Asura_Maths::Min(uTimeDiff, xModes[s_eWiiStrapState].uDuration);

	if (s_eWiiStrapState == MODE_START)
	{
		SetInsideStrapReminder(true);
		s_pTPLScreenA = LoadTPL(GetTextureFilename(false), s_xScreenATex);
		SetInsideStrapReminder(false);
		VISetBlack(false);

		if (!s_pTPLScreenA)
		{
			s_bStrapReminderShown = true;
			s_eWiiStrapState = MODE_QUIT;
			return;
		}
		uStrapReminderStart = OSGetTick();
	}
	else
	{
		if (xModes[s_eWiiStrapState].bLoadScreenB)
		{
			s_bInsideStrapReminder = true;
			s_pTPLScreenB = LoadTPL(GetTextureFilename(true), s_xScreenBTex);
			s_bInsideStrapReminder = false;
			if (!s_pTPLScreenB)
			{
				s_xScreenBTex = s_xScreenATex;
			}
		}

		Asura_GX::Set3DProjection(false, true);

		GXSetAlphaCompare( GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0 );
		GXSetZCompLoc( GX_TRUE );
		GXSetCullMode(GX_CULL_NONE);
		Asura_GX::SetZBufferMode(false, ASURA_ZBUFFERCMP_ALWAYS, false);
		GXSetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);

		GXClearVtxDesc( );
		GXSetVtxDesc( GX_VA_POS, GX_DIRECT );
		GXSetVtxDesc( GX_VA_TEX0, GX_DIRECT );

		// Colour channels - we are not using rasterised colour at all.
		GXSetNumChans    (0);

		// Texgens - unit, shift S, shift T.
		GXSetNumTexGens  (1);
		GXSetNumTevStages  (1);
		GXSetNumIndStages( 0 );

		GXSetTevDirect( GX_TEVSTAGE0);
		GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
		GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
		GXSetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL );
		GXSetTevColorIn( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST,  GX_CC_ZERO);
		GXSetTevColorOp( GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
		GXSetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K0);
		GXSetTevAlphaIn( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO);
		GXSetTevAlphaOp( GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ADDHALF, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
		GXSetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
			
		if (xModes[s_eWiiStrapState].pScreen)
		{
			GXLoadTexObj(xModes[s_eWiiStrapState].pScreen, GX_TEXMAP0);
		}

		GXColor xFade = {255,255,255,255};
		if (s_eWiiStrapState == MODE_FADE_IN)
		{
			xFade.r = xFade.g = xFade.b = (uElapsed * 255) / xModes[s_eWiiStrapState].uDuration;
		}
		else if (s_eWiiStrapState == MODE_FADE_OUT)
		{
			xFade.r = xFade.g = xFade.b = 255 - (uElapsed * 255) / xModes[s_eWiiStrapState].uDuration;
		}
		else
		{
			xFade.r = xFade.g = xFade.b = 255;
		}

		GXSetTevKColor(GX_KCOLOR0, xFade);
		Render();

	}
	if (xModes[s_eWiiStrapState].bSkippable)
	{
		Asura_Wii_Controller* pxWiiController = static_cast<Asura_Wii_Controller*>(Asura_Input::GetController(0));

		if (pxWiiController && pxWiiController->IsConnected() && (pxWiiController->GetKpadStatus()->hold & (~KPAD_BUTTON_HOME & 0xffff)))
		{
			uStrapReminderStart = OSGetTick();

			s_eWiiStrapState = MODE_FADE_OUT;
		}
	}

	if (uElapsed == xModes[s_eWiiStrapState].uDuration)
	{
		if (xModes[s_eWiiStrapState].uDuration)
		{
			uStrapReminderStart = OSGetTick();
		}

		s_eWiiStrapState = (StrapReminderMode)(s_eWiiStrapState + 1);
	}


	if (s_eWiiStrapState == MODE_QUIT && !s_bStrapReminderShown)
	{
		s_bStrapReminderShown = true;
		if (s_pTPLScreenA)
		{
			Asura_DeallocateAlign(s_pTPLScreenA);
		}
		if (s_pTPLScreenB)
		{
			Asura_DeallocateAlign(s_pTPLScreenB);
		}
	}
}

void Asura_Wii_StrapReminder::CheckForFinish()
{
	if (s_eWiiStrapState >= MODE_SCREENA_LOADB)
	{
		static bool bHaveJoined = false;
		if (!bHaveJoined)
		{
			OSJoinThread(&g_xWiiStrapThread, NULL);
			bHaveJoined = true;
			delete[] s_pcWiiStrapThreadStack;
			GXSetCurrentGXThread();
		}
		Update();
	}
}

void* Asura_Wii_StrapReminder::ThreadFunc(void*)
{
	while (s_eWiiStrapState < MODE_SCREENA_LOADB)
	{
		Asura_Wii_StrapReminder::Update();
	}
	return NULL;
}

const char* Asura_Wii_StrapReminder::GetTextureFilename(bool bScreenB)
{
	const Asura_LocalisedText_Language eLanguage = GetLanguage();

	static char szBuf[512];

	strcpy(szBuf, "StrapReminder/");
#ifdef ASURA_GX_PAL
	strcat(szBuf, "eu/");
#else
	strcat(szBuf, "us/");
#endif
	strcat(szBuf, bScreenB ? "strapB_" : "strapA_");
	strcat(szBuf, "640x480_");
	switch (eLanguage)
	{
		default:
		case ASURA_LT_L_ENGLISH:
		{
			strcat(szBuf, "English");
			break;
		}
		case ASURA_LT_L_FRENCH:
		{
			strcat(szBuf, "French");
			break;
		}
		case ASURA_LT_L_GERMAN:
		{
			strcat(szBuf, "German");
			break;
		}
		case ASURA_LT_L_ITALIAN:
		{
			strcat(szBuf, "Italian");
			break;
		}
		case ASURA_LT_L_DUTCH:
		{
			strcat(szBuf, "Dutch");
			break;
		}
		case ASURA_LT_L_SPANISH:
		{
#ifdef ASURA_GX_PAL
			strcat(szBuf,  "Spanish_EU");
#else
			strcat(szBuf,  "Spanish_US");
#endif
			break;
		}
	}
	strcat(szBuf,  ".tpl");

	return szBuf;
}

Asura_LocalisedText_Language Asura_Wii_StrapReminder::GetLanguage()
{
	Asura_LocalisedText_Language eRequestedLanguage;

	const u8 eSystemLanguage = SCGetLanguage();
	switch(eSystemLanguage)
	{
		case SC_LANG_FRENCH:
		{
			eRequestedLanguage = ASURA_LT_L_FRENCH;
			break;
		}
		case SC_LANG_ITALIAN:
		{
			eRequestedLanguage = ASURA_LT_L_ITALIAN;
			break;
		}
		case SC_LANG_GERMAN:
		{
			eRequestedLanguage = ASURA_LT_L_GERMAN;
			break;
		}
		case SC_LANG_SPANISH:
		{
			eRequestedLanguage = ASURA_LT_L_SPANISH;
			break;
		}
		case SC_LANG_JAPANESE:
		{
			eRequestedLanguage = ASURA_LT_L_JAPANESE;
			break;
		}
		case SC_LANG_DUTCH:
		{
			eRequestedLanguage = ASURA_LT_L_DUTCH;
			break;
		}
		case SC_LANG_ENGLISH:
		default:
		{
			eRequestedLanguage = ASURA_LT_L_ENGLISH;
			break;
		}
	}
	Asura_LocalisedText_Language eLanguageToUse = eRequestedLanguage;

	// Strap language is not restricted by languages on disc.
	Asura_LocalisedText_Language uStrapLanguage = eRequestedLanguage;
	// However it is restricted by region
#ifdef ASURA_GX_PAL
	switch(uStrapLanguage)
	{
		case ASURA_LT_L_ENGLISH:
		case ASURA_LT_L_FRENCH:
		case ASURA_LT_L_ITALIAN:
		case ASURA_LT_L_GERMAN:
		case ASURA_LT_L_SPANISH:
		case ASURA_LT_L_DUTCH:
		{
			// These languages are allowed
			break;
		}
		default:
		{
			// Everything else defaults to English.
			uStrapLanguage = ASURA_LT_L_ENGLISH;
			break;
		}
	}
#else
	switch(uStrapLanguage)
	{
		case ASURA_LT_L_ENGLISH:
		case ASURA_LT_L_FRENCH:
		case ASURA_LT_L_SPANISH:
		{
			// These languages are allowed
			break;
		}
		default:
		{
			// Everything else defaults to English.
			uStrapLanguage = ASURA_LT_L_ENGLISH;
			break;
		}
	}
#endif
	return uStrapLanguage;
}

void Asura_Wii_StrapReminder::Render()
{
	const bool bWideScreen = (SCGetAspectRatio() == SC_ASPECT_RATIO_16x9);
	static bool uSwap;
	const float fScreenWidth = Asura_GX::GetScreenWidth();
	const float fScreenHeight = Asura_GX::GetScreenHeight();

	if (bWideScreen)
	{
		const float fX1 = 0.0f;
		const float fX2 = 2.0f * fScreenWidth / 16.0f;
		const float fX3 = 14.0f * fScreenWidth / 16.0f;
		const float fX4 = fScreenWidth;

		GXBegin(GX_QUADS, GX_VTXFMT2, 4);
			GXPosition2f32(fX1, 0.0f);
			GXTexCoord2f32(0.0F, 0.0F);
			GXPosition2f32(fX2, 0.0f);
			GXTexCoord2f32(0.0f, 0.0F);
			GXPosition2f32(fX2, fScreenHeight);
			GXTexCoord2f32(0.0F, 0.0F);
			GXPosition2f32(fX1, fScreenHeight);
			GXTexCoord2f32(0.0F, 0.0F);
		GXEnd();


		GXBegin(GX_QUADS, GX_VTXFMT2, 4);
			GXPosition2f32(fX2, 0.0f);
			GXTexCoord2f32(0.0F, 0.0F);
			GXPosition2f32(fX3, 0.0f);
			GXTexCoord2f32(1.0F, 0.0F);
			GXPosition2f32(fX3, fScreenHeight);
			GXTexCoord2f32(1.0F, 1.0F);
			GXPosition2f32(fX2, fScreenHeight);
			GXTexCoord2f32(0.0F, 1.0F);
		GXEnd();

		GXBegin(GX_QUADS, GX_VTXFMT2, 4);
			GXPosition2f32(fX3, 0.0f);
			GXTexCoord2f32(0.0F, 0.0F);
			GXPosition2f32(fX4, 0.0f);
			GXTexCoord2f32(0.0f, 0.0F);
			GXPosition2f32(fX4, fScreenHeight);
			GXTexCoord2f32(0.0F, 0.0F);
			GXPosition2f32(fX3, fScreenHeight);
			GXTexCoord2f32(0.0F, 0.0F);
		GXEnd();

	}
	else
	{
		GXBegin(GX_QUADS, GX_VTXFMT2, 4);
			GXPosition2f32(0.0f, 0.0f);
			GXTexCoord2f32(0.0F, 0.0F);
			GXPosition2f32(fScreenWidth, 0.0f);
			GXTexCoord2f32(1.0F, 0.0F);
			GXPosition2f32(fScreenWidth, fScreenHeight);
			GXTexCoord2f32(1.0F, 1.0F);
			GXPosition2f32(0.0f, fScreenHeight);
			GXTexCoord2f32(0.0F, 1.0F);
		GXEnd();
	}

	GXCopyDisp(Asura_GX::GetFrameBuffer(uSwap ^ 1), GX_FALSE);
	GXDrawDone();
	VISetNextFrameBuffer( Asura_GX::GetFrameBuffer(uSwap ^= 1) );
	VIFlush();
	VIWaitForRetrace();
}

static volatile bool g_bReadTPL = false;

static void DVDCallbackFunction( s32 result, DVDFileInfo* fileInfo )
{
	g_bReadTPL = true;
}

TPLPalette* Asura_Wii_StrapReminder::LoadTPL(const char* szFilename, GXTexObj &xTexObj)
{
	DVDFileInfo xfileInfo;
	if (DVDOpen(szFilename, &xfileInfo))
	{
		u32 uFilesize = DVDGetLength(&xfileInfo);

		TPLPalette *pTPL = reinterpret_cast<TPLPalettePtr>(Asura_AllocateAlign( OSRoundUp32B(uFilesize), ASURA_ALIGN_32_BYTE));

		g_bReadTPL = false;

		DVDReadAsync(&xfileInfo, pTPL, uFilesize, 0, DVDCallbackFunction);

		while(!g_bReadTPL)
		{
			Asura_PlatformFileAccessUtils::CheckDiscStatus();
		}

		DCFlushRange(pTPL, uFilesize);

		if(pTPL->versionNumber != 2142000 )
		{
			OSHalt("invalid version number for texture palette");
		}

		pTPL->descriptorArray = (TPLDescriptorPtr)(((u32)(pTPL->descriptorArray)) + ((u32)pTPL));

		for (unsigned int i = 0; i < pTPL->numDescriptors; i++ )
		{
			if(pTPL->descriptorArray[i].textureHeader)
			{
				pTPL->descriptorArray[i].textureHeader = (TPLHeaderPtr)(((u32)(pTPL->descriptorArray[i].textureHeader)) + ((u32)pTPL));	
				if(!(pTPL->descriptorArray[i].textureHeader->unpacked))
				{
					pTPL->descriptorArray[i].textureHeader->data = (Ptr)((u32)(pTPL->descriptorArray[i].textureHeader->data) + (u32)pTPL);
					pTPL->descriptorArray[i].textureHeader->unpacked = 1;
				}
			}
			if(pTPL->descriptorArray[i].CLUTHeader)
			{
				pTPL->descriptorArray[i].CLUTHeader = (TPLClutHeaderPtr)((u32)(pTPL->descriptorArray[i].CLUTHeader) + (u32)pTPL);		
				if(!(pTPL->descriptorArray[i].CLUTHeader->unpacked))
				{
					pTPL->descriptorArray[i].CLUTHeader->data = (Ptr)((u32)(pTPL->descriptorArray[i].CLUTHeader->data) + (u32)pTPL);
					pTPL->descriptorArray[i].CLUTHeader->unpacked = 1;
				}
			}		
		}
		TPLGetGXTexObjFromPalette(pTPL, &xTexObj, 0);
		DVDClose(&xfileInfo);
		return pTPL;
	}
	return NULL;
}
