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

	config.c

	Application configuration file manager

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

#include "ncdz.h"

#define LINEBUF_SIZE	256


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

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;


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

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

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]", },
	{ CFG_INT,	"RegionSwitch",			&neogeo_region,			0,	2	},
	{ CFG_BOOL,	"EnableLoadScreen",		&neogeo_loadscreen,		0,	1	},

	{ CFG_NONE,	"[Video Settings]", },
	{ CFG_INT,	"RenderMode",			&neogeo_blitter,		0,	3	},
	{ CFG_BOOL,	"WaitVsync",			&neogeo_vsync,			0,	0	},
	{ CFG_BOOL,	"ShowFPS",				&neogeo_showfps,		0,	1	},
	{ CFG_BOOL,	"60FPSLimit",			&neogeo_speedlimit,		1,	1	},
	{ CFG_BOOL,	"AutoFrameSkip",		&neogeo_autoframeskip,	0,	1	},
	{ CFG_INT,	"AutoFrameSkipLimit",	&neogeo_autoframeskip_limit,	8,	10	},
	{ CFG_INT,	"FrameSkipLevel",		&neogeo_frameskip,		0,	11	},
	{ CFG_INT,	"CacheClearTimeing",	&neogeo_cacheclear,		3,	6	},
	{ CFG_INT,	"PaletteRefresh",		&neogeo_paletterefresh,	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,	"[Menu Settings]", },
	{ CFG_INT,	"BGImageBrightness",	&bg_brightness,			100,100	},
	{ CFG_INT,	"LogoPaletteRed",		&logo_pal.r,			31,	31	},
	{ CFG_INT,	"LogoPaletteGreen",		&logo_pal.g,			31,	31	},
	{ CFG_INT,	"LogoPaletteBlue",		&logo_pal.b,			31,	31	},
	{ CFG_INT,	"TitlePaletteRed",		&title_pal.r,			25,	31	},
	{ CFG_INT,	"TitlePaletteGreen",	&title_pal.g,			25,	31	},
	{ CFG_INT,	"TitlePaletteBlue",		&title_pal.b,			31,	31	},
	{ CFG_INT,	"InfobarPaletteRed",	&infobar_pal.r,			28,	31	},
	{ CFG_INT,	"InfobarPaletteGreen",	&infobar_pal.g,			28,	31	},
	{ CFG_INT,	"InfobarPaletteBlue",	&infobar_pal.b,			28,	31	},
	{ CFG_INT,	"SelectPaletteRed",		&select_pal.r,			31,	31	},
	{ CFG_INT,	"SelectPaletteGreen",	&select_pal.g,			31,	31	},
	{ CFG_INT,	"SelectPaletteBlue",	&select_pal.b,			31,	31	},
	{ CFG_INT,	"NormalPaletteRed",		&normal_pal.r,			25,	31	},
	{ CFG_INT,	"NormalPaletteGreen",	&normal_pal.g,			25,	31	},
	{ CFG_INT,	"NormalPaletteBlue",	&normal_pal.b,			25,	31	},
	{ CFG_INT,	"BGColor1PaletteRed",	&bg1_pal.r,				8,	31	},
	{ CFG_INT,	"BGColor1PaletteGreen",	&bg1_pal.g,				8,	31	},
	{ CFG_INT,	"BGColor1PaletteBlue",	&bg1_pal.b,				8,	31	},
	{ CFG_INT,	"BGColor2PaletteRed",	&bg2_pal.r,				15,	31	},
	{ CFG_INT,	"BGColor2PaletteGreen",	&bg2_pal.g,				15,	31	},
	{ CFG_INT,	"BGColor2PaletteBlue",	&bg2_pal.b,				15,	31	},
	{ CFG_INT,	"BGColor3PaletteRed",	&bg3_pal.r,				12,	31	},
	{ CFG_INT,	"BGColor3PaletteGreen",	&bg3_pal.g,				12,	31	},
	{ CFG_INT,	"BGColor3PaletteBlue",	&bg3_pal.b,				12,	31	},
	{ CFG_INT,	"FileSel1PaletteRed",	&filesel1_pal.r,		0,	31	},
	{ CFG_INT,	"FileSel1PaletteGreen",	&filesel1_pal.g,		0,	31	},
	{ CFG_INT,	"FileSel1PaletteBlue",	&filesel1_pal.b,		15,	31	},
	{ CFG_INT,	"FileSel2PaletteRed",	&filesel2_pal.r,		0,	31	},
	{ CFG_INT,	"FileSel2PaletteGreen",	&filesel2_pal.g,		0,	31	},
	{ CFG_INT,	"FileSel2PaletteBlue",	&filesel2_pal.b,		0,	31	},

	{ 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 /*PSP_CTRL_RTRIGGER*/,	0	},
	{ CFG_PAD,	"SwitchPlayer",	&input_map[27],	0,	0	},

	{ 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) == 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);
}


/*------------------------------------------------------
	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) == 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);
}


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

static int load_inifile(const char *path, cfg_type *cfg)
{
	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;
					}
				}
			}
		}

		fclose(fp);

		return 1;
	}

	return 0;
}


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

static int save_inifile(const char *path, cfg_type *cfg)
{
	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;
			}
		}

		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;
}
