/* vi:set ts=4 sw=4:
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *							This file by Robert Webb
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 */

/*
 * tables.c: functions that use lookup tables for various things, generally to
 * do with special key codes.
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"

/*
 * Some useful tables.
 */

static struct
{
	int		mod_mask;		/* Bit-mask for particular key modifier */
	char_u	name;			/* Single letter name of modifier */
} mod_mask_table[] =
{
	{MOD_MASK_ALT,		(char_u)'M'},
	{MOD_MASK_CTRL,		(char_u)'C'},
	{MOD_MASK_SHIFT,	(char_u)'S'},
	{MOD_MASK_2CLICK,	(char_u)'2'},
	{MOD_MASK_3CLICK,	(char_u)'3'},
	{MOD_MASK_4CLICK,	(char_u)'4'},
	{0x0,				NUL}
};

/*
 * Shifted key terminal codes and their unshifted equivalent.
 * Don't add mouse codes here, they are handled seperately!
 */
static char_u shifted_keys_table[] =
{
/*  shifted     			unshifted  */
	'&', '9',				'@', '1',			/* begin */
	'&', '0',				'@', '2',			/* cancel */
	'*', '1',				'@', '4',			/* command */
	'*', '2',				'@', '5',			/* copy */
	'*', '3',				'@', '6',			/* create */
	'*', '4',				'k', 'D',			/* delete char */
	'*', '5',				'k', 'L',			/* delete line */
	'*', '7',				'@', '7',			/* end */
	'*', '9',				'@', '9',			/* exit */
	'*', '0',				'@', '0',			/* find */
	'#', '1',				'%', '1',			/* help */
	'#', '2',				'k', 'h',			/* home */
	'#', '3',				'k', 'I',			/* insert */
	'#', '4',				'k', 'l',			/* left arrow */
	'%', 'a',				'%', '3',			/* message */
	'%', 'b',				'%', '4',			/* move */
	'%', 'c',				'%', '5',			/* next */
	'%', 'd',				'%', '7',			/* options */
	'%', 'e',				'%', '8',			/* previous */
	'%', 'f',				'%', '9',			/* print */
	'%', 'g',				'%', '0',			/* redo */
	'%', 'h',				'&', '3',			/* replace */
	'%', 'i',				'k', 'r',			/* right arrow */
	'%', 'j',				'&', '5',			/* resume */
	'!', '1',				'&', '6',			/* save */
	'!', '2',				'&', '7',			/* suspend */
	'!', '3',				'&', '8',			/* undo */
	KS_EXTRA, KE_S_UP,		'k', 'u',			/* up arrow */
	KS_EXTRA, KE_S_DOWN,    'k', 'd',			/* down arrow */

	KS_EXTRA, KE_S_F1,      'k', '1',    		/* F1 */
	KS_EXTRA, KE_S_F2,      'k', '2',
	KS_EXTRA, KE_S_F3,      'k', '3',
	KS_EXTRA, KE_S_F4,      'k', '4',
	KS_EXTRA, KE_S_F5,      'k', '5',
	KS_EXTRA, KE_S_F6,      'k', '6',
	KS_EXTRA, KE_S_F7,      'k', '7',
	KS_EXTRA, KE_S_F8,      'k', '8',
	KS_EXTRA, KE_S_F9,      'k', '9',
	KS_EXTRA, KE_S_F10,     'k', ';',			/* F10 */

	KS_EXTRA, KE_S_F11,     'F', '1',
	KS_EXTRA, KE_S_F12,     'F', '2',
	KS_EXTRA, KE_S_F13,     'F', '3',
	KS_EXTRA, KE_S_F14,     'F', '4',
	KS_EXTRA, KE_S_F15,     'F', '5',
	KS_EXTRA, KE_S_F16,     'F', '6',
	KS_EXTRA, KE_S_F17,     'F', '7',
	KS_EXTRA, KE_S_F18,     'F', '8',
	KS_EXTRA, KE_S_F19,     'F', '9',
	KS_EXTRA, KE_S_F20,     'F', 'A',

	KS_EXTRA, KE_S_F21,     'F', 'B',
	KS_EXTRA, KE_S_F22,     'F', 'C',
	KS_EXTRA, KE_S_F23,     'F', 'D',
	KS_EXTRA, KE_S_F24,     'F', 'E',
	KS_EXTRA, KE_S_F25,     'F', 'F',
	KS_EXTRA, KE_S_F26,     'F', 'G',
	KS_EXTRA, KE_S_F27,     'F', 'H',
	KS_EXTRA, KE_S_F28,     'F', 'I',
	KS_EXTRA, KE_S_F29,     'F', 'J',
	KS_EXTRA, KE_S_F30,     'F', 'K',

	KS_EXTRA, KE_S_F31,     'F', 'L',
	KS_EXTRA, KE_S_F32,     'F', 'M',
	KS_EXTRA, KE_S_F33,     'F', 'N',
	KS_EXTRA, KE_S_F34,     'F', 'O',
	KS_EXTRA, KE_S_F35,     'F', 'P',

	KS_EXTRA, KE_S_TAB,     KS_EXTRA, KE_TAB,	/* TAB */
	NUL
};

