#ifndef PODULE_API_H
#define PODULE_API_H

#define MAKE_PODULE_API_VERSION(major, minor) (((minor) << 16) | (major))

#define PODULE_API_VERSION_GET_MAJOR(version) ((version) & 0xffff)
#define PODULE_API_VERSION_GET_MINOR(version) ((version) >> 16)

/*v1.1 - add PODULE_FLAGS_NEXT and PODULE_FLAGS_NET.
  v1.0 - initial version.*/
#define PODULE_API_VERSION MAKE_PODULE_API_VERSION(1, 1)

struct podule_t;

typedef enum podule_io_type
{
	/*Access is in IOC space*/
	PODULE_IO_TYPE_IOC = 0,
	/*Access is in MEMC space*/
	PODULE_IO_TYPE_MEMC,
	/*Access is in EASI space (not Archimedes)*/
	PODULE_IO_TYPE_EASI
} podule_io_type;

typedef struct podule_functions_t
{
	/*init() - Initialise podule instance
	  @podule: podule pointer

	  Returns zero on success, non-zero on error.

	  Any instance data should be stored in podule->p*/
	int (*init)(struct podule_t *podule);
	/*close() - Close podule instance
	  @podule: podule pointer

	  This function is responsible for freeing any instance data in podule->p*/
	void (*close)(struct podule_t *podule);

	/*write_b() - Write byte to podule space
	  @podule: podule pointer
	  @type:   type of access (see @podule_io_type)
	  @addr:   address to write to
	  @val:    value to write*/
	void (*write_b)(struct podule_t *podule, podule_io_type type, uint32_t addr, uint8_t val);
	/*write_w() - Write word to podule space
	  @podule: podule pointer
	  @type:   type of access (see @podule_io_type)
	  @addr:   address to write to
	  @val:    value to write*/
	void (*write_w)(struct podule_t *podule, podule_io_type type, uint32_t addr, uint16_t val);
	/*write_l() - Write long word to podule space
	  @podule: podule pointer
	  @type:   type of access (see @podule_io_type)
	  @addr:   address to write to
	  @val:    value to write*/
	void (*write_l)(struct podule_t *podule, podule_io_type type, uint32_t addr, uint32_t val);
	/*read_b() - Read byte from podule space
	  @podule: podule pointer
	  @type:   type of access (see @podule_io_type)
	  @addr:   address to read from

	  Returns value read from podule space*/
	uint8_t  (*read_b)(struct podule_t *podule, podule_io_type type, uint32_t addr);
	/*read_w() - Read word from podule space
	  @podule: podule pointer
	  @type:   type of access (see @podule_io_type)
	  @addr:   address to read from

	  Returns value read from podule space*/
	uint16_t (*read_w)(struct podule_t *podule, podule_io_type type, uint32_t addr);
	/*read_l() - Read long from podule space
	  @podule: podule pointer
	  @type:   type of access (see @podule_io_type)
	  @addr:   address to read from

	  Returns value read from podule space*/
	uint32_t (*read_l)(struct podule_t *podule, podule_io_type type, uint32_t addr);

	/*reset() - Reset podule
	  @podule: podule pointer*/
	void (*reset)(struct podule_t *podule);

	/*run() - Callback from podule timer
	  @podule:       podule pointer
	  @timeslice_us: time expired since last run() call

	  Returns delay (in us) to reload timer with. Non-zero delay can be used
	  to create a free-running timer. Returning zero will stop the timer.

	  When using a free-running timer, @timeslice_us must be used to check
	  how much time has actually passed since the last callback. Emulators
	  should support at least a 1ms timer granularity. Arculator will
	  generally implement a one instruction granularity*/
	int (*run)(struct podule_t *podule, int timeslice_us);
} podule_functions_t;

#define CONFIG_STRING 0
#define CONFIG_INT 1
#define CONFIG_BINARY 2
#define CONFIG_SELECTION 3
#define CONFIG_SELECTION_STRING 4
#define CONFIG_BUTTON 5

