#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/unistd.h>
#include <pspkernel.h>
#include <pspctrl.h>
#include <pspdebug.h>
#include <pspdisplay.h>
#include <pspiofilemgr.h>
#include <psptypes.h>
#include <pspsdk.h> 

#include <pspumd.h>

#include "blit.h"

#include "../../include/systemctrl_se.h"
#include "../../include/pspmodulemgr_kernel.h"
#include "../../include/systemctrl.h"

#define HOSTFSDRIVER_NAME "USBHostFSDriver"
#define HOSTFSDRIVER_PID  (0x1C9)
#define PSP_USBBUS_DRIVERNAME "USBBusDriver"

PSP_MODULE_INFO("usbhost_loader", 0x1000, 1, 1);   // 0x1000 = Kernel MODE
PSP_MAIN_THREAD_ATTR(0);
//PSP_MAIN_THREAD_STACK_SIZE_KB(0);

int line = 1;
int usbhost = 0;
int nethost = 0;
int game = 0;
int iso = 0;

char pc_ip_address[16];

void doBlit(char *msg)
{
	
	blit_string(1, line, msg,0xffffff,0x000000);
	line++;
}

int unload_loader(void)
{
	SceModule *mod;
	SceUID modid;
	int ret = 0;
	int status;

	mod = sceKernelFindModuleByName("usbiso_bootloader");
	if(mod != NULL)
	{
		modid = mod->modid;
		ret = sceKernelStopModule(modid, 0, NULL, &status, NULL);
		if(ret >= 0)
		{
			ret = sceKernelUnloadModule(modid);
		}
	}
	else
	{
		doBlit("Couldn't find bootloader\n");
	}

	return 0;
}

int build_args(char *args, const char *execfile, int argc, char **argv)
{
	int loc = 0;
	int i;

	strcpy(args, execfile);
	loc += strlen(execfile) + 1;
	for(i = 0; i < argc; i++)
	{
		strcpy(&args[loc], argv[i]);
		loc += strlen(argv[i]) + 1;
	}

	return loc;
}

int loadStartModule(const char *name, int argc, char **argv)
{
	SceUID modid;
	int status;
	char args[1024];
	int len;

	modid = sceKernelLoadModule(name, 0, NULL);
	if(modid >= 0)
	{
		len = build_args(args, name, argc, argv);
		modid = sceKernelStartModule(modid, len, (void *) args, &status, NULL);
	}
	else
	{
		doBlit("Error loading module.");
	}

	return modid;
}


int loadStartDiscModule(const char *name, int argc, char **argv)
{
	SceUID modid;
	int status;
	char args[1024];
	int len;

	SceKernelLMOption option;
	SceUID mpid = PSP_MEMORY_PARTITION_USER;

	memset(&option, 0, sizeof(option));
   
	option.size = sizeof(option);
	option.mpidtext = mpid;
	option.mpiddata = mpid;
	option.position = 0;
	option.access = 1; 
		
	modid = ModuleMgrForKernel_1B91F6EC(name, 0, &option);
	if(modid >= 0)
	{
		len = build_args(args, name, argc, argv);
		modid = sceKernelStartModule(modid, len, (void *) args, &status, NULL);
	}
	else
	{
		doBlit("Error loading module.");
	}

	return modid;
}

int loadStartEbootModule(const char *name, int argc, char **argv)
{
	SceUID modid;
	int status;
	char args[1024];
	int len;

	SceKernelLMOption option;
	SceUID mpid = PSP_MEMORY_PARTITION_USER;

	memset(&option, 0, sizeof(option));
   
	option.size = sizeof(option);
	option.mpidtext = mpid;
	option.mpiddata = mpid;
	option.position = 1;
	option.access = 1; 
		
	modid = sceKernelLoadModuleForLoadExecVSHMs2(name, 0, &option);
	if(modid >= 0)
	{
		len = build_args(args, name, argc, argv);
		modid = sceKernelStartModule(modid, len, (void *) args, &status, NULL);
	}
	else
	{
		doBlit("Error loading module.");
	}

	return modid;
}

int loadNetHost()
{
	doBlit("Starting Nethost");

	char *argv[5];
	
	char param0[16];
	strncpy(param0, pc_ip_address, strlen(pc_ip_address)+1);
	char param1[]="7513";
	char param2[]="1";
	char param3[]="test";
	char param4[]="test";
	
	argv[0] = param0;
	argv[1] = param1;
	argv[2] = param2;
	argv[3] = param3;
	argv[4] = param4;
	
	loadStartModule("ms0:/seplugins/cfe/nethostfs_371.prx", 5, &argv);
	
	sceKernelDelayThread(100*100000);
	
	return 0;
}

int loadUsbHost() {

	doBlit("Starting Usbhost");
	
	loadStartModule("ms0:/seplugins/cfe/usbhostfs.prx", 0, NULL);
	int retVal = 0;
	retVal = sceUsb_driver_AE5DE6AF(PSP_USBBUS_DRIVERNAME, 0, 0); //SceUsbStart
	if (retVal != 0) {
		printf("Error starting USB Bus driver\n");
		return 0;
	}
	retVal = sceUsb_driver_AE5DE6AF(HOSTFSDRIVER_NAME, 0, 0);
	if (retVal != 0) {
		printf("Error starting USB Host driver\n");
		return 0;
	}
	retVal = sceUsb_driver_586DB82C(HOSTFSDRIVER_PID); //SceUsbActivate
			
	sceKernelDelayThread(30*100000);

	return 0;
}

