/* テスト */
/* 
 * Debug Status: Not checked.
 * - Fixed __T on this page.
 */
/*
 * File: cmd0.c
 * Purpose: Deal with command processing.
 *
 * Copyright (c) 2007 Andrew Sidwell, Ben Harrison
 *
 * 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.
 */
extern "C" 
{
#include "angband.h"
#include "wizard.h"
#include "cmds.h"
#include "ui-menu.h"
#include "game-cmd.h"
}

extern void textui_cmd_throw(void);
extern void textui_cmd_fire(void);
extern void do_cmd_knowledge(void);
extern void msg_print(const _TCHAR *msg);
extern errr cmd_insert(cmd_code c, ...);
extern void message_flush(void);
extern void screen_save(void);
extern void toggle_inven_equip(void);
extern void screen_load(void);
extern bool get_check(const _TCHAR *prompt);
extern void prt(const _TCHAR *str, int row, int col);
extern errr Term_putstr(int x, int y, int n, byte a, const _TCHAR *s);
extern void do_cmd_debug(void);

/*
 * This file contains (several) big lists of commands, so that they can be
 * easily maniuplated for e.g. help displays, or if a port wants to provide a
 * native menu containing a command list.
 *
 * Consider a two-paned layout for the command menus. XXX
 *
 * This file still needs some clearing up. XXX
 */

/*** Big list of commands ***/

/* Useful typedef */
typedef void do_cmd_type(void);

/* Forward declare these, because they're really defined later */
static do_cmd_type do_cmd_wizard, do_cmd_try_debug,
            do_cmd_mouseclick, do_cmd_port,
			do_cmd_xxx_options, do_cmd_menu, do_cmd_monlist, do_cmd_itemlist;

#ifdef ALLOW_BORG
static do_cmd_type do_cmd_try_borg;
#endif

/*
 * Holds a generic command - if cmd is set to other than CMD_NULL  
 * it simply pushes that command to the game, otherwise the hook  
 * function will be called. 
 */
typedef struct
{
	const _TCHAR *desc;
	_TCHAR key;
	cmd_code cmd;
	do_cmd_type *hook;
} command_type;

/* Magic use */
static command_type cmd_magic[] =
{
	{ __T("Gain new spells or prayers"), 'G', CMD_NULL, textui_cmd_study },
	{ __T("Browse a book"),              'b', CMD_NULL, do_cmd_browse },
	{ __T("Cast a spell"),               'm', CMD_NULL, textui_cmd_cast },
	{ __T("Pray a prayer"),              'p', CMD_NULL, textui_cmd_pray }
};

/* General actions */
static command_type cmd_action[] =
{
	{ __T("Search for traps/doors"),     's', CMD_SEARCH, NULL},
	{ __T("Disarm a trap or chest"),     'D', CMD_NULL, textui_cmd_disarm },
	{ __T("Rest for a while"),           'R', CMD_NULL, textui_cmd_rest },
	{ __T("Look around"),                'l', CMD_NULL, do_cmd_look },
	{ __T("Target monster or location"), '*', CMD_NULL, do_cmd_target },
	{ __T("Dig a tunnel"),               'T', CMD_NULL, textui_cmd_tunnel },
	{ __T("Go up staircase"),            '<', CMD_GO_UP, NULL },
	{ __T("Go down staircase"),          '>', CMD_GO_DOWN, NULL },
	{ __T("Toggle search mode"),         'S', CMD_TOGGLE_SEARCH, NULL },
	{ __T("Open a door or a chest"),     'o', CMD_NULL, textui_cmd_open },
	{ __T("Close a door"),               'c', CMD_NULL, textui_cmd_close },
	{ __T("Jam a door shut"),            'j', CMD_NULL, textui_cmd_spike },
	{ __T("Bash a door open"),           'B', CMD_NULL, textui_cmd_bash }
};

/* Item use commands */
static command_type cmd_item_use[] =
{
	{ __T("Read a scroll"),            'r', CMD_NULL, textui_cmd_read_scroll },
	{ __T("Quaff a potion"),           'q', CMD_NULL, textui_cmd_quaff_potion },
	{ __T("Use a staff"),              'u', CMD_NULL, textui_cmd_use_staff },
	{ __T("Aim a wand"),               'a', CMD_NULL, textui_cmd_aim_wand },
	{ __T("Zap a rod"),                'z', CMD_NULL, textui_cmd_zap_rod },
	{ __T("Activate an object"),       'A', CMD_NULL, textui_cmd_activate },
	{ __T("Eat some food"),            'E', CMD_NULL, textui_cmd_eat_food },
	{ __T("Fuel your light source"),   'F', CMD_NULL, textui_cmd_refill },
	{ __T("Fire your missile weapon"), 'f', CMD_NULL, textui_cmd_fire },
	{ __T("Throw an item"),            'v', CMD_NULL, textui_cmd_throw }
};

