/***************************************************************************

	config.c

	Application configuration file manager

***************************************************************************/

#include "ncdz.h"

#define LINEBUF_SIZE	256


enum
{
	CFG_NONE = 0,
	CFG_INT,
	CFG_BOOL,
	CFG_PAD,
	CFG_STR
};

enum
{
	PAD_NONE = 0,
	PAD_UP,
	PAD_DOWN,
	PAD_LEFT,
	PAD_RIGHT,
	PAD_CIRCLE,
	PAD_CROSS,
	PAD_SQUARE,
	PAD_TRIANGLE,
	PAD_LTRIGGER,
	PAD_RTRIGGER,
	PAD_START,
	PAD_SELECT,
	PAD_MAX
};

typedef struct cfg_t
{
	int type;
	const char *name;
	int *value;
	int def;
	int max;
} cfg_type;

typedef struct cfg2_t
{
	int type;
	const char *name;
	char *value;
	int max_len;
} cfg2_type;


/****************************************************************************
	Function prototypes
 ***************************************************************************/

static int load_inifile(const char *path, cfg_type *cfg, cfg2_type *cfg2);
static int save_inifile(const char *path, cfg_type *cfg, cfg2_type *cfg2);

static int get_config_bool(char *str);
static int get_config_int(char *str, int maxval);
static int get_config_pad(char *str);

static const char *set_config_bool(int value);
static char *set_config_int(int value, int maxval);
static const char *set_config_pad(int value);


/****************************************************************************
	Private variables
 ***************************************************************************/

static const char *inifile_name = "ncdzpsp.ini";


/****************************************************************************
	Private structures
 ***************************************************************************/

