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

	messopts.c - MESS specific option handling

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

#include "mame.h"
#include "options.h"
#include "driver.h"
#include "pool.h"
#include "config.h"
#include "xmlfile.h"
#include "messopts.h"
#include "osdmess.h"

#define OPTION_ADDED_DEVICE_OPTIONS	"added_device_options"


/***************************************************************************
    LOCAL VARIABLES
***************************************************************************/

const options_entry mess_core_options[] =
{
	{ NULL,							NULL,   OPTION_HEADER,						"MESS SPECIFIC OPTIONS" },
    { "ramsize;ram",				NULL,	0,									"size of RAM (if supported by driver)" },
	{ "writeconfig;wc",				"0",	OPTION_BOOLEAN,						"writes configuration to (driver).ini on exit" },
	{ OPTION_SKIP_WARNINGS,			"0",    OPTION_BOOLEAN,						"skip displaying the warnings screen" },
	{ OPTION_ADDED_DEVICE_OPTIONS,	"0",	OPTION_BOOLEAN | OPTION_INTERNAL,	"device-specific options have been added" },
	{ NULL }
};



/***************************************************************************
    IMPLEMENTATION
***************************************************************************/

/*-------------------------------------------------
    mess_get_device_option - accesses a device
	option, by device and index
-------------------------------------------------*/

const char *mess_get_device_option(const device_class *devclass, int device_index)
{
	const char *result = NULL;
	const char *dev_name;

	if (options_get_bool(mame_options(), OPTION_ADDED_DEVICE_OPTIONS))
	{
		/* identify the option name */
		dev_name = device_instancename(devclass, device_index);

		/* access the option */
		result = options_get_string(mame_options(), dev_name);
	}
	return result;
}



/*-------------------------------------------------
    mess_enumerate_devices - runs a specified proc
	for all devices on a driver
-------------------------------------------------*/

static void mess_enumerate_devices(core_options *opts, const game_driver *gamedrv,
	void (*proc)(core_options *opts, const game_driver *gamedrv, const device_class *devclass, int device_index, int global_index))
{
	struct SystemConfigurationParamBlock cfg;
	device_getinfo_handler handlers[64];
	int count_overrides[ARRAY_LENGTH(handlers)];
	int i, j, count, index = 0;
	device_class devclass;

	/* retrieve getinfo handlers */
	memset(&cfg, 0, sizeof(cfg));
	memset(handlers, 0, sizeof(handlers));
	cfg.device_slotcount = sizeof(handlers) / sizeof(handlers[0]);
	cfg.device_handlers = handlers;
	cfg.device_countoverrides = count_overrides;
	if (gamedrv->sysconfig_ctor)
		gamedrv->sysconfig_ctor(&cfg);

	/* run this on all devices */
	for (i = 0; handlers[i]; i++)
	{
		/* create the device class */
		memset(&devclass, 0, sizeof(devclass));
		devclass.gamedrv = gamedrv;
		devclass.get_info = handlers[i];

		/* how many of this device exist? */
		count = (int) device_get_info_int(&devclass, DEVINFO_INT_COUNT);

		/* loop on each device instance */
		for (j = 0; j < count; j++)
		{
			proc(opts, gamedrv, &devclass, j, index++);
		}
	}
}



/*-------------------------------------------------
    add_device_options_for_device - adds options
	for this device on a driver
-------------------------------------------------*/

static void add_device_options_for_device(core_options *opts, const game_driver *gamedrv,
	const device_class *devclass, int device_index, int global_index)
{
	options_entry this_entry[2];
	const char *dev_name;
	const char *dev_short_name;
	char dev_full_name[128];

	/* first device? add the header as to be pretty */
	if (global_index == 0)
	{
		memset(this_entry, 0, sizeof(this_entry));
		this_entry[0].description = "MESS DEVICES";
		this_entry[0].flags = OPTION_HEADER;
		options_add_entries(opts, this_entry);
	}

	/* retrieve info about the device instance */
	dev_name = device_instancename(devclass, device_index);
	dev_short_name = device_briefinstancename(devclass, device_index);
	snprintf(dev_full_name, sizeof(dev_full_name) / sizeof(dev_full_name[0]),
		"%s;%s", dev_name, dev_short_name);

	/* add the option */
	memset(this_entry, 0, sizeof(this_entry));
	this_entry[0].name = dev_full_name;
	options_add_entries(opts, this_entry);
}