/* Item management commands */
static command_type cmd_item_manage[]  =
{
	{ __T("Display equipment listing"), 'e', CMD_NULL, do_cmd_equip },
	{ __T("Display inventory listing"), 'i', CMD_NULL, do_cmd_inven },
	{ __T("Pick up objects"),           'g', CMD_PICKUP, NULL },
	{ __T("Wear/wield an item"),        'w', CMD_NULL, textui_cmd_wield },
	{ __T("Take/unwield off an item"),  't', CMD_NULL, textui_cmd_takeoff },
	{ __T("Drop an item"),              'd', CMD_NULL, textui_cmd_drop },
	{ __T("Destroy an item"),           'k', CMD_NULL, textui_cmd_destroy },
	{ __T("Examine an item"),           'I', CMD_NULL, do_cmd_observe },
	{ __T("Inscribe an object"),        '{', CMD_NULL, textui_cmd_inscribe },
	{ __T("Uninscribe an object"),      '}', CMD_NULL, textui_cmd_uninscribe }
};

/* Information access commands */
static command_type cmd_info[] =
{
	{ __T("Full dungeon map"),             'M', CMD_NULL, do_cmd_view_map },
	{ __T("Display visible item list"),    ']', CMD_NULL, do_cmd_itemlist },
	{ __T("Display visible monster list"), '[', CMD_NULL, do_cmd_monlist },
	{ __T("Locate player on map"),         'L', CMD_NULL, do_cmd_locate },
	{ __T("Help"),                         '?', CMD_NULL, do_cmd_help },
	{ __T("Identify symbol"),              '/', CMD_NULL, do_cmd_query_symbol },
	{ __T("Character description"),        'C', CMD_NULL, do_cmd_change_name },
	{ __T("Check knowledge"),              '~', CMD_NULL, do_cmd_knowledge },
	{ __T("Repeat level feeling"),   KTRL('F'), CMD_NULL, do_cmd_feeling },
	{ __T("Show previous message"),  KTRL('O'), CMD_NULL, do_cmd_message_one },
	{ __T("Show previous messages"), KTRL('P'), CMD_NULL, do_cmd_messages }
};

/* Utility/assorted commands */
static command_type cmd_util[] =
{
	{ __T("Interact with options"),      '=', CMD_NULL, do_cmd_xxx_options },
	{ __T("Port-specific preferences"),  '!', CMD_NULL, do_cmd_port },
	{ __T("Save and don't quit"),  KTRL('S'), CMD_SAVE, NULL  },
	{ __T("Save and quit"),        KTRL('X'), CMD_QUIT, NULL  },
	{ __T("Quit (commit suicide)"),      'Q', CMD_NULL, textui_cmd_suicide },
	{ __T("Redraw the screen"),    KTRL('R'), CMD_NULL, do_cmd_redraw },
	{ __T("Load \"screen dump\""),       '(', CMD_NULL, do_cmd_load_screen },
	{ __T("Save \"screen dump\""),       ')', CMD_NULL, do_cmd_save_screen },
	{ __T("Switch langage"),       KTRL('L'), CMD_NULL, do_cmd_switch_lang }
};

/* Commands that shouldn't be shown to the user */ 
static command_type cmd_hidden[] =
{
	{ __T("Take notes"),               ':', CMD_NULL, do_cmd_note },
	{ __T("Version info"),             'V', CMD_NULL, do_cmd_version },
	{ __T("Load a single pref line"),  '"', CMD_NULL, do_cmd_pref },
	{ __T("Mouse click"),           MOUSEY, CMD_NULL, do_cmd_mouseclick },
	{ __T("Enter a store"),            '_', CMD_ENTER_STORE, NULL  },
	{ __T("Toggle windows"),     KTRL('E'), CMD_NULL, toggle_inven_equip }, /* XXX */
	{ __T("Alter a grid"),             '+', CMD_NULL, textui_cmd_alter },
	{ __T("Walk"),                     ';', CMD_NULL, textui_cmd_walk },
	{ __T("Jump into a trap"),         '-', CMD_NULL, textui_cmd_jump },
	{ __T("Start running"),            '.', CMD_NULL, textui_cmd_run },
	{ __T("Stand still"),              ',', CMD_HOLD, NULL },
	{ __T("Check knowledge"),          '|', CMD_NULL, do_cmd_knowledge },
	{ __T("Display menu of actions"), '\n', CMD_NULL, do_cmd_menu },
	{ __T("Display menu of actions"), '\r', CMD_NULL, do_cmd_menu },
	{ __T("Toggle wizard mode"), KTRL('W'), CMD_NULL, do_cmd_wizard },
	{ __T("Repeat previous command"),  KTRL('V'), CMD_REPEAT, NULL },

#ifdef ALLOW_DEBUG
	{ __T("Debug mode commands"), KTRL('A'), CMD_NULL, do_cmd_try_debug },
#endif
#ifdef ALLOW_BORG
	{ __T("Borg commands"),       KTRL('Z'), CMD_NULL, do_cmd_try_borg }
#endif
};