static cfg_type default_options[] =
{
	{ CFG_NONE,	"[System Settings]", },
	{ CFG_INT,	"PSPClock",				&psp_cpuclock,			0,	2	},

	{ CFG_NONE,	"[Machine Settings]", },
#if JAPANESE_UI
	{ CFG_INT,	"RegionSwitch",			&neogeo_region,			0,	2	},
#else
	{ CFG_INT,	"RegionSwitch",			&neogeo_region,			1,	2	},
#endif
	{ CFG_BOOL,	"EnableLoadScreen",		&neogeo_loadscreen,		0,	1	},
	{ CFG_BOOL,	"CDROMSpeedLimit",		&neogeo_cdspeed_limit,	0,	1	},

	{ CFG_NONE,	"[Video Settings]", },
	{ CFG_INT,	"RenderMode",			&neogeo_blitter,		0,	3	},
	{ CFG_BOOL,	"WaitVsync",			&neogeo_vsync,			0,	0	},
	{ CFG_BOOL,	"AutoFrameSkip",		&neogeo_autoframeskip,	0,	1	},
	{ CFG_INT,	"FrameSkipLevel",		&neogeo_frameskip,		0,	11	},
	{ CFG_BOOL,	"ShowFPS",				&neogeo_showfps,		0,	1	},
	{ CFG_BOOL,	"60FPSLimit",			&neogeo_speedlimit,		1,	1	},

	{ CFG_NONE,	"[Audio Settings]", },
	{ CFG_BOOL,	"EnableSound",			&neogeo_sound_enable,	1,	1	},
	{ CFG_INT,	"SampleRate",			&neogeo_samplerate,		1,	2	},
	{ CFG_INT,	"SoundVolume",			&neogeo_sound_volume,	10,	10	},
	{ CFG_BOOL,	"EnableCDDA",			&neogeo_cdda_enable,	1,	1	},
	{ CFG_INT,	"CDDAVolume",			&neogeo_cdda_volume,	10,	10	},

	{ CFG_NONE,	"[Input Settings]", },
	{ CFG_INT,	"Controler",			&neogeo_input_port,		0,	1	},

	{ CFG_NONE,	"[Color Settings]", },
	{ CFG_INT,	"BGImageType",			&bgimage_type,					  0,  3	},
	{ CFG_INT,	"BGImageBlightness",	&bgimage_blightness,			 75,100	},
	{ CFG_INT,	"TitleFontR",			&ui_palette[UI_PAL_TITLE].r,	255,255	},
	{ CFG_INT,	"TitleFontG",			&ui_palette[UI_PAL_TITLE].g,	255,255	},
	{ CFG_INT,	"TitleFontB",			&ui_palette[UI_PAL_TITLE].b,	255,255	},
	{ CFG_INT,	"SelectFontR",			&ui_palette[UI_PAL_SELECT].r,	255,255	},
	{ CFG_INT,	"SelectFontG",			&ui_palette[UI_PAL_SELECT].g,	255,255	},
	{ CFG_INT,	"SelectFontB",			&ui_palette[UI_PAL_SELECT].b,	255,255	},
	{ CFG_INT,	"NormalFontR",			&ui_palette[UI_PAL_NORMAL].r,	180,255	},
	{ CFG_INT,	"NormalFontG",			&ui_palette[UI_PAL_NORMAL].g,	180,255	},
	{ CFG_INT,	"NormalFontB",			&ui_palette[UI_PAL_NORMAL].b,	180,255	},
	{ CFG_INT,	"InfoFontR",			&ui_palette[UI_PAL_INFO].r,		255,255	},
	{ CFG_INT,	"InfoFontG",			&ui_palette[UI_PAL_INFO].g,		255,255	},
	{ CFG_INT,	"InfoFontB",			&ui_palette[UI_PAL_INFO].b,		 64,255	},
	{ CFG_INT,	"WarningFontR",			&ui_palette[UI_PAL_WARNING].r,	255,255	},
	{ CFG_INT,	"WarningFontG",			&ui_palette[UI_PAL_WARNING].g,	 64,255	},
	{ CFG_INT,	"WarningFontB",			&ui_palette[UI_PAL_WARNING].b,	 64,255	},
	{ CFG_INT,	"BGColor1R",			&ui_palette[UI_PAL_BG1].r,		48,	255	},
	{ CFG_INT,	"BGColor1G",			&ui_palette[UI_PAL_BG1].g,		48,	255	},
	{ CFG_INT,	"BGColor1B",			&ui_palette[UI_PAL_BG1].b,		48,	255	},
	{ CFG_INT,	"BGColor2R",			&ui_palette[UI_PAL_BG2].r,		34,	255	},
	{ CFG_INT,	"BGColor3G",			&ui_palette[UI_PAL_BG2].g,		34,	255	},
	{ CFG_INT,	"BGColor2B",			&ui_palette[UI_PAL_BG2].b,		34,	255	},
	{ CFG_INT,	"FrameColorR",			&ui_palette[UI_PAL_FRAME].r,	0,	255	},
	{ CFG_INT,	"FrameColorG",			&ui_palette[UI_PAL_FRAME].g,	0,	255	},
	{ CFG_INT,	"FrameColorB",			&ui_palette[UI_PAL_FRAME].b,	0,	255	},
	{ CFG_INT,	"FileSelect1R",			&ui_palette[UI_PAL_FILESEL1].r,	40,	255	},
	{ CFG_INT,	"FileSelect1G",			&ui_palette[UI_PAL_FILESEL1].g,	40,	255	},
	{ CFG_INT,	"FileSelect1B",			&ui_palette[UI_PAL_FILESEL1].b,	40,	255	},
	{ CFG_INT,	"FileSelect2R",			&ui_palette[UI_PAL_FILESEL2].r,	120,255	},
	{ CFG_INT,	"FileSelect2G",			&ui_palette[UI_PAL_FILESEL2].g,	120,255	},
	{ CFG_INT,	"FileSelect2B",			&ui_palette[UI_PAL_FILESEL2].b,	120,255	},

	{ CFG_NONE, NULL, }
};

static cfg2_type default_options2[] =
{
	{ CFG_NONE,	"[Directory Settings]", 				},
	{ CFG_STR,	"StartupDir", startupDir,	MAX_PATH	},
	{ CFG_NONE, NULL, }
};

