/**
 * \file ui-menu.h
 * \brief Generic menu interaction functions
 *
 * Copyright (c) 2007 Pete Mack
 * Copyright (c) 2010 Andi Sidwell
 *
 * This work is free software; you can redistribute it and/or modify it
 * under the terms of either:
 *
 * a) the GNU General Public License as published by the Free Software
 *    Foundation, version 2, or
 *
 * b) the "Angband licence":
 *    This software may be copied and distributed for educational, research,
 *    and not for profit purposes provided that this copyright and statement
 *    are included in all such copies.  Other copyrights may also apply.
 */

#ifndef INCLUDED_UI_MENU_H
#define INCLUDED_UI_MENU_H

#include "ui-output.h"

/*** Constants ***/

/* Colors for interactive menus */
enum
{
	CURS_UNKNOWN = 0,		/* Use gray / dark blue for cursor */
	CURS_KNOWN = 1			/* Use white / light blue for cursor */
};

/**
 * Type wrapper for various row styles.
 */
typedef enum _menu_row_style_t {
	MN_ROW_STYLE_DISABLED = CURS_UNKNOWN,
	MN_ROW_STYLE_ENABLED = CURS_KNOWN,
} menu_row_style_t;

/**
 * Type wrapper for row validity.
 */
typedef enum _menu_row_validity_t {
	MN_ROW_INVALID = 0,
	MN_ROW_VALID = 1,
	MN_ROW_HIDDEN = 2,
} menu_row_validity_t;

/* Cursor colours for different states */
extern const uint8_t curs_attrs[2][2];

/* Standard menu orderings */
extern const char lower_case[];			/* abc..z */
extern const char upper_case[];			/* ABC..Z */
extern const char all_letters[];		/* abc..zABC..Z */
extern const char all_letters_nohjkl[];		/* abc..gim..zABC..Z */


/*
  Together, these classes define the constant properties of
  the various menu classes.

  A menu consists of:
   - menu_iter, which describes how to handle the type of "list" that's 
     being displayed as a menu
   - a menu_skin, which describes the layout of the menu on the screen.
   - various bits and bobs of other data (e.g. the actual list of entries)
 */
struct menu;



/*** Predefined menu "skins" ***/

/**
 * Types of predefined skins available.
 */
typedef enum
{
	/*
	 * A simple list of actions with an associated name and id.
	 * Private data: an array of menu_action
	 */
	MN_ITER_ACTIONS = 1,

	/*
	 * A list of strings to be selected from - no associated actions.
	 * Private data: an array of const char *
	 */
	MN_ITER_STRINGS = 2
} menu_iter_id;


/**
 * Primitive menu item with bound action.
 */
typedef struct
{
	int flags;
	char tag;
	const char *name;
	void (*action)(const char *title, int row);
} menu_action;


/**
 * Flags for menu_actions.
 */
#define MN_ACT_GRAYED     0x0001 /* Allows selection but no action */
#define MN_ACT_HIDDEN     0x0002 /* Row is hidden, but may be selected via tag */


/**
 * Underlying function set for displaying lists in a certain kind of way.
 */
typedef struct
{
	/* Returns menu item tag (optional) */
	char (*get_tag)(struct menu *menu, int oid);

	/*
	 * Validity checker (optional--all rows are assumed valid if not present)
 	 * Return values will be interpreted as: 0 = no, 1 = yes, 2 = hide.
 	 */
	int (*valid_row)(struct menu *menu, int oid);

	/* Displays a menu row */
	void (*display_row)(struct menu *menu, int oid, bool cursor,
			int row, int col, int width);

	/* Handle 'positive' events (selections or cmd_keys) */
	/* XXX split out into a select handler and a cmd_key handler */
	bool (*row_handler)(struct menu *menu, const ui_event *event, int oid);

	/* Called when the screen resizes */
	void (*resize)(struct menu *m);
} menu_iter;



/*** Menu skins ***/

/**
 * Identifiers for the kind of layout to use
 */
typedef enum
{
	MN_SKIN_SCROLL = 1,   /**< Ordinary scrollable single-column list */
	MN_SKIN_OBJECT = 2,   /**< Special single-column list for object choice */
	MN_SKIN_COLUMNS = 3   /**< Multicolumn view */
} skin_id;