/*
 * A categorised list of all the command lists.
 */
typedef struct
{
	const _TCHAR *name;
	command_type *list;
	size_t len;
} command_list;

static command_list cmds_all[] =
{
	{ __T("Use magic/Pray"),  cmd_magic,       N_ELEMENTS(cmd_magic) },
	{ __T("Action commands"), cmd_action,      N_ELEMENTS(cmd_action) },
	{ __T("Use item"),        cmd_item_use,    N_ELEMENTS(cmd_item_use) },
	{ __T("Manage items"),    cmd_item_manage, N_ELEMENTS(cmd_item_manage) },
	{ __T("Information"),     cmd_info,        N_ELEMENTS(cmd_info) },
	{ __T("Utility"),         cmd_util,        N_ELEMENTS(cmd_util) },
	{ __T("Hidden"),          cmd_hidden,      N_ELEMENTS(cmd_hidden) }
};

/*
 * Determines if a map location is "meaningful".
 * Excludes the impassible border.
 */

bool in_bounds_fully(int y, int x)
{
	int wid, hgt;
	if (p_ptr->depth == 0)
	{
		wid = TOWN_WID;
		hgt = TOWN_HGT;
	}
	else
	{
		wid = DUNGEON_WID;
		hgt = DUNGEON_HGT;
	}
	return ((x > 0) && (y > 0) && (x < wid - 1) && (y < hgt - 1));
}

/*
 * Toggle wizard mode
 */
static void do_cmd_wizard(void)
{
	/* Verify first time */
	if (!(p_ptr->noscore & NOSCORE_WIZARD))
	{
		/* Mention effects */
		msg_print(__T("You are about to enter 'wizard' mode for the very first time!"));
		msg_print(__T("This is a form of cheating, and your game will not be scored!"));
		message_flush();

		/* Verify request */
		if (!get_check(__T("Are you sure you want to enter wizard mode? ")))
			return;

		/* Mark savefile */
		p_ptr->noscore |= NOSCORE_WIZARD;
	}

	/* Toggle mode */
	if (p_ptr->wizard)
	{
		p_ptr->wizard = FALSE;
		msg_print(__T("Wizard mode off."));
	}
	else
	{
		p_ptr->wizard = TRUE;
		msg_print(__T("Wizard mode on."));
	}

	/* Update monsters */
	p_ptr->update |= (PU_MONSTERS);

	/* Redraw "title" */
	p_ptr->redraw |= (PR_TITLE);
}

#ifdef ALLOW_DEBUG

/*
 * Verify use of "debug" mode
 */
static void do_cmd_try_debug(void)
{
	/* Ask first time */
	if (!(p_ptr->noscore & NOSCORE_DEBUG))
	{
		/* Mention effects */
		msg_print(__T("You are about to use the dangerous, unsupported, debug commands!"));
		msg_print(__T("Your machine may crash, and your savefile may become corrupted!"));
		message_flush();

		/* Verify request */
		if (!get_check(__T("Are you sure you want to use the debug commands? ")))
			return;

		/* Mark savefile */
		p_ptr->noscore |= NOSCORE_DEBUG;
	}

	/* Okay */
	do_cmd_debug();
}

#endif /* ALLOW_DEBUG */

#ifdef ALLOW_BORG

/*
 * Verify use of "borg" mode
 */
static void do_cmd_try_borg(void)
{
	/* Ask first time */
	if (!(p_ptr->noscore & NOSCORE_BORG))
	{
		/* Mention effects */
		msg_print(__T("You are about to use the dangerous, unsupported, borg commands!"));
		msg_print(__T("Your machine may crash, and your savefile may become corrupted!"));
		message_flush();

		/* Verify request */
		if (!get_check(__T("Are you sure you want to use the borg commands? ")))
			return;

		/* Mark savefile */
		p_ptr->noscore |= NOSCORE_BORG;
	}

	/* Okay */
	do_cmd_borg();
}