static cfg_type default_keycfg[] =
{
	{ CFG_NONE,	"[NEOGEO CDZ Settings]", },
	{ CFG_PAD,	"Up",			&input_map[0],	PSP_CTRL_UP,		0	},
	{ CFG_PAD,	"Down",			&input_map[1],	PSP_CTRL_DOWN,		0	},
	{ CFG_PAD,	"Left",			&input_map[2],	PSP_CTRL_LEFT,		0	},
	{ CFG_PAD,	"Right",		&input_map[3],	PSP_CTRL_RIGHT,		0	},
	{ CFG_PAD,	"ButtonA",		&input_map[4],	PSP_CTRL_CROSS,		0	},
	{ CFG_PAD,	"ButtonB",		&input_map[5],	PSP_CTRL_CIRCLE,	0	},
	{ CFG_PAD,	"ButtonC",		&input_map[6],	PSP_CTRL_SQUARE,	0	},
	{ CFG_PAD,	"ButtonD",		&input_map[7],	PSP_CTRL_TRIANGLE,	0	},
	{ CFG_PAD,	"Start",		&input_map[8],	PSP_CTRL_START,		0	},
	{ CFG_PAD,	"Select",		&input_map[9],	PSP_CTRL_SELECT,	0	},

	{ CFG_NONE,	"[Autofire Settings]", },
	{ CFG_PAD,	"AutofireA",	&input_map[10],	0,	0	},
	{ CFG_PAD,	"AutofireB",	&input_map[11],	0,	0	},
	{ CFG_PAD,	"AutofireC",	&input_map[12],	0,	0	},
	{ CFG_PAD,	"AutofireD",	&input_map[13],	0,	0	},
	{ CFG_INT,	"AFInterval",	&af_interval,	1,	10	},

	{ CFG_NONE,	"[Hotkey Settings]", },
	{ CFG_PAD,	"HotkeyeAB",	&input_map[14],	0,	0	},
	{ CFG_PAD,	"HotkeyeAC",	&input_map[15],	0,	0	},
	{ CFG_PAD,	"HotkeyeAD",	&input_map[16],	0,	0	},
	{ CFG_PAD,	"HotkeyeBC",	&input_map[17],	0,	0	},
	{ CFG_PAD,	"HotkeyeBD",	&input_map[18],	0,	0	},
	{ CFG_PAD,	"HotkeyeCD",	&input_map[19],	0,	0	},
	{ CFG_PAD,	"HotkeyeABC",	&input_map[20],	0,	0	},
	{ CFG_PAD,	"HotkeyeABD",	&input_map[21],	0,	0	},
	{ CFG_PAD,	"HotkeyeACD",	&input_map[22],	0,	0	},
	{ CFG_PAD,	"HotkeyeBCD",	&input_map[23],	0,	0	},
	{ CFG_PAD,	"HotkeyeABCD",	&input_map[24],	0,	0	},

	{ CFG_NONE,	"[System Key Settings]", },
	{ CFG_PAD,	"Menu",			&input_map[25],	PSP_CTRL_LTRIGGER,	0	},
	{ CFG_PAD,	"Snapshot",		&input_map[26],	0,	0	},
	{ CFG_PAD,	"SwitchPlayer",	&input_map[27],	0,	0	},

	{ CFG_NONE,	"[Emulation Settings]", },
	{ CFG_INT,	"SpriteCacheClearTimeing",	&neogeo_cacheclear,	3,	3	},

	{ CFG_NONE, NULL, }
};


typedef struct padname_t
{
	int code;
	const char name[16];
} PADNAME;

