/*
 *  Copyright (C) 2010 WiiPower
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <gccore.h>
#include <ogcsys.h>
#include <sys/unistd.h>
#include <wiiuse/wpad.h>
//#include <ogc/lwp_watchdog.h>
//#include <time.h>
#include <malloc.h>
#include <stdarg.h>

#include "tools.h"
#include "storage.h"
#include "geckomenu.h"
#include "font.h"
#include "background.h"
#include "libcios.h"
#include "sha1.h"

extern s32 __IOS_ShutdownSubsystems();
extern void __exception_closeall();

u32 *xfb = NULL;
static GXRModeObj *rmode = NULL;
u32 screenheight;

void init_video()
{
	/* Init Video, Pad, Fonts */
	VIDEO_Init();
	init_font();

	rmode = VIDEO_GetPreferredMode(NULL);
	
	xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));

	VIDEO_Configure(rmode);

	VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK);
	VIDEO_SetNextFramebuffer(xfb);

	VIDEO_SetBlack(FALSE);

	VIDEO_Flush();

	VIDEO_WaitVSync();
	if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
    VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK);
	
	screenheight = rmode->xfbHeight;
}

static u32 hashes[12][5] = {
{0x20e60607, 0x4e02c484, 0x2bbc5758, 0xee2b40fc, 0x35a68b0a},		// cIOSrev13a
{0x620c57c7, 0xd155b67f, 0xa451e2ba, 0xfb5534d7, 0xaa457878}, 		// cIOSrev13b
{0x3c968e54, 0x9e915458, 0x9ecc3bda, 0x16d0a0d4, 0x8cac7917},		// cIOS37 rev18
{0xe811bca8, 0xe1df1e93, 0x779c40e6, 0x2006e807, 0xd4403b97},		// cIOS38 rev18
{0x697676f0, 0x7a133b19, 0x881f512f, 0x2017b349, 0x6243c037},		// cIOS57 rev18
{0x34ec540b, 0xd1fb5a5e, 0x4ae7f069, 0xd0a39b9a, 0xb1a1445f},		// cIOS60 rev18
{0xd98a4dd9, 0xff426ddb, 0x1afebc55, 0x30f75489, 0x40b27ade},		// cIOS70 rev18
{0x0a49cd80, 0x6f8f87ff, 0xac9a10aa, 0xefec9c1d, 0x676965b9},		// cIOS37 rev19
{0x09179764, 0xeecf7f2e, 0x7631e504, 0x13b4b7aa, 0xca5fc1ab},		// cIOS38 rev19
{0x6010d5cf, 0x396415b7, 0x3c3915e9, 0x83ded6e3, 0x8f418d54},		// cIOS57 rev19
{0x589d6c4f, 0x6bcbd80a, 0xe768f258, 0xc53a322c, 0xd143f8cd},		// cIOS60 rev19
{0x8969e0bf, 0x7f9b2391, 0x31ecfd88, 0x1c6f76eb, 0xf9418fe6}		// cIOS70 rev19
};

static char infos[12][24] = {
{"cIOS rev13a slot 249"},
{"cIOS rev13b slot 249"},
{"cIOS37rev18 slot 249"},
{"cIOS38rev18 slot 249"},
{"cIOS57rev18 slot 249"},
{"cIOS60rev18 slot 249"},
{"cIOS70rev18 slot 249"},
{"cIOS37rev19 slot 249"},
{"cIOS38rev19 slot 249"},
{"cIOS57rev19 slot 249"},
{"cIOS60rev19 slot 249"},
{"cIOS70rev19 slot 249"}
};	

static u32 info_number = 10;
	
void show_banner()
{
	signed_blob *TMD = NULL;
	tmd *t = NULL;
	u32 TMD_size = 0;
	u32 i;

	int ret = GetTMD((((u64)(1) << 32) | (IOS_GetVersion())), &TMD, &TMD_size);
	
	char *info;
	char default_info[32];
	sprintf(default_info, "IOS%u (Rev %u)", IOS_GetVersion(), IOS_GetRevision());
	info = (char *)default_info;

	if (ret == 0)
	{
		t = (tmd*)SIGNATURE_PAYLOAD(TMD);

		sha1 hash;
		SHA1((u8 *)TMD, TMD_size, hash);;

		free(TMD);

		for (i = 0;i < info_number;i++)
		{
			if (memcmp((void *)hash, (u32 *)&hashes[i], sizeof(sha1)) == 0)
			{
				info = (char *)&infos[i];
			}
		}
	}

	DrawBackground(rmode); // Show banner
	VIDEO_WaitVSync();
	write_font(58, screenheight - 60,true, "NeoGamma R9 beta23, %s", info);
	drawicon(xfb, 10, screenheight - 63, 32/2, 27,0); // Show Gecko icon
	VIDEO_WaitVSync();
}

void wait(u32 s)
{
	time_t t;
	t = time(NULL) + s; 
	while (time(NULL) < t)
	{
		// ;
		Verify_Flags(); // Standby and Reset
	}
}

void clear_status()
{
	clearscreen(xfb, 0, 210, 320, 24, 0x00800080);
}

void print_status(const char *Format, ...)
{
	char Buffer[256];
	va_list args;

	va_start(args, Format);
	vsprintf(Buffer, Format, args);

	va_end(args);

	clear_status();
	write_font(185, 210,true, Buffer);
	VIDEO_WaitVSync();
}

void *allocate_memory(u32 size)
{
	return memalign(32, (size+31)&(~31) );
}

void Verify_Flags()
{
	if (Power_Flag)
	{
		WPAD_Shutdown();
		STM_ShutdownToStandby();
	}
	if (Reset_Flag)
	{
		WPAD_Shutdown();
		STM_RebootSystem();
	}
}

void waitforbuttonpress(u32 *out, u32 *outGC)
{
	u32 pressed = 0;
	u32 pressedGC = 0;

	while (true)
	{
		Verify_Flags(); // Standby and Reset

		WPAD_ScanPads();
		pressed = WPAD_ButtonsDown(0) | WPAD_ButtonsDown(1) | WPAD_ButtonsDown(2) | WPAD_ButtonsDown(3);

		PAD_ScanPads();
		pressedGC = PAD_ButtonsDown(0) | PAD_ButtonsDown(1) | PAD_ButtonsDown(2) | PAD_ButtonsDown(3);

		if (pressed || pressedGC) 
		{
			if (pressedGC)
			{
				// Without waiting you can't select anything
				usleep (20000);
			}
			if (out) *out = pressed;
			if (outGC) *outGC = pressedGC;
			return;
		}
	}
}


void shutdown_all()
{
	storage_shutdown();
	WPAD_Shutdown();
	IRQ_Disable();
	__IOS_ShutdownSubsystems();
	__exception_closeall();
}

u32 be32(const u8 *data)
{
	return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
}