#endif /* ALLOW_BORG */

/*
 * Quit the game.
 */
void do_cmd_quit(cmd_code code, cmd_arg args[])
{
	/* Stop playing */
	p_ptr->playing = FALSE;

	/* Leaving */
	p_ptr->leaving = TRUE;
}

/*
 * Handle a mouseclick, using the horrible hack that is '\xff'.
 */
static void do_cmd_mouseclick(void)
{
	int x, y;

	if (!OPT(mouse_movement)) return;

	y = KEY_GRID_Y(p_ptr->command_cmd_ex);
	x = KEY_GRID_X(p_ptr->command_cmd_ex);

	/* Check for a valid location */
	if (!in_bounds_fully(y, x)) return;

	/* XXX We could try various things here like going up/down stairs */
	if ((p_ptr->py == y) && (p_ptr->px == x) /* && (p_ptr->command_cmd_ex.mousebutton) */)
	{
		textui_cmd_rest();
	}
	else /* if (p_ptr->command_cmd_ex.mousebutton == 1) */
	{
		if (p_ptr->timed[TMD_CONFUSED])
		{
			cmd_insert(CMD_WALK, DIR_UNKNOWN);
		}
		else
		{
			cmd_insert(CMD_PATHFIND, y, x);
		}
	}
	/*
	else if (p_ptr->command_cmd_ex.mousebutton == 2)
	{
		target_set_location(y, x);
		msg_print(__T("Target set.");
	}
	*/
}

/*
 * Port-specific options
 *
 * Should be moved to the options screen. XXX
 */
static void do_cmd_port(void)
{
	(void)Term_user(0);
}

/*
 * Display the options and redraw afterward.
 */
static void do_cmd_xxx_options(void)
{
	do_cmd_options();
	do_cmd_redraw();
}

/*
 * Display the main-screen monster list.
 */
static void do_cmd_monlist(void)
{
	/* Save the screen and display the list */
	screen_save();
	display_monlist();

	/* Wait */
	anykey();

	/* Return */
	screen_load();
}

/*
 * Display the main-screen item list.
 */
static void do_cmd_itemlist(void)
{
	/* Save the screen and display the list */
	screen_save();
	display_itemlist();

	/* Wait */
	anykey();

	/* Return */
	screen_load();
}

/*
 * Invoked when the command isn't recognised.
 */
static void do_cmd_unknown(void)
{
	prt(__T("Type '?' for help."), 0, 0);
}

/* List indexed by char */
struct { 
	do_cmd_type *hook; 
	cmd_code cmd; 
} converted_list[UCHAR_MAX+1];

/*** Menu functions ***/

/* Display an entry on a command menu */
static void cmd_sub_entry(menu_type *menu, int oid, bool cursor, int row, int col, int width)
{
	byte attr = (cursor ? TERM_L_BLUE : TERM_WHITE);
	const command_type *commands = (command_type *)menu->menu_data;

	(void)width;

	/* Write the description */
	Term_putstr(col, row, -1, attr, commands[oid].desc);

	/* Include keypress */
	Term_addch(attr, ' ');
	Term_addch(attr, '(');

	/* KTRL()ing a control character does not alter it at all */
	if (KTRL(commands[oid].key) == commands[oid].key)
	{
		Term_addch(attr, '^');
		Term_addch(attr, UN_KTRL(commands[oid].key));
	}
	else
	{
		Term_addch(attr, commands[oid].key);
	}

	Term_addch(attr, ')');
}

/* Handle user input from a command menu */
static bool cmd_sub_action(_TCHAR cmd, void *db, int oid)
{
	(void)db;
	(void)oid;

	/* Only handle enter */
	if (cmd == '\n' || cmd == '\r')
		return TRUE;
	else
		return FALSE;
}

/*
 * Display a list of commands.
 */