static const PADNAME pad_name[13] =
{
	{ 0,					"PAD_NONE"		},
	{ PSP_CTRL_UP,			"PAD_UP"		},
	{ PSP_CTRL_DOWN,		"PAD_DOWN"		},
	{ PSP_CTRL_LEFT,		"PAD_LEFT"		},
	{ PSP_CTRL_RIGHT,		"PAD_RIGHT"		},
	{ PSP_CTRL_CROSS,		"PAD_CROSS"		},
	{ PSP_CTRL_CIRCLE,		"PAD_CIRCLE"	},
	{ PSP_CTRL_SQUARE,		"PAD_SQUARE"	},
	{ PSP_CTRL_TRIANGLE,	"PAD_TRIANGLE"	},
	{ PSP_CTRL_START,		"PAD_START"		},
	{ PSP_CTRL_SELECT,		"PAD_SELECT"	},
	{ PSP_CTRL_LTRIGGER,	"PAD_LTRIGGER"	},
	{ PSP_CTRL_RTRIGGER,	"PAD_RTRIGGER"	}
};


/*------------------------------------------------------
	Load settings
 -----------------------------------------------------*/

void load_settings(void)
{
	int i;
	char path[MAX_PATH];

	for (i = 0; default_options[i].name; i++)
	{
		if (default_options[i].value)
			*default_options[i].value = default_options[i].def;
	}

	sprintf(path, "%s%s", launchDir, inifile_name);

	if (load_inifile(path, default_options, default_options2) == 0)
		save_settings();
}


/*------------------------------------------------------
	Save settings
 -----------------------------------------------------*/

void save_settings(void)
{
	char path[MAX_PATH];

	sprintf(path, "%s%s", launchDir, inifile_name);

	save_inifile(path, default_options, default_options2);
}


/*------------------------------------------------------
	Load keyconfig
 -----------------------------------------------------*/

void load_keyconfig(const char *name)
{
	int i;
	char path[MAX_PATH];

	for (i = 0; default_keycfg[i].name; i++)
	{
		if (default_keycfg[i].value)
			*default_keycfg[i].value = default_keycfg[i].def;
	}

	sprintf(path, "%sconfig/%s.ini", launchDir, name);

	if (load_inifile(path, default_keycfg, NULL) == 0)
		save_keyconfig(name);
}


/*------------------------------------------------------
	Save keyconfig
 -----------------------------------------------------*/

void save_keyconfig(const char *name)
{
	char path[MAX_PATH];

	sprintf(path, "%sconfig/%s.ini", launchDir, name);

	save_inifile(path, default_keycfg, NULL);
}


/*------------------------------------------------------
	Load settings from .ini file
 -----------------------------------------------------*/

static int load_inifile(const char *path, cfg_type *cfg, cfg2_type *cfg2)
{
	FILE *fp;

	if ((fp = fopen(path, "r")) != NULL)
	{
		int i;
		char linebuf[LINEBUF_SIZE];

		while (1)
		{
			char *name, *value;

			memset(linebuf, LINEBUF_SIZE, 0);
			if (fgets(linebuf, LINEBUF_SIZE - 1, fp) == NULL)
				break;

			if (linebuf[0] == ';' || linebuf[0] == '[')
				continue;

			name = strtok(linebuf, " =\r\n");
			if (name == NULL)
				continue;

			value = strtok(NULL, " =\r\n");
			if (value == NULL)
				continue;

			/* check name and value */
			for (i = 0; cfg[i].name; i++)
			{
				if (!strcmp(name, cfg[i].name))
				{
					switch (cfg[i].type)
					{
					case CFG_INT:  *cfg[i].value = get_config_int(value, cfg[i].max); break;
					case CFG_BOOL: *cfg[i].value = get_config_bool(value); break;
					case CFG_PAD:  *cfg[i].value = get_config_pad(value); break;
					}
				}
			}
		}

		if (cfg2)
		{
			fseek(fp, 0, SEEK_SET);

			while (1)
			{
				char *name, *value;
				char *p1, *p2, temp[LINEBUF_SIZE];

				memset(linebuf, LINEBUF_SIZE, 0);
				if (fgets(linebuf, LINEBUF_SIZE - 1, fp) == NULL)
					break;

				strcpy(temp, linebuf);

				if (linebuf[0] == ';' || linebuf[0] == '[')
					continue;

				name = strtok(linebuf, " =\r\n");
				if (name == NULL)
					continue;

				value = strtok(NULL, " =\r\n");
				if (value == NULL)
					continue;

				p1 = strchr(temp, '\"');
				if (p1)
				{
					p2 = strchr(p1 + 1, '\"');
					if (p2)
					{
						value = p1 + 1;
						*p2 = '\0';
					}
				}

				/* check name and value */
				for (i = 0; cfg2[i].name; i++)
				{
					if (!strcmp(name, cfg2[i].name))
					{
						if (cfg2[i].type == CFG_STR)
						{
							memset(cfg2[i].value, 0, cfg2[i].max_len);
							strncpy(cfg2[i].value, value, cfg2[i].max_len - 1);
						}
					}
				}
			}
		}

		fclose(fp);

		return 1;
	}

	return 0;
}