/*Control can not be altered by user*/
#define CONFIG_FLAGS_DISABLED (1 << 0)
/*'name' entry should be prefixed with the prefix passed to config_open()*/
#define CONFIG_FLAGS_NAME_PREFIXED (1 << 1)

typedef struct podule_config_selection_t
{
	/*User-visible description of selection*/
	const char *description;
	/*Value this selection represents. Valid when parent control is
	  CONFIG_SELECTION*/
	int value;
	/*Value this selection represents. Valid when parent control is
	  CONFIG_SELECTION_STRING*/
	char *value_string;
} podule_config_selection_t;

typedef struct podule_config_item_t
{
	/*Name of config variable. May be NULL if this is a GUI field that isn't
	  backed by a config variable. May be prefixed if CONFIG_FLAGS_NAME_PREFIXED
	  is set.

	  If this is non-NULL then any change to the item will cause a config
	  update and an emulator reset*/
	const char *name;
	/*User-visible description of item*/
	const char *description;
	/*ID to be used with config_get_current()/config_set_current()*/
	const int id;
	/*Type of control to be presented to the user*/
	const int type;
	/*Combination of CONFIG_FLAGS_* */
	const int flags;

	const char *default_string;
	int default_int;
	podule_config_selection_t *selection;

	/*function() - Callback function for control
	  @window_p: window pointer
	  @item:     item pointer
	  @new_val:  new value (if valid)

	  If this item has a type other than CONFIG_BUTTON then this function
	  indicates that the user has altered the value of this item, and
	  @new_val is valid.

	  If the type is CONFIG_BUTTON, then this function indicates that the
	  user has clicked on the button, and @new_val is NULL.

	  Returns non-zero if this callback has made any changes to other items
	  in this dialogue, and that the dialogue needs to be updated.*/
	int (*function)(void *window_p, const struct podule_config_item_t *item, void *new_data);
} podule_config_item_t;

typedef struct podule_config_t
{
	/*Title of dialogue window*/
	const char *title;
	/*init() - function called on config dialogue open
	  @window_p: window pointer*/
	void (*init)(void *window_p);
	/*close() - function called if user pressed OK
	  @window_p: window pointer

	  Returns non-zero if the emulated machine needs to be reset*/
	int (*close)(void *window_p);

	podule_config_item_t items[];
} podule_config_t;

/*Only one instance of this podule allowed per system*/
#define PODULE_FLAGS_UNIQUE (1 << 0)
/*Podule is 8-bit minipodule (A30x0/A4000)*/
#define PODULE_FLAGS_8BIT (1 << 1)
/*Indicates another podule header is present after this one. Used to support multiple podules in a single library*/
#define PODULE_FLAGS_NEXT (1 << 2)
/*Podule is network interface expansion (A3020/A4000)*/
#define PODULE_FLAGS_NET (1 << 3)

#define PODULE_FLAGS_VALID_VERSION_1_0  (PODULE_FLAGS_UNIQUE | PODULE_FLAGS_8BIT)
#define PODULE_FLAGS_VALID 		(PODULE_FLAGS_UNIQUE | PODULE_FLAGS_8BIT | PODULE_FLAGS_NEXT | PODULE_FLAGS_NET)

typedef struct podule_header_t
{
	/*Podule API version. Should always be set to PODULE_API_VERSION*/
	const uint32_t version;
	/*Podule flags. See PODULE_FLAGS_* */
	const uint32_t flags;
	/*Short internal name, no spaces. Should be <= 15 characters*/
	const char *short_name;
	/*Long user visible name*/
	const char *name;

	const podule_functions_t functions;

	podule_config_t *config;
} podule_header_t;

typedef struct podule_t
{
	/*Pointer to header for this podule*/
	const podule_header_t *header;
	/*Pointer to private instance data for this podule*/
	void *p;
} podule_t;

#define CONFIG_FILESEL_LOAD (0)
#define CONFIG_FILESEL_SAVE (1 << 0)