int main_thread(SceSize args, void *argp)
{
	char buffer[128];
	int fd;
	
	unload_loader();

	sceKernelDelayThread(1000*1000);
	
	fd = sceIoOpen("fatms0:/seplugins/cfe/nethost_iso_ip", PSP_O_RDONLY, 0644);
	if(fd < 0){
		sceIoClose(fd);
	}
	else {
		sceIoRead(fd, pc_ip_address, 16);
		sceIoClose(fd);
		sceIoRemove("fatms0:/seplugins/cfe/nethost_iso_ip");
		loadNetHost();
		iso=1;
		nethost=1;
	}
	
	if(!nethost) {
		fd = sceIoOpen("fatms0:/seplugins/cfe/nethost_game_ip", PSP_O_RDONLY, 0644);
		if(fd < 0){
			sceIoClose(fd);
		}
		else {
			sceIoRead(fd, pc_ip_address, 16);
			sceIoClose(fd);
			sceIoRemove("fatms0:/seplugins/cfe/nethost_game_ip");
			loadNetHost();
			game=1;
			nethost=1;
		}	
	}
	
	if(!nethost) {
		fd = sceIoOpen("fatms0:/seplugins/cfe/usbhost_iso", PSP_O_RDONLY, 0644);		
		if(fd < 0){
			sceIoClose(fd);
		}
		else {
			sceIoClose(fd);
			sceIoRemove("fatms0:/seplugins/cfe/usbhost_iso");
			loadUsbHost();
			iso=1;
			usbhost=1;
		}
	}
	
	if(!nethost) {
		if(!usbhost) {
			fd = sceIoOpen("fatms0:/seplugins/cfe/usbhost_game", PSP_O_RDONLY, 0644);		
			if(fd < 0){
				sceIoClose(fd);
			}
			else {
				sceIoClose(fd);
				sceIoRemove("fatms0:/seplugins/cfe/usbhost_game");
				loadUsbHost();
				game=1;
				usbhost=1;		
			}
		}
	}

	int discIn = 0;
	
	if(iso)
	{
		if (sceUmd_F2D0EDFB()) {
			discIn = 1;
			sctrlSESetDiscOut(1);
			doBlit("Umd disc in drive, using Normal UMD mode");
		}
		else 
		{
			doBlit("No Umd disc in drive, using oe isofs mode");
		}
	}

	fd = sceIoOpen("ms0:/seplugins/cfe/game_path", PSP_O_RDONLY, 0644);
	memset(buffer, 0, 128);
	sceIoRead(fd, buffer, 128);
	sceIoClose(fd);
	sceIoRemove("ms0:/seplugins/cfe/game_path");
	sceKernelDelayThread(20*100000);

	if(iso) {
	
		SEConfig config;
		int dummy[8]; 
		sctrlSEGetConfig(&config);

		doBlit("Mounting iso");
		
		if(discIn) {
		
			sctrlSESetDiscOut(0);
			sceKernelDelayThread(10000);	
			sctrlSEMountUmdFromFile(buffer, 0, config.useisofsonumdinserted);
			sceKernelDelayThread(10000);
			
			int i = sceUmd_F2D0EDFB(0);
			if(!(i)) sceUmd_8EF08FCE(UMD_WAITFORDISC);
			sceUmd_C6183D47(1, "disc0:");
			sceUmd_8EF08FCE(UMD_WAITFORINIT);
			
			doBlit("Starting: ");
			doBlit(buffer);
		
			loadStartDiscModule("disc0:/PSP_GAME/SYSDIR/EBOOT.BIN", 0, NULL);
		}
		else {
	
			sctrlSEMountUmdFromFile(buffer, 1, 1);
			sceKernelDelayThread(1000);
			
			int i = sceUmd_F2D0EDFB(0);
			if(!(i)) sceUmd_8EF08FCE(UMD_WAITFORDISC);

			sceUmd_C6183D47(1, "disc0:");
			sceUmd_8EF08FCE(UMD_WAITFORINIT);

			doBlit("Starting: ");
			doBlit(buffer);
		
			loadStartDiscModule("disc0:/PSP_GAME/SYSDIR/EBOOT.BIN", 0, NULL);
		}	
	}
	else {
		doBlit("Starting: ");	
		doBlit(buffer);
		loadStartEbootModule(buffer, 0, NULL);
	}
	
	sceKernelSelfStopUnloadModule(1, 0, NULL);
	
	return 0;
}

/* Entry point */
int module_start(SceSize args, void *argp)
{
	int thid;

	/* Create a high priority thread */
	thid = sceKernelCreateThread("usbdevice_flasher", main_thread, 8, 0x1000, 0, NULL);
	if(thid >= 0)
	{
		sceKernelStartThread(thid, args, argp);
	}

	return 0;
}
int module_stop()
{
   return 0;
}