/*------------------------------------------------------
	Save settings to .ini file
 -----------------------------------------------------*/

static int save_inifile(const char *path, cfg_type *cfg, cfg2_type *cfg2)
{
	FILE *fp;

	if ((fp = fopen(path, "w")) != NULL)
	{
		int i;

		fprintf(fp, ";-------------------------------------------\r\n");
		fprintf(fp, "; " APPNAME_STR " " VERSION_STR "\r\n");
		fprintf(fp, ";-------------------------------------------\r\n");

		for (i = 0; cfg[i].name; i++)
		{
			switch (cfg[i].type)
			{
			case CFG_NONE: if (cfg[i].name) fprintf(fp, "\r\n%s\r\n", cfg[i].name); break;
			case CFG_INT:  fprintf(fp, "%s = %s\r\n", cfg[i].name, set_config_int(*cfg[i].value, cfg[i].max)); break;
			case CFG_BOOL: fprintf(fp, "%s = %s\r\n", cfg[i].name, set_config_bool(*cfg[i].value)); break;
			case CFG_PAD:  fprintf(fp, "%s = %s\r\n", cfg[i].name, set_config_pad(*cfg[i].value)); break;
			}
		}

		if (cfg2)
		{
			for (i = 0; cfg2[i].name; i++)
			{
				switch (cfg2[i].type)
				{
				case CFG_NONE: if (cfg2[i].name) fprintf(fp, "\r\n%s\r\n", cfg2[i].name); break;
				case CFG_STR:  fprintf(fp, "%s = \"%s\"\r\n", cfg2[i].name, cfg2[i].value); break;
				}
			}
		}

		fclose(fp);

		return 1;
	}

	return 0;
}


/*------------------------------------------------------
	Get boolean value
 -----------------------------------------------------*/

static int get_config_bool(char *str)
{
	if (!stricmp(str, "yes"))
		return 1;
	else
		return 0;
}


/*------------------------------------------------------
	Get integer value
 -----------------------------------------------------*/

static int get_config_int(char *str, int maxval)
{
	int value = atoi(str);

	if (value < 0) value = 0;
	if (value > maxval) value = maxval;
	return value;
}


/*------------------------------------------------------
	Get key config value
 -----------------------------------------------------*/

static int get_config_pad(char *str)
{
	int i;

	for (i = 0; i < PAD_MAX; i++)
	{
		if (strcmp(str, pad_name[i].name) == 0)
			return pad_name[i].code;
	}

	return pad_name[PAD_NONE].code;
}


/*------------------------------------------------------
	Set boolean value
 -----------------------------------------------------*/

static const char *set_config_bool(int value)
{
	if (value)
		return "yes";
	else
		return "no";
}


/*------------------------------------------------------
	Set integer value
 -----------------------------------------------------*/

static char *set_config_int(int value, int maxval)
{
	static char buf[16];

	if (value < 0) value = 0;
	if (value > maxval) value = maxval;

	sprintf(buf, "%d", value);

	return buf;
}


/*------------------------------------------------------
	Set key config value
 -----------------------------------------------------*/

static const char *set_config_pad(int value)
{
	int i;

	for (i = 0; i < PAD_MAX; i++)
	{
		if (value == pad_name[i].code)
			return pad_name[i].name;
	}

	return pad_name[PAD_NONE].name;
}