static bool cmd_menu(command_list *list, void *selection_p)
{
	menu_type menu;
	menu_iter commands_menu = { NULL, NULL, cmd_sub_entry, cmd_sub_action };
	region area = { 23, 4, 37, 13 };

	ui_event_data evt;
	int cursor = 0;
	command_type *selection = (command_type *)selection_p;

	/* Set up the menu */
	WIPE(&menu, menu);
	menu.cmd_keys = __T("\x8B\x8C\n\r");
	menu.count = list->len;
	menu.menu_data = list->list;
	menu_init(&menu, MN_SKIN_SCROLL, &commands_menu, &area);

	/* Set up the screen */
	screen_save();
	window_make(21, 3, 62, 17);

	/* Select an entry */
	evt = menu_select(&menu, &cursor, 0);

	/* Load de screen */
	screen_load();

	if (evt.type == EVT_SELECT)
	{
		*selection = list->list[evt.index];
	}

	if (evt.type == EVT_ESCAPE)
	{
		return FALSE;
	}
	else
	{
		return TRUE;
	}
}

static bool cmd_list_action(_TCHAR cmd, void *db, int oid)
{
	if (cmd == '\n' || cmd == '\r' || cmd == MOUSEY) 
	{
		return cmd_menu(&cmds_all[oid], db);
	}
	else
	{
		return FALSE;
	}
}

static void cmd_list_entry(menu_type *menu, int oid, bool cursor, int row, int col, int width)
{
	byte attr = (cursor ? TERM_L_BLUE : TERM_WHITE);
	(void)menu;
	(void)width;
	Term_putstr(col, row, -1, attr, cmds_all[oid].name);
}

/*
 * Display a list of command types, allowing the user to select one.
 */
static void do_cmd_menu(void)
{
	menu_type menu;
	menu_iter commands_menu = { NULL, NULL, cmd_list_entry, cmd_list_action };
	region area = { 21, 5, 37, 6 };

	ui_event_data evt;
	int cursor = 0;
	command_type chosen_command = { NULL };

	/* Set up the menu */
	WIPE(&menu, menu);
	menu.cmd_keys = __T("\x8B\x8C\n\r");
	menu.count = N_ELEMENTS(cmds_all) - 1;
	menu.menu_data = &chosen_command;
	menu_init(&menu, MN_SKIN_SCROLL, &commands_menu, &area);

	/* Set up the screen */
	screen_save();
	window_make(19, 4, 58, 11);

	/* Select an entry */
	evt = menu_select(&menu, &cursor, 0);

	/* Load de screen */
	screen_load();

	/* If a command was chosen, do it. */
	if (chosen_command.cmd != CMD_NULL) 
	{ 
		cmd_insert(chosen_command.cmd); 
	} 
	else if (chosen_command.hook) 
	{
		chosen_command.hook();
	}
}

/*** Exported functions ***/

/*
 * Initialise the command list.
 */
void cmd_init(void)
{
	size_t i, j;

	/* Go through all the lists of commands */
	for (j = 0; j < N_ELEMENTS(cmds_all); j++)
	{
		command_type *commands = cmds_all[j].list;

		/* Fill everything in */
		for (i = 0; i < cmds_all[j].len; i++)
		{
			_TCHAR key = commands[i].key; // key = 65535

			/* Note: at present converted_list is UCHAR_MAX + 1 
			   large, so 'key' is always a valid index. */
			converted_list[key].hook = commands[i].hook;
			converted_list[key].cmd = commands[i].cmd;
		}
	}

	/* Fill in the rest */
	for (i = 0; i < N_ELEMENTS(converted_list); i++)
	{
		switch (i)
		{
			/* Ignore */
			case ESCAPE:
			case ' ':
			case '\a':
			{
				break;
			}

			default:
			{
				if (!converted_list[i].hook && !converted_list[i].cmd)
				{
					converted_list[i].hook = do_cmd_unknown;
					converted_list[i].cmd = CMD_NULL;
				}
			}
		}		
	}
}

/*
 * Parse and execute the current command
 * Give "Warning" on illegal commands.
 */
void textui_process_command(bool no_request)
{
	if (!no_request)
		request_command();

	/* Handle repeating the last command */
	/* repeat_check();*/

	/* Handle resize events XXX */
	if (p_ptr->command_cmd_ex.type == EVT_RESIZE)
	{
		do_cmd_redraw();
	}
	else
	{
#if 0   /* Command key goes up to 256 (including MOUSEY). */
		/* Within these boundaries, the cast to unsigned char will have the desired effect */
		assert(p_ptr->command_cmd >= CHAR_MIN && p_ptr->command_cmd <= CHAR_MAX);
#endif
		/* Execute the command */
		if (converted_list[(_TCHAR) p_ptr->command_cmd].cmd != CMD_NULL)
			cmd_insert(converted_list[(_TCHAR) p_ptr->command_cmd].cmd);
		else if (converted_list[(_TCHAR) p_ptr->command_cmd].hook)
			converted_list[(_TCHAR) p_ptr->command_cmd].hook();
	}
}
