#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(THREAD_ATTR_USER | THREAD_ATTR_VFPU);

int line = 1;

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_A1A78C58(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 main_thread(SceSize args, void *argp)
{
	unload_loader();

	sceKernelDelayThread(1000*1000);
	
	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);
	
	if (!sceUmd_F2D0EDFB()) {
	
		doBlit("No umd disc in drive, changing mode to oe isofs");
		SEConfig config;
		int dummy[8]; // Dummy for new m33 configuration, thanks dark alex
		sctrlSEGetConfig(&config);	
		config.umdmode = MODE_NP9660;
		sctrlSESetConfig(&config);
		sceKernelDelayThread(10000);
			
		doBlit("Mounting iso");
		
		char buffer[128];
		int fd;
		fd = sceIoOpen("ms0:/seplugins/cfe/usbhost_iso.log", PSP_O_RDONLY, 0644);
		memset(buffer, 0, 128);
		sceIoRead(fd, buffer, 128);
		sceIoClose(fd);
		sceIoRemove("ms0:/seplugins/cfe/usbhost_iso.log");
		
		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);
		
		sceKernelDelayThread(10000);
		
		char buffer2[128];
		sprintf(buffer2, "Starting %s", buffer);
		doBlit(buffer2);
		
		loadStartDiscModule("disc0:/PSP_GAME/SYSDIR/EBOOT.BIN", 0, NULL);
		
	} 
	else {
		doBlit("Umd disc in drive, changing mode to Normal");
		SEConfig config;
		int dummy[8]; // Dummy for new m33 configuration, thanks dark alex
		sctrlSEGetConfig(&config);	
		config.umdmode = MODE_UMD;
		sctrlSESetConfig(&config);
		sceKernelDelayThread(10000);
			
		doBlit("Mounting iso");

		char buffer[128];
		int fd;
		fd = sceIoOpen("ms0:/seplugins/cfe/usbhost_iso.log", PSP_O_RDONLY, 0644);
		memset(buffer, 0, 128);
		sceIoRead(fd, buffer, 128);
		sceIoClose(fd);
		sceIoRemove("ms0:/seplugins/cfe/usbhost_iso.log");
		sceKernelDelayThread(1000);

		int i = sceUmd_F2D0EDFB(0);
		if(!(i)) sceUmd_8EF08FCE(UMD_WAITFORDISC);

		sceUmd_C6183D47(1, "disc0:");
		sceUmd_8EF08FCE(UMD_WAITFORINIT);
		
		sceKernelDelayThread(10000);
		
		sctrlSEMountUmdFromFile(buffer, 0, 1);
		sceKernelDelayThread(1000);
			
		char buffer2[128];
		sprintf(buffer2, "Starting %s", buffer);
		doBlit(buffer2);
		loadStartDiscModule("disc0:/PSP_GAME/SYSDIR/EBOOT.BIN", 0, NULL);
	}	

	sceKernelExitDeleteThread(0);
	
	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, 64*1024, 0, NULL);
	if(thid >= 0)
	{
		sceKernelStartThread(thid, args, argp);
	}

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