/* Class functions for menu layout */
typedef struct
{
	/* Determines the cursor index given a (mouse) location */
	int (*get_cursor)(int row, int col, int n, int top, region *loc);

	/* Displays the current list of visible menu items */
	void (*display_list)(struct menu *menu, int cursor, int *top, region *);

	/* Specifies the relative menu item given the state of the menu */
	char (*get_tag)(struct menu *menu, int pos);

	/* Process a direction */
	ui_event (*process_dir)(struct menu *menu, int dir);
} menu_skin;



/*** Base menu structure ***/

/**
 * Flags for menu appearance & behaviour
 */
enum
{
	/* Tags are associated with the view, not the element */
	MN_REL_TAGS = 0x01,

	/* No tags -- movement key and mouse browsing only */
	MN_NO_TAGS = 0x02,

	/* Tags to be generated by the display function */
	MN_PVT_TAGS = 0x04,

	/* Tag selections can be made regardless of the case of the key pressed. 
	 * i.e. 'a' activates the line tagged 'A'. */
	MN_CASELESS_TAGS = 0x08,

	/* double tap (or keypress) for selection; single tap is cursor movement */
	MN_DBL_TAP = 0x10,

	/* no select events to be triggered */
	MN_NO_ACTION = 0x20,

	/* Tags can be selected via an inscription */
	MN_INSCRIP_TAGS = 0x40,

	/* Allow a keymap trigger whose first character in the action is ESCAPE
	 * to break out of the menu; note that selections, cmd_keys, and
	 * switch_keys take precedence so a keymap trigger that is also one
	 * of those will not break out */
	MN_KEYMAP_ESC = 0x80
};


/* Base menu type */
struct menu
{
	/** Public variables **/
	const char *header;
	const char *title;
	const char *prompt;

	/* Keyboard shortcuts for menu selection-- shouldn't overlap cmd_keys */
	const char *selections; 

	/* Menu selections corresponding to inscriptions */
	char *inscriptions; 

	/* String of characters that when pressed, menu handler should be called */
	/* Mustn't overlap with 'selections' or some items may be unselectable */
	const char *cmd_keys;

	/* String of characters that when pressed, return an EVT_SWITCH */
	/* Mustn't overlap with previous blah blah */
	const char *switch_keys;

  	/* auxiliary browser help function */
	void (*browse_hook)(int oid, void *db, const region *loc);

	/* This is an auxiliary function to allow predefined skins to handle
	 * events for cmd_keys or switch_keys within menu_select() rather than
	 * have menu_select()'s caller handle them.  If used, the function
	 * should return true if the event, ev, was handled or false if it was
	 * not. oid is the index of the cursor position in the list. */
	bool (*keys_hook)(struct menu *m, const ui_event *ev, int oid);

	/* This is an auxiliary function to allow presentation of a context
	 * menu in response to a mouse click.  If used, the function should
	 * return true if the event, in, was handled or false if it was not.
	 * *out has the details for the event that will be passed upstream and
	 * is initialized prior to the call to the auxiliary function.  If the
	 * input event was handled, the function way want to modify *out.
	 * This function is called after screening out any mouse events that
	 * are handled natively by menu_select(). */
	bool (*context_hook)(struct menu *m, const ui_event *in, ui_event *out);

	/* Flags specifying the behavior of this menu (from struct menu_flags) */
	int flags;


	/** Private variables **/

	/* Stored boundary, set by menu_layout().  This is used to calculate
	 * where the menu should be displayed on display & resize */
	region boundary;

	int filter_count;        /* number of rows in current view */
	const int *filter_list;  /* optional filter (view) of menu objects */

	int count;               /* number of rows in underlying data set */
	void *menu_data;         /* the data used to access rows. */

	const menu_skin *skin;      /* menu display style functions */
	const menu_iter *row_funcs; /* menu skin functions */

	/* State variables */
	int cursor;             /* Currently selected row */
	int top;                /* Position in list for partial display */
	region active;          /* Subregion actually active for selection */
	int cursor_x_offset;    /* Adjustment to the default position of the cursor on a line. */
};