static struct key_name_entry
{
	int		key;		/* Special key code or ascii value */
	char_u	*name;		/* Name of key */
} key_names_table[] =
{
	{' ',				(char_u *)"Space"},
	{TAB,				(char_u *)"Tab"},
	{K_TAB,				(char_u *)"Tab"},
	{NL,				(char_u *)"NL"},
	{NL,				(char_u *)"NewLine"},	/* Alternative name */
	{NL,				(char_u *)"LineFeed"},	/* Alternative name */
	{NL,				(char_u *)"LF"},		/* Alternative name */
	{CR,				(char_u *)"CR"},
	{CR,				(char_u *)"Return"},	/* Alternative name */
	{ESC,				(char_u *)"Esc"},
	{'|',				(char_u *)"Bar"},
	{K_UP,				(char_u *)"Up"},
	{K_DOWN,			(char_u *)"Down"},
	{K_LEFT,			(char_u *)"Left"},
	{K_RIGHT,			(char_u *)"Right"},

	{K_F1,	 			(char_u *)"F1"},
	{K_F2,	 			(char_u *)"F2"},
	{K_F3,	 			(char_u *)"F3"},
	{K_F4,	 			(char_u *)"F4"},
	{K_F5,	 			(char_u *)"F5"},
	{K_F6,	 			(char_u *)"F6"},
	{K_F7,	 			(char_u *)"F7"},
	{K_F8,	 			(char_u *)"F8"},
	{K_F9,	 			(char_u *)"F9"},
	{K_F10,				(char_u *)"F10"},

	{K_F11,				(char_u *)"F11"},
	{K_F12,				(char_u *)"F12"},
	{K_F13,				(char_u *)"F13"},
	{K_F14,				(char_u *)"F14"},
	{K_F15,				(char_u *)"F15"},
	{K_F16,				(char_u *)"F16"},
	{K_F17,				(char_u *)"F17"},
	{K_F18,				(char_u *)"F18"},
	{K_F19,				(char_u *)"F19"},
	{K_F20,				(char_u *)"F20"},

	{K_F21,				(char_u *)"F21"},
	{K_F22,				(char_u *)"F22"},
	{K_F23,				(char_u *)"F23"},
	{K_F24,				(char_u *)"F24"},
	{K_F25,				(char_u *)"F25"},
	{K_F26,				(char_u *)"F26"},
	{K_F27,				(char_u *)"F27"},
	{K_F28,				(char_u *)"F28"},
	{K_F29,				(char_u *)"F29"},
	{K_F30,				(char_u *)"F30"},

	{K_F31,				(char_u *)"F31"},
	{K_F32,				(char_u *)"F32"},
	{K_F33,				(char_u *)"F33"},
	{K_F34,				(char_u *)"F34"},
	{K_F35,				(char_u *)"F35"},

	{K_HELP,			(char_u *)"Help"},
	{K_UNDO,			(char_u *)"Undo"},
	{K_BS,				(char_u *)"BS"},
	{K_BS,				(char_u *)"BackSpace"},	/* Alternative name */
	{K_INS,				(char_u *)"Insert"},
	{K_INS,				(char_u *)"Ins"},		/* Alternative name */
	{K_DEL,				(char_u *)"Del"},
	{K_DEL,				(char_u *)"Delete"},	/* Alternative name */
	{K_HOME,			(char_u *)"Home"},
	{K_END,				(char_u *)"End"},
	{K_PAGEUP,			(char_u *)"PageUp"},
	{K_PAGEDOWN,		(char_u *)"PageDown"},
	{K_KHOME,			(char_u *)"kHome"},
	{K_KEND,			(char_u *)"kEnd"},
	{K_KPAGEUP,			(char_u *)"kPageUp"},
	{K_KPAGEDOWN,		(char_u *)"kPageDown"},

	{K_MOUSE,			(char_u *)"Mouse"},
	{K_LEFTMOUSE,		(char_u *)"LeftMouse"},
	{K_LEFTDRAG,		(char_u *)"LeftDrag"},
	{K_LEFTRELEASE,		(char_u *)"LeftRelease"},
	{K_MIDDLEMOUSE,		(char_u *)"MiddleMouse"},
	{K_MIDDLEDRAG,		(char_u *)"MiddleDrag"},
	{K_MIDDLERELEASE,	(char_u *)"MiddleRelease"},
	{K_RIGHTMOUSE,		(char_u *)"RightMouse"},
	{K_RIGHTDRAG,		(char_u *)"RightDrag"},
	{K_RIGHTRELEASE,	(char_u *)"RightRelease"},
	{K_ZERO,			(char_u *)"Nul"},
	{0,					NULL}
};

