#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pspiofilemgr.h>
#include <pspctrl.h>

#include "conf.h"

//#define isvalidvarchar(c) (isalnum(c) || c == '_' || c == '=') 

#define isvalidvarchar(c) (c == 'a' || c == 'b' || c == 'c' || c == 'd' || c == 'e' || c == 'f' \
 || c == 'g' || c == 'h' || c == 'i' || c == 'j' || c == 'k' || c == 'l' || c == 'm' || c == 'n' || c == 'o' \
 || c == 'p' || c == 'q' || c == 'r' || c == 's' || c == 't' || c == 'u' || c == 'v' || c == 'w' || c == 'x' \
 || c == 'y' || c == 'z' || c == 'A' || c == 'B' || c == 'C' || c == 'D' || c == 'E' || c == 'F' \
 || c == 'G' || c == 'H' || c == 'I' || c == 'J' || c == 'K' || c == 'L' || c == 'M' || c == 'N' || c == 'O' \
 || c == 'P' || c == 'Q' || c == 'R' || c == 'S' || c == 'T' || c == 'U' || c == 'V' || c == 'W' || c == 'X' \
 || c == 'Y' || c == 'Z' || c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' \
 || c == '7' || c == '8' || c == '9' || c == '_' || c == '=')


static int read_line(SceUID fd, char *line, int max)
{
	int i, read;
	char ch;

	i = 0;

	if (max == 0)
		return 1;

	do
	{
		read = sceIoRead(fd, &ch, 1);
		
		if (read && ch != '\n' && ch != '\r')
			line[i++] = ch;		

	} while (ch != '\n' && read == 1 && i < max);

	line[i] = 0;

	return !read;
}

static int get_tokens(char tokens[][128], int maxtokens, char *line)
{
	int iline = 0;
	int itoken = 0;
	int jtoken = 0;
	int intoken = 0;
	int instring = 0;
	char ch;

	while (itoken < maxtokens)
	{
		ch = line[iline++];

		if (ch == 0)
		{
			if (instring)
				return 0; // Error: not terminated string

			if (intoken)			
				tokens[itoken++][jtoken] = 0;

			break;
		}

		if (!instring && (ch == '#' || ch == ';'))
		{
			if (intoken)			
				tokens[itoken++][jtoken] = 0;

			break;
		}

		//if (isspace(ch) || ch == '=')
		if(ch == ' ' || ch == '=')
		{
			if (intoken)
			{
				if (!instring)
				{
					intoken = 0;
					tokens[itoken++][jtoken] = 0;
					jtoken = 0;
				}

				else
				{
					tokens[itoken][jtoken++] = ch;
				}
			}
		}

		else if (ch == '"')
		{
			if (intoken)
			{
				if ((!instring && jtoken != 0) || 
					(instring && isvalidvarchar(line[iline])))
				{
					// Error: Mixing string token with something else 
					return 0; 
				}

				tokens[itoken][jtoken++] = ch;				
				instring = !instring;
			}
			
			else
			{
				intoken = 1;
				instring = 1;
				tokens[itoken][jtoken++] = ch;
			}
		}

		else if (isvalidvarchar(ch))
		{
			if (!intoken)
				intoken = 1;

			tokens[itoken][jtoken++] = ch;
		}

		else
		{
			if (instring)
				tokens[itoken][jtoken++] = ch;
		}
	}

	return itoken;
}

static char *get_string(char *out, int max, char *in)
{
	char *p;
	int len;
	
	memset(out, 0, max);

	if (in[0] != '"')
		return NULL;

	p = strchr(in+1, '"');

	if (!p)
		return NULL;

	if (p-(in+1) > max)
		len = max;
	else
		len = p-(in+1);

	strncpy(out, in+1, len);	

	return out;
}

static int get_integer(char *str)
{
	return strtol(str, NULL, 0);
}

static int get_boolean(char *str)
{
	if (strcmp(str, "false") == 0)
		return 0;

	if (strcmp(str, "true") == 0)
		return 1;	

	if (strcmp(str, "off") == 0)
		return 0;

	if (strcmp(str, "on") == 0)
		return 1;

	return get_integer(str);
}