/*-------------------------------------------------
    mess_add_device_options - add all of the device
	options for a specified device
-------------------------------------------------*/

void mess_add_device_options(core_options *opts, const game_driver *driver)
{
	/* enumerate our callback for every device */
	mess_enumerate_devices(opts, driver, add_device_options_for_device);

	/* record that we've added device options */
	options_set_bool(opts, OPTION_ADDED_DEVICE_OPTIONS, TRUE, OPTION_PRIORITY_CMDLINE);
}



/*-------------------------------------------------
    mess_driver_name_callback - called when we
	parse the driver name, so we can add options
	specific to that driver
-------------------------------------------------*/

static void mess_driver_name_callback(core_options *opts, const char *arg)
{
	const game_driver *driver;

	/* only add these options if we have not yet added them */
	if (!options_get_bool(opts, OPTION_ADDED_DEVICE_OPTIONS))
	{
		driver = driver_get_name(arg);
		if (driver != NULL)
		{
			mess_add_device_options(opts, driver);
		}
	}
}



/*-------------------------------------------------
    mess_options_init - called from core to add
	MESS specific options
-------------------------------------------------*/

void mess_options_init(core_options *opts)
{
	/* add MESS-specific options */
	options_add_entries(opts, mess_core_options);

	/* add OSD-MESS specific options (hack!) */
	osd_mess_options_init(opts);

	/* we need to dynamically add options when the device name is parsed */
	options_set_option_callback(opts, OPTION_GAMENAME, mess_driver_name_callback);
}



/*-------------------------------------------------
    extract_device_options_for_device - extracts
	options for this device on a driver
-------------------------------------------------*/

static void extract_device_options_for_device(core_options *opts, const game_driver *gamedrv,
	const device_class *devclass, int device_index, int global_index)
{
	mess_image *image;
	iodevice_t dev_type;
	const char *dev_tag;
	const char *dev_name;
	const char *filename;

	assert_always(options_get_bool(opts, OPTION_ADDED_DEVICE_OPTIONS), "extract_device_options_for_device() called without device options");

	/* identify the correct image */
	dev_tag = device_get_info_string(devclass, DEVINFO_STR_DEV_TAG);
	if (dev_tag != NULL)
	{
		image = image_from_devtag_and_index(dev_tag, device_index);
	}
	else
	{
		dev_type = (iodevice_t) (int) device_get_info_int(devclass, DEVINFO_INT_TYPE);
		image = image_from_devtype_and_index(dev_type, device_index);
	}

	/* access the filename */
	filename = image_filename(image);

	/* identify the option name */
	dev_name = device_instancename(devclass, device_index);

	/* and set the option */
	options_set_string(opts, dev_name, filename ? filename : "", OPTION_PRIORITY_CMDLINE);
}



/*-------------------------------------------------
    write_config - emit current option statuses as
	INI files
-------------------------------------------------*/

static int write_config(const char *filename, const game_driver *gamedrv)
{
	file_error filerr;
	mame_file *f;
	char buffer[128];
	int retval = 1;

	if (gamedrv)
	{
		sprintf(buffer, "%s.ini", gamedrv->name);
		filename = buffer;
	}

	filerr = mame_fopen(SEARCHPATH_INI, buffer, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &f);
	if (filerr != FILERR_NONE)
		goto done;

	options_output_ini_file(mame_options(), mame_core_file(f));
	retval = 0;

done:
	if (f)
		mame_fclose(f);
	return retval;
}



/*-------------------------------------------------
    mess_options_extract - extract device options
	out of core into the options
-------------------------------------------------*/

void mess_options_extract(void)
{
	/* only extract the device options if we've added them */
	if (options_get_bool(mame_options(), OPTION_ADDED_DEVICE_OPTIONS))
		mess_enumerate_devices(mame_options(), Machine->gamedrv, extract_device_options_for_device);

	/* write the config, if appropriate */
	if (options_get_bool(mame_options(), OPTION_WRITECONFIG))
		write_config(NULL, Machine->gamedrv);
}