#define KEY_NAMES_TABLE_LEN (sizeof(key_names_table) / sizeof(struct key_name_entry))

#ifdef USE_MOUSE
static struct
{
	int		pseudo_code;		/* Code for pseudo mouse event */
	int		button;				/* Which mouse button is it? */
	int		is_click;			/* Is it a mouse button click event? */
	int		is_drag;			/* Is it a mouse drag event? */
} mouse_table[] =
{
	{KE_LEFTMOUSE,		MOUSE_LEFT,		TRUE,	FALSE},
	{KE_LEFTDRAG,		MOUSE_LEFT,		FALSE,	TRUE},
	{KE_LEFTRELEASE,	MOUSE_LEFT,		FALSE,	FALSE},
	{KE_MIDDLEMOUSE,	MOUSE_MIDDLE,	TRUE,	FALSE},
	{KE_MIDDLEDRAG,		MOUSE_MIDDLE,	FALSE,	TRUE},
	{KE_MIDDLERELEASE,	MOUSE_MIDDLE,	FALSE,	FALSE},
	{KE_RIGHTMOUSE,		MOUSE_RIGHT,	TRUE,	FALSE},
	{KE_RIGHTDRAG,		MOUSE_RIGHT,	FALSE,	TRUE},
	{KE_RIGHTRELEASE,	MOUSE_RIGHT,	FALSE,	FALSE},
	{KE_IGNORE,			MOUSE_RELEASE,	FALSE,	TRUE},	/* DRAG without CLICK */
	{KE_IGNORE,			MOUSE_RELEASE,	FALSE,	FALSE}, /* RELEASE without CLICK */
	{0,					0,				0,		0},
};
#endif /* USE_MOUSE */

/*
 * Return the modifier mask bit (MOD_MASK_*) which corresponds to the given
 * modifier name ('S' for Shift, 'C' for Ctrl etc).
 */
	int
name_to_mod_mask(c)
	int		c;
{
	int		i;

	for (i = 0; mod_mask_table[i].mod_mask; i++)
		if (TO_LOWER(c) == TO_LOWER(mod_mask_table[i].name))
			return mod_mask_table[i].mod_mask;
	return 0x0;
}

/*
 * Decide whether the given key code (K_*) is a shifted special
 * key (by looking at mod_mask).  If it is, then return the appropriate shifted
 * key code, otherwise just return the character as is.
 */
	int
check_shifted_spec_key(c)
	int		c;
{
	int		i;
	int		key0;
	int		key1;

	if (mod_mask & MOD_MASK_SHIFT)
	{
		if (c == TAB)			/* TAB is not in the table, K_TAB is */
			return K_S_TAB;
		key0 = KEY2TERMCAP0(c);
		key1 = KEY2TERMCAP1(c);
		for (i = 0; shifted_keys_table[i] != NUL; i += 4)
			if (key0 == shifted_keys_table[i + 2] &&
											key1 == shifted_keys_table[i + 3])
				return TERMCAP2KEY(shifted_keys_table[i],
												   shifted_keys_table[i + 1]);
	}
	return c;
}

/*
 * Decide whether the given special key is shifted or not.  If it is we
 * return OK and change it to the equivalent unshifted special key code,
 * otherwise we leave it as is and return FAIL.
 */
	int
unshift_special_key(p)
	char_u	*p;
{
	int		i;

	for (i = 0; shifted_keys_table[i]; i += 4)
		if (p[0] == shifted_keys_table[i] && p[1] == shifted_keys_table[i + 1])
		{
			p[0] = shifted_keys_table[i + 2];
			p[1] = shifted_keys_table[i + 3];
			return OK;
		}
	return FAIL;
}

/*
 * Return a string which contains the name of the given key when the given
 * modifiers are down.
 */
	char_u *