void read_config(const char *file, CONFIGFILE *config)
{
	SceUID fd = sceIoOpen(file, PSP_O_RDONLY, 0777);

	/* Default: all values to zero */
	memset(config, 0, sizeof(CONFIGFILE));

	if (fd > 0)
	{
		int eof = 0, ntokens;
		char line[128];
		char tokens[2][128];
		
		while (!eof)
		{
			eof = read_line(fd, line, 127);
			ntokens = get_tokens(tokens, 2, line);	

			if (ntokens == 2)
			{
				// Default vars
				if (strcmp(tokens[0], "enableUsbMass") == 0)
				{
					config->enableUsbMass = get_boolean(tokens[1]);
				} 
				else if (strcmp(tokens[0], "defaultCpuSpeedVsh") == 0)
				{
					config->defaultCpuSpeedVsh = get_integer(tokens[1]);
				}
				else if (strcmp(tokens[0], "defaultBrightnessVsh") == 0)
				{
					config->defaultBrightnessVsh = get_integer(tokens[1]);
				}	
				else if (strcmp(tokens[0], "usePspSlim") == 0)
				{
					config->usePspSlim = get_boolean(tokens[1]);
				}
				
				
				else if (strcmp(tokens[0], "comboButton") == 0)
				{
					get_string(config->comboButton, 10, tokens[1]);
				}
				
				
				// Reset Function
				else if (strcmp(tokens[0], "resetButton") == 0)
				{
					get_string(config->resetButton, 10, tokens[1]);
					resetEnable=1;	
				}
				
				
				// Power Off Function
				else if (strcmp(tokens[0], "poweroffButton") == 0)
				{
					get_string(config->poweroffButton, 10, tokens[1]);
					poweroffEnable=1;
				}
				
				
				// Cpu Speed Function			
				else if (strcmp(tokens[0], "cpuspeedButton") == 0)
				{
					get_string(config->cpuspeedButton, 10, tokens[1]);
					cpuspeedEnable=1;
				}
				
				
				// Usb Mass Storage Function
				else if (strcmp(tokens[0], "startStopUsbmassButton") == 0)
				{
					get_string(config->startStopUsbmassButton, 10, tokens[1]);
					startStopUsbmassEnable=1;
				}
				
				
				// Ftpd Function
				else if (strcmp(tokens[0], "startFtpdButton") == 0)
				{
					get_string(config->startFtpdButton, 10, tokens[1]);
					startFtpdEnable=1;
				}
				
				
				// Screenshot Function
				else if (strcmp(tokens[0], "takeScreenshotButton") == 0)
				{
					get_string(config->takeScreenshotButton, 10, tokens[1]);
					takeScreenshotEnable=1;
				}
				
				
				// Capture Function
				else if (strcmp(tokens[0], "captureButton") == 0)
				{
					get_string(config->captureButton, 10, tokens[1]);
					captureEnable=1;
				}
				
				// Brightness Function
				else if (strcmp(tokens[0], "maxBrightnessButton") == 0)
				{
					get_string(config->maxBrightnessButton, 10, tokens[1]);
					maxBrightnessEnable=1;
				}
	
	
				// Usbhostfs UMD Mode Function
				else if (strcmp(tokens[0], "usbhostUmdModeButton") == 0)
				{
					get_string(config->usbhostUmdModeButton, 10, tokens[1]);
					usbhostUmdModeEnable=1;
				}
	
				// Help Function
				else if (strcmp(tokens[0], "showHelpButton") == 0)
				{
					get_string(config->showHelpButton, 10, tokens[1]);
					showHelpEnable=1;	
				}
				
				// Capture Folder Directory
				else if (strcmp(tokens[0], "captureFolder") == 0)
				{
					get_string(config->captureFolder, 64, tokens[1]);
				}	

				// Replace Update Icon FUnction
				else if (strcmp(tokens[0], "replace_update_icon_with") == 0)
				{
					get_string(config->replace_update_icon_with, 128, tokens[1]);
					enable_replace_update_icon_with=1;	
				}
				// Replace Video Icon FUnction
				else if (strcmp(tokens[0], "replace_video_icon_with") == 0)
				{
					get_string(config->replace_video_icon_with, 128, tokens[1]);
					enable_replace_video_icon_with=1;	
				}
				// Replace location free Icon FUnction
				else if (strcmp(tokens[0], "replace_locationFree_icon_with") == 0)
				{
					get_string(config->replace_locationFree_icon_with, 128, tokens[1]);
					enable_replace_locationFree_icon_with=1;
				}	
				// Replace RemotePlay Icon FUnction
				else if (strcmp(tokens[0], "replace_remotePlay_icon_with") == 0)
				{
					get_string(config->replace_remotePlay_icon_with, 128, tokens[1]);
					enable_replace_remotePlay_icon_with=1;
				}
				// Replace Rss Icon FUnction
				else if (strcmp(tokens[0], "replace_rss_icon_with") == 0)
				{
					get_string(config->replace_rss_icon_with, 128, tokens[1]);
					enable_replace_rss_icon_with=1;
				}	
				// Replace game sharing Icon FUnction
				else if (strcmp(tokens[0], "replace_gameSharing_icon_with") == 0)
				{
					get_string(config->replace_gameSharing_icon_with, 128, tokens[1]);
					enable_replace_gameSharing_icon_with=1;
				}	
				// Replace camera Icon FUnction
				else if (strcmp(tokens[0], "replace_camera_icon_with") == 0)
				{
					get_string(config->replace_camera_icon_with, 128, tokens[1]);
					enable_replace_camera_icon_with=1;
				}	
				
				// nethost
				else if (strcmp(tokens[0], "pc_ip_address") == 0)
				{
					get_string(config->pc_ip_address, 16, tokens[1]);
				}	
				
				else if (strcmp(tokens[0], "use_transparency") == 0)
				{
					config->use_transparency = get_boolean(tokens[1]);
				} 
			}
		}

		sceIoClose(fd);
	}
}