/*** Menu API ***/

/**
 * Allocate and return a new, initialised, menu.
 */
struct menu *menu_new(skin_id id, const menu_iter *iter);
struct menu *menu_new_action(menu_action *acts, size_t n);
void menu_free(struct menu *m);


/**
 * Initialise a menu, using the skin and iter functions specified.
 */
void menu_init(struct menu *menu, skin_id id, const menu_iter *iter);


/**
 * Given a predefined menu kind, return its iter functions.
 */
const menu_iter *menu_find_iter(menu_iter_id iter_id);


/**
 * Set menu private data and the number of menu items.
 *
 * Menu private data is then available from inside menu callbacks using
 * menu_priv().
 */
void menu_setpriv(struct menu *menu, int count, void *data);


/**
 * Return menu private data, set with menu_setpriv().
 */
void *menu_priv(struct menu *menu);


/*
 * Set a filter on what items a menu can display.
 *
 * Use this if your menu private data has 100 items, but you want to choose
 * which ones of those to display at any given time, e.g. in an inventory menu.
 * object_list[] should be an array of indexes to display, and n should be its
 * length.
 */
void menu_set_filter(struct menu *menu, const int object_list[], int n);


/**
 * Remove any filters set on a menu by menu_set_filer().
 */
void menu_release_filter(struct menu *menu);


/**
 * Ready a menu for display in the region specified.
 *
 * XXX not ready for dynamic resizing just yet
 */
bool menu_layout(struct menu *menu, const region *loc);


/**
 * Display a menu.
 * If reset_screen is true, it will reset the screen to the previously saved
 * state before displaying.
 */
void menu_refresh(struct menu *menu, bool reset_screen);


/**
 * Run a menu.
 *
 * 'notify' is a bitwise OR of ui_event_type events that you want to
 * menu_select to return to you if they're not handled inside the menu loop.
 * e.g. if you want to handle key events without specifying a menu_iter->handle
 * function, you can set notify to EVT_KBRD, and any non-navigation keyboard
 * events will stop the menu loop and return them to you.
 *
 * Some events are returned by default, and else are EVT_ESCAPE and EVT_SELECT.
 * 
 * Event types that can be returned:
 *   EVT_ESCAPE: no selection; go back (by default)
 *   EVT_SELECT: menu->cursor is the selected menu item (by default)
 *   EVT_MOVE:   the cursor has moved
 *   EVT_KBRD:   unhandled keyboard events
 *   EVT_MOUSE:  unhandled mouse events  
 *   EVT_RESIZE: resize events
 * 
 * XXX remove 'notify'
 *
 * If popup is true, the screen background is saved before starting the menu,
 * and restored before each redraw. This allows variably-sized information
 * at the bottom of the menu.
 */
ui_event menu_select(struct menu *menu, int notify, bool popup);

/**
 * Set the menu cursor to the next valid row.
 */
void menu_ensure_cursor_valid(struct menu *m);


/* Interal menu stuff that cmd-know needs because it's quite horrible */
bool menu_handle_mouse(struct menu *menu, const ui_event *in, ui_event *out);
bool menu_handle_keypress(struct menu *menu, const ui_event *in, ui_event *out);

/**
 * Allow adjustment of the cursor's default x offset.
 */
void menu_set_cursor_x_offset(struct menu *m, int offset);

/*** Dynamic menu handling ***/

struct menu *menu_dynamic_new(void);
void menu_dynamic_add(struct menu *m, const char *text, int value);
void menu_dynamic_add_valid(struct menu *m, const char *text, int value, menu_row_validity_t valid);
void menu_dynamic_add_label(struct menu *m, const char *text, const char label, int value, char *label_list);
void menu_dynamic_add_label_valid(struct menu *m, const char *text, const char label, int value, char *label_list, menu_row_validity_t valid);
size_t menu_dynamic_longest_entry(struct menu *m);
void menu_dynamic_calc_location(struct menu *m, int mx, int my);
int menu_dynamic_select(struct menu *m);
void menu_dynamic_free(struct menu *m);

#endif /* INCLUDED_UI_MENU_H */
