

#ifndef __OPTIONS_H__
#define __OPTIONS_H__

#include "osdcore.h"
#include "corefile.h"
#include "tagmap.h"



//**************************************************************************
//  CONSTANTS
//**************************************************************************

// option types
const UINT32 OPTION_TYPE_MASK		= 0x0007;		// up to 8 different types
enum
{
	OPTION_INVALID,			// invalid
	OPTION_HEADER,			// a header item
	OPTION_COMMAND,			// a command
	OPTION_BOOLEAN,			// boolean option
	OPTION_INTEGER,			// integer option
	OPTION_FLOAT,			// floating-point option
	OPTION_STRING			// string option
};

// option priorities
const int OPTION_PRIORITY_DEFAULT	= 0;			// defaults are at 0 priority
const int OPTION_PRIORITY_LOW		= 50;			// low priority
const int OPTION_PRIORITY_NORMAL	= 100;			// normal priority
const int OPTION_PRIORITY_HIGH		= 150;			// high priority
const int OPTION_PRIORITY_MAXIMUM	= 255;			// maximum priority



//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

// static structure describing a single option with its description and default value
struct options_entry
{
	const char *		name;				// name on the command line
	const char *		defvalue;			// default value of this argument
	UINT32				flags;				// flags to describe the option
	const char *		description;		// description for -showusage
};


// structure holding information about a collection of options
class core_options
{
	static const int MAX_UNADORNED_OPTIONS = 16;

public:
	// information about a single entry in the options
	class entry
	{
		friend class core_options;

		// construction/destruction
		entry(const options_entry &entry);

	public:
		// getters
		entry *next() const { return m_next; }
		const char *name() const { return m_name[0] ? m_name[0].cstr() : NULL; }
		const char *description() const { return m_description; }
		const char *value() const { return m_data; }
		const char *default_value() const { return m_defdata; }
		const char *minimum() const { return m_minimum; }
		const char *maximum() const { return m_maximum; }
		UINT32 seqid() const { return m_seqid; }
		int type() const { return (m_flags & OPTION_TYPE_MASK); }
		UINT32 flags() const { return m_flags; }
		bool is_header() const { return type() == OPTION_HEADER; }
		bool is_command() const { return type() == OPTION_COMMAND; }
		bool has_range() const { return (m_minimum && m_maximum); }

		// setters
		void set_value(const char *newvalue, int priority);
		void set_default_value(const char *defvalue);
		void revert(int priority);

	private:
		// internal state
		entry *					m_next;				// link to the next data
		UINT32					m_flags;			// flags from the entry
		UINT32					m_seqid;			// sequence ID; bumped on each change
		bool					m_error_reported;	// have we reported an error on this option yet?
		int						m_priority;			// priority of the data set
		const char *			m_description;		// description for this item
		astring					m_name[4];			// up to 4 names for the item
		astring					m_data;				// data for this item
		astring					m_defdata;			// default data for this item
		astring					m_minimum;			// minimum value
		astring					m_maximum;			// maximum value
	};

	// construction/destruction
	core_options();
	core_options(const options_entry *entrylist);
	core_options(const options_entry *entrylist1, const options_entry *entrylist2);
	core_options(const options_entry *entrylist1, const options_entry *entrylist2, const options_entry *entrylist3);
	core_options(const core_options &src);
	virtual ~core_options();

	// operators
	core_options &operator=(const core_options &rhs);
	bool operator==(const core_options &rhs);
	bool operator!=(const core_options &rhs);

	// getters
	entry *first() const { return m_entrylist; }
	const char *command() const { return m_command; }

	// configuration
	void add_entries(const options_entry *entrylist, bool override_existing = false);
	void set_default_value(const char *name, const char *defvalue);
	void remove_entry(entry &delentry);

	// parsing/input
	bool parse_command_line(int argc, char **argv, int priority, astring &error_string);
	bool parse_ini_file(core_file &inifile, int priority, int ignore_priority, astring &error_string);

	// reverting
	void revert(int priority = OPTION_PRIORITY_MAXIMUM);

	// output
	const char *output_ini(astring &buffer, const core_options *diff = NULL);
	const char *output_help(astring &buffer);

	// reading
	const char *value(const char *option) const;
	bool bool_value(const char *name) const { return (atoi(value(name)) != 0); }
	int int_value(const char *name) const { return atoi(value(name)); }
	float float_value(const char *name) const { return atof(value(name)); }
	UINT32 seqid(const char *name) const;
	bool exists(const char *name) const;

	// setting
	void set_command(const char *command);
	bool set_value(const char *name, const char *value, int priority, astring &error_string);
	bool set_value(const char *name, int value, int priority, astring &error_string);
	bool set_value(const char *name, float value, int priority, astring &error_string);

	// misc
	static const char *unadorned(int x = 0) { return s_option_unadorned[MIN(x, MAX_UNADORNED_OPTIONS)]; }

private:
	// internal helpers
	void reset();
	void append_entry(entry &newentry);
	void copyfrom(const core_options &src);
	bool validate_and_set_data(entry &curentry, const char *newdata, int priority, astring &error_string);

	// internal state
	entry *					m_entrylist;			// head of list of entries
	entry **				m_entrylist_tailptr;	// pointer to tail of entry list
	tagmap_t<entry *>		m_entrymap;				// map for fast lookup
	astring					m_command;				// command found
	static const char *const s_option_unadorned[];	// array of unadorned option "names"
};



#endif /* __OPTIONS_H__ */