get_special_key_name(c, modifiers)
	int		c;
	int		modifiers;
{
	static char_u string[MAX_KEY_NAME_LEN + 1];

	int		i, idx;
	char_u	*s;
	char_u	name[2];

	string[0] = '<';
	idx = 1;

	/* translate shifted keys into unshifted keys and set modifier */
	if (IS_SPECIAL(c))
	{
		name[0] = KEY2TERMCAP0(c);
		name[1] = KEY2TERMCAP1(c);
		if (unshift_special_key(&name[0]))
			modifiers |= MOD_MASK_SHIFT;
		c = TERMCAP2KEY(name[0], name[1]);
	}

	/* translate the modifier into a string */
	for (i = 0; mod_mask_table[i].mod_mask; i++)
		if (modifiers & mod_mask_table[i].mod_mask)
		{
			string[idx++] = mod_mask_table[i].name;
			string[idx++] = (char_u)'-';
		}

	/* try to find the key in the special key table */
	i = find_special_key_in_table(c);
	if (i < 0)			/* unknown special key, output t_xx */
	{
		if (IS_SPECIAL(c))
		{
			string[idx++] = 't';
			string[idx++] = '_';
			string[idx++] = KEY2TERMCAP0(c);
			string[idx++] = KEY2TERMCAP1(c);
		}
		/* Not a special key, only modifiers, output directly */
		else
		{
			if (isprintchar(c))
				string[idx++] = c;
			else
			{
				s = transchar(c);
				while (*s)
					string[idx++] = *s++;
			}
		}
	}
	else				/* use name of special key */
	{
		STRCPY(string + idx, key_names_table[i].name);
		idx = STRLEN(string);
	}
	string[idx++] = '>';
	string[idx] = NUL;
	return string;
}

/*
 * Try to find key "c" in the special key table.
 * Return the index when found, -1 when not found.
 */
	int
find_special_key_in_table(c)
	int		c;
{
	int		i;

	for (i = 0; key_names_table[i].name != NULL; i++)
		if (c == key_names_table[i].key)
			break;
	if (key_names_table[i].name == NULL)
		i = -1;
	return i;
}

/*
 * Find the special key with the given name (the given string does not have to
 * end with NUL, the name is assumed to end before the first non-idchar).
 * If the name starts with "t_" the next two characters are interpreted as a
 * termcap name.
 * Return the key code, or 0 if not found.
 */
	int
get_special_key_code(name)
	char_u	*name;
{
	char_u	*table_name;
	char_u	string[3];
	int		i, j;

	/*
	 * If it's <t_xx> we get the code for xx from the termcap
	 */
	if (name[0] == 't' && name[1] == '_' && name[2] != NUL && name[3] != NUL)
	{
		string[0] = name[2];
		string[1] = name[3];
		string[2] = NUL;
		if (add_termcap_entry(string, FALSE) == OK)
			return TERMCAP2KEY(name[2], name[3]);
	}
	else
		for (i = 0; key_names_table[i].name != NULL; i++)
		{
			table_name = key_names_table[i].name;
			for (j = 0; isidchar(name[j]) && table_name[j] != NUL; j++)
				if (TO_LOWER(table_name[j]) != TO_LOWER(name[j]))
					break;
			if (!isidchar(name[j]) && table_name[j] == NUL)
				return key_names_table[i].key;
		}
	return 0;
}

	char_u *
get_key_name(i)
	int		i;
{
	if (i >= KEY_NAMES_TABLE_LEN)
		return NULL;
	return  key_names_table[i].name;
}

#ifdef USE_MOUSE
/*
 * Look up the given mouse code to return the relevant information in the other
 * arguments.  Return which button is down or was released.
 */
	int
get_mouse_button(code, is_click, is_drag)
	int		code;
	int		*is_click;
	int		*is_drag;
{
	int		i;

	for (i = 0; mouse_table[i].pseudo_code; i++)
		if (code == mouse_table[i].pseudo_code)
		{
			*is_click = mouse_table[i].is_click;
			*is_drag = mouse_table[i].is_drag;
			return mouse_table[i].button;
		}
	return 0;		/* Shouldn't get here */
}

/*
 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
 * the given information about which mouse button is down, and whether the
 * mouse was clicked, dragged or released.
 */
	int
get_pseudo_mouse_code(button, is_click, is_drag)
	int		button;		/* eg MOUSE_LEFT */
	int		is_click;
	int		is_drag;
{
	int		i;

	for (i = 0; mouse_table[i].pseudo_code; i++)
		if (button == mouse_table[i].button
			&& is_click == mouse_table[i].is_click
			&& is_drag == mouse_table[i].is_drag)
		{
			return mouse_table[i].pseudo_code;
		}
	return KE_IGNORE;		/* not recongnized, ignore it */
}
#endif /* USE_MOUSE */