typedef struct podule_callbacks_t
{
	/*set_irq() - Update IRQ state
	  @podule: podule pointer
	  @state:  new IRQ state*/
	void (*set_irq)(podule_t *podule, int state);
	/*set_fiq() - Update FIQ state
	  @podule: podule pointer
	  @state:  new FIQ state*/
	void (*set_fiq)(podule_t *podule, int state);

	/*set_timer_delay_us() - Set timer delay until podule run() function called
	  @podule:   podule pointer
	  @delay_us: desired delay in us*/
	void (*set_timer_delay_us)(podule_t *podule, int delay_us);
	/*get_timer_remaining_us() - Return time until timer expires and run()
				     is called
	  @podule: podule pointer

	  Returns: Time until timer expires in us, or zero if timer already
	  expired*/
	int (*get_timer_remaining_us)(podule_t *podule);
	/*stop_timer() - Stop timer from running. run() will not be called
	  @podule: podule pointer*/
	void (*stop_timer)(podule_t *podule);

	/*config_get_int() - Read integer variable from podule configuration
	  @podule: podule pointer
	  @name:   name of variable
	  @def:    default value to return if variable does not exist

	  Returns: Value of variable, or @def if variable does not exist*/
	int (*config_get_int)(podule_t *podule, const char *name, int def);
	/*config_get_string() - Read string variable from podule configuration
	  @podule: podule pointer
	  @name:   name of variable
	  @def:    default string to return if variable does not exist

	  Returns: Pointer to variable string, or @def if variable does not exist*/
	const char *(*config_get_string)(podule_t *podule, const char *name, const char *def);
	/*config_set_int() - Write integer variable to podule configuration
	  @podule: podule pointer
	  @name:   name of variable
	  @val:    value to write*/
	void (*config_set_int)(podule_t *podule, const char *name, int val);
	/*config_set_string() - Write string variable to podule configuration
	  @podule: podule pointer
	  @name:   name of variable
	  @val:    Pointer to string to write*/
	void (*config_set_string)(podule_t *podule, const char *name, char *val);

	/*config_open() - Open new configuration dialogue.
	  @window_p: parent window pointer
	  @config:   config to open
	  @prefix:   prefix for any prefixed config items. May be NULL

	  Returns: Non-zero if dialogue closed with OK, zero otherwise

	  Should only be used from a callback from an already open dialogue*/
	int (*config_open)(void *window_p, podule_config_t *config,
			const char *prefix);
	/*config_file_selector() - Open a file selector
	  @window_p:     parent window pointer
	  @title:        window title
	  @default_path: Default path for file. May be NULL
	  @default_fn:   Default filename for file. May be NULL
	  @default_ext:  Default extension for file. May be NULL
	  @wildcard:
	  @dest:         String to hold destination file path/name
	  @dest_len:     Length of @dest, including NULL terminator
	  @flags:        Combination of CONFIG_FILESEL_*

	  Returns: Non-zero if file selected, zero if user dismissed selector
	 */
	int (*config_file_selector)(void *window_p, const char *title,
			const char *default_path, const char *default_fn,
			const char *default_ext, const char *wildcard,
			char *dest, int dest_len, int flags);
	/*config_get_current() - Get the value of a config item in the currently
				 open dialogue
	  @window_p: window pointer
	  @id:       ID of config item

	  Returns: Pointer to value, or NULL if item not found*/
	void *(*config_get_current)(void *window_p, int id);
	/*config_get_current() - Set the value of a config item in the currently
				 open dialogue
	  @window_p: window pointer
	  @id:       ID of config item
	  @val:      Pointer to new value*/
	void (*config_set_current)(void *window_p, int id, void *val);
} podule_callbacks_t;

/*Main entry point to be implemented by the podule. Podule should store
  callbacks and path locally, and return a pointer to the podule header.

  If the podule can't be initialised (eg due to a missing file), this
  function should return NULL. The podule will then be unavailable to
  the user.*/
const podule_header_t *podule_probe(const podule_callbacks_t *callbacks, char *path);

#endif /*PODULE_API_H*/
