#include <../source/TAL/objbase.h>

/*
 * 03.07.2004
 
 NEEDS COMPLETE REWRITE
 
 */

#include <../arch/arch.h>

input_state_p	V_CALL
inputState(void)
{
	return &GetThreadData()->input_state;
}

/*
 *	Returns name associated with given keyboard code.
 */
char_p	V_GLOBAL
keycodeGetName(keycode_t code)
{
	switch(code)
	{
		case key_escape:	return "Escape";
		case key_1:			return "1";
		case key_2:			return "2";
		case key_3:			return "3";
		case key_4:			return "4";
		case key_5:			return "5";
		case key_6:			return "6";
		case key_7:			return "7";
		case key_8:			return "8";
		case key_9:			return "9";
		case key_0:			return "0";
		case key_minus:		return "Minus";
		case key_equal:		return "Plus";
		case key_backspace:	return "Backspace";
		case key_tab:		return "Tab";
		case key_Q:			return "Q";
		case key_W:			return "W";
		case key_E:			return "E";
		case key_R:			return "R";
		case key_T:			return "T";
		case key_Y:			return "Y";
		case key_U:			return "U";
		case key_I:			return "I";
		case key_O:			return "O";
		case key_P:			return "P";
		case key_left_bracket:	return "Left bracket";
		case key_right_bracket:	return "Right bracket";
		case key_enter:		return "Enter";
		case key_left_control:	return "Left control";
		case key_A:			return "A";
		case key_S:			return "S";
		case key_D:			return "D";
		case key_F:			return "F";
		case key_G:			return "G";
		case key_H:			return "H";
		case key_J:			return "J";
		case key_K:			return "K";
		case key_L:			return "L";
		case key_colon:		return "Colon";
		case key_apostrophe:	return "Apostrophe";
		case key_back_quote:	return "Back quote";
		case key_left_shift:	return "Left shift";
		case key_back_slash:	return "Backslash";
		case key_Z:			return "Z";
		case key_X:			return "X";
		case key_C:			return "C";
		case key_V:			return "V";
		case key_B:			return "B";
		case key_N:			return "N";
		case key_M:			return "M";
		case key_comma:		return "Less";
		case key_dot:		return "Greater";
		case key_slash:		return "Slash";
		case key_right_shift:	return "Right shift";
		case key_printscreen:	return "Print screen";
		case key_left_alt:		return "Left meta";
		case key_space:		return "Space";
		case key_capslock:	return "Caps lock";
		case key_f1:		return "F1";
		case key_f2:		return "F2";
		case key_f3:		return "F3";
		case key_f4:		return "F4";
		case key_f5:		return "F5";
		case key_f6:		return "F6";
		case key_f7:		return "F7";
		case key_f8:		return "F8";
		case key_f9:		return "F9";
		case key_f10:		return "F10";
		case key_f11:		return "F11";
		case key_f12:		return "F12";
		case key_numlock:	return "Num lock";
		case key_scrolllock:	return "Scroll lock";
		case key_home:		return "Home";
		case key_up:		return "Up";
		case key_pageup:	return "Page up";
		case key_kp_minus:	return "Keypad -";
		case key_left:		return "Left";
		case key_kp_5:	return "Keypad 5";
		case key_right:		return "Right";
		case key_kp_plus:	return "Keypad +";
		case key_end:		return "End";
		case key_down:		return "Down";
		case key_pagedown:	return "Page down";
		case key_insert:	return "Insert";
		case key_delete:	return "Delete";
		case key_kp_asterisk:	return "Keypad *";
		case key_kp_slash:	return "Keypad /";
		case key_pause:			return "Pause";
		case key_right_alt:		return "Right meta";
		case key_right_control:	return "Right control";
		case key_kp_enter:	return "Keypad Enter";
		case key_kp_7:	return "Keypad 7";
		case key_kp_8:	return "Keypad 8";
		case key_kp_9:	return "Keypad 9";
		case key_kp_4:	return "Keypad 4";
		case key_kp_6:	return "Keypad 6";
		case key_kp_1:	return "Keypad 1";
		case key_kp_2:	return "Keypad 2";
		case key_kp_3:	return "Keypad 3";
		case key_kp_0:	return "Keypad 0";
		case key_kp_dot:	return "Keypad .";
		case key_left_win:		return "Left WIN";
		case key_right_win:		return "Right WIN";
		case key_context:		return "Context";
		case key_left_mouse:	return "Left mouse";
		case key_middle_mouse:	return "Middle mouse";
		case key_right_mouse:	return "Right mouse";

		case key_unknown:
		case key_maxcode:
		default:				break;
	}
	return "[unknown key]";
}

/*
 *	Returns keycode by name.
 */
keycode_t	V_GLOBAL
keycodeGetCode(char_p name)
{
	int i, limit;
	if(!name || !name[0])
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"keycodeGetCode('%s'): incorrect parameter.\n",
			strSafe(name));
#endif	/* CHECKED_BUILD */
		return key_unknown;
	}
    limit=(int)key_maxcode;
	for(i=0;i<limit;i++)
		if(!strcmp(name,keycodeGetName((keycode_t)i)))
			return (keycode_t)i;
	return key_unknown;
}

/* Returns char for numpad keycode */
V_LOCAL	char_t
inputKeypadCodeToChar(keycode_t key)
{
	switch(key)
	{
        case key_kp_7:		return '7';
		case key_kp_8:		return '8';
		case key_kp_9:		return '9';
		case key_kp_4:		return '4';
		case key_kp_6:		return '6';
		case key_kp_1:		return '1';
		case key_kp_2:		return '2';
		case key_kp_3:		return '3';
		case key_kp_0:		return '0';
		case key_kp_dot:	return '.';
        default: break;
	}
    return (char_t)0;
}

/* Returns english lowercase character for given keycode */
V_LOCAL	char_t
inputKeycodeToCharLower(keycode_t key)
{
	switch(key)
	{
		case key_escape:	return (char_t)27;
		case key_1:			return '1';
		case key_2:			return '2';
		case key_3:			return '3';
		case key_4:			return '4';
		case key_5:			return '5';
		case key_6:			return '6';
		case key_7:			return '7';
		case key_8:			return '8';
		case key_9:			return '9';
		case key_0:			return '0';
		case key_minus:		return '-';
		case key_equal:		return '=';
		case key_backspace:	return (char_t)8;
		case key_tab:		return (char_t)9;
		case key_Q:			return 'q';
		case key_W:			return 'w';
		case key_E:			return 'e';
		case key_R:			return 'r';
		case key_T:			return 't';
		case key_Y:			return 'y';
		case key_U:			return 'u';
		case key_I:			return 'i';
		case key_O:			return 'o';
		case key_P:			return 'p';
		case key_left_bracket:	return '[';
		case key_right_bracket:	return ']';
		case key_enter:		return (char_t)13;
		case key_A:			return 'a';
		case key_S:			return 's';
		case key_D:			return 'd';
		case key_F:			return 'f';
		case key_G:			return 'g';
		case key_H:			return 'h';
		case key_J:			return 'j';
		case key_K:			return 'k';
		case key_L:			return 'l';
		case key_colon:		return ';';
		case key_apostrophe:	return '\'';
		case key_back_quote:	return '`';
		case key_back_slash:	return '\\';
		case key_Z:			return 'z';
		case key_X:			return 'x';
		case key_C:			return 'c';
		case key_V:			return 'v';
		case key_B:			return 'b';
		case key_N:			return 'n';
		case key_M:			return 'm';
		case key_comma:		return ',';
		case key_dot:		return '.';
		case key_slash:		return '/';
		case key_kp_asterisk:	return (char_t)42;
		case key_kp_slash:	return (char_t)47;
		case key_space:			return (char_t)32;
		case key_kp_minus:	return (char_t)'-';
		case key_kp_plus:		return (char_t)'+';
		case key_kp_enter:	return (char_t)13;
        default: break;
	}
	return (char_t)0;
}

/*
 * Returns english uppercase character for given keycode.
 */
V_LOCAL	char_t
inputKeycodeToCharUpper(keycode_t key)
{
	switch(key)
	{
		case key_escape:	return (char_t)27;
		case key_1:			return '!';
		case key_2:			return '@';
		case key_3:			return '#';
		case key_4:			return '$';
		case key_5:			return '%';
		case key_6:			return '^';
		case key_7:			return '&';
		case key_8:			return '*';
		case key_9:			return '(';
		case key_0:			return ')';
		case key_minus:		return '_';
		case key_equal:		return '+';
		case key_backspace:	return (char_t)8;
		case key_tab:		return (char_t)9;
		case key_Q:			return 'Q';
		case key_W:			return 'W';
		case key_E:			return 'E';
		case key_R:			return 'R';
		case key_T:			return 'T';
		case key_Y:			return 'Y';
		case key_U:			return 'U';
		case key_I:			return 'I';
		case key_O:			return 'O';
		case key_P:			return 'P';
		case key_left_bracket:	return '{';
		case key_right_bracket:	return '}';
		case key_enter:		return (char_t)13;
		case key_A:			return 'A';
		case key_S:			return 'S';
		case key_D:			return 'D';
		case key_F:			return 'F';
		case key_G:			return 'G';
		case key_H:			return 'H';
		case key_J:			return 'J';
		case key_K:			return 'K';
		case key_L:			return 'L';
		case key_colon:		return ':';
		case key_apostrophe:	return '"';
		case key_back_quote:	return '~';
		case key_back_slash:	return '|';
		case key_Z:			return 'Z';
		case key_X:			return 'X';
		case key_C:			return 'C';
		case key_V:			return 'V';
		case key_B:			return 'B';
		case key_N:			return 'N';
		case key_M:			return 'M';
		case key_comma:		return '<';
		case key_dot:		return '>';
		case key_slash:		return '?';
		case key_kp_asterisk:	return (char_t)42;
		case key_kp_slash:	return (char_t)47;
		case key_space:		return (char_t)32;
		case key_kp_minus:	return (char_t)'-';
		case key_kp_plus:	return (char_t)'+';
		case key_kp_enter:	return (char_t)13;
        default: break;
	}
	return (char_t)0;
}

/*
 * For given english character, returns code of national character.
 */
V_LOCAL char_t
inputLocaleChar(char_t char_en,input_state_t *input_state)
{
	int i;
	static char engl_qwerty[]=
        "~!@#$%^&*QWERTYUIOP{}ASDFGHJKL:'ZXCVBNM,.?"\
        "`qwertyuiop[]asdfghjkl;'zxcvbnm,./";
	/*
     *	WARNING: this is hack.
     *	Thanks Eugene Roshal for FAR: shell with both koi8- and
     *	win1251-capable editor !
     */
	static char russian_win1251[]=
    	"¨!\"¹;%:?*ÉÖÓÊÅÍÃØÙÇÕÚÔÛÂÀÏÐÎËÄÆÝß×ÑÌÈÒÜÁÞ,"
    	"¸éöóêåíãøùçõúôûâàïðîëäæýÿ÷ñìèòüáþ.";
	static char russian_koi8[]=
    	"³!\"¿;%:?*êãõëåîçûýúèÿæù÷áðòïìäöüñþóíéôøâà."
    	"£ÊÃÕËÅÎÇÛÝÚÈßÆÙ×ÁÐÒÏÌÄÖÜÑÞÓÍÉÔØÂÀ.";
	/* TODO: support other langs and kmaps ... */
    /* end of hack */
	char *out_qwerty;
    switch(input_state->locale)
    {
    	case input_locale_english:
        	return char_en;
    	case input_locale_russian:
        	out_qwerty=russian_win1251;
            break;
		case input_locale_russian_koi8:
        	out_qwerty=russian_koi8;
            break;
		/* TODO: add support for other locales */
        default:
#ifdef	CHECKED_BUILD
			conlPrintf(LOG_TRACE,
            	"inputLocaleChar(%c): unknown locale '%u'.\n",
                char_en,(unsigned)input_state->locale);
#endif	/* CHECKED_BUILD */
			return char_en;
    }
    for(i=0;engl_qwerty[i];i++)
    {
    	if(engl_qwerty[i]==char_en)
        	return out_qwerty[i];
	}
    return char_en;
}

/*
 * Returns character code for keyboard code.
 * Uses thread-specific input state.
 */            
char_t	V_GLOBAL
inputKeycodeToChar(keycode_t key)
{
	char_t ch;
	input_state_t *state;
    state = inputState();
    if(state->meta || state->control)
    	return (char_t)0;	/* unprintable characters */
	if(state->shift == state->capslock) /* let's assume lowercase */
		ch=inputKeycodeToCharLower(key);
	else
    	ch=inputKeycodeToCharUpper(key);
	if(ch>32)
    	ch=inputLocaleChar(ch,state);
    else
		if(ch == 0 && state->numlock)
    		ch=inputKeypadCodeToChar(key);
	return ch;
}

#ifdef	ARCH_WINDOWING_SUPPORTED

/*
 * No need for global input state.
 */
#define	MSG_LOCALE_EN	0
#define	MSG_LOCALE_RU	1

V_LOCAL char_t
inputLocaleChar2(char_t char_en,s32_t locale)
{
	int i;
	static char engl_qwerty[]=
        "~!@#$%^&*QWERTYUIOP{}ASDFGHJKL:'ZXCVBNM,.?"\
        "`qwertyuiop[]asdfghjkl;'zxcvbnm,./";
	static char russian_win1251[]=
    	"¨!\"¹;%:?*ÉÖÓÊÅÍÃØÙÇÕÚÔÛÂÀÏÐÎËÄÆÝß×ÑÌÈÒÜÁÞ,"
    	"¸éöóêåíãøùçõúôûâàïðîëäæýÿ÷ñìèòüáþ.";
	char *out_qwerty;
    switch(locale)
    {
    	case MSG_LOCALE_EN:
        	return char_en;
    	case MSG_LOCALE_RU:
        	out_qwerty=russian_win1251;
            break;
        default:
#ifdef	CHECKED_BUILD
			conlPrintf(LOG_TRACE,
            	"inputLocaleChar2(%c): unknown locale '%d'.\n",
                char_en,locale);
#endif	/* CHECKED_BUILD */
			return char_en;
    }
    for(i=0;engl_qwerty[i];i++)
    {
    	if(engl_qwerty[i]==char_en)
        	return out_qwerty[i];
	}
    return char_en;
}

#if 0

char_t	V_GLOBAL
inputKeycodeToChar2(ui_message_p msg)
{
	char_t ch;
	s32_t code = MSG_KEY_CODE(msg);
	s32_t flags = MSG_KEY_FLAGS(msg);
	s32_t locale = MSG_KEY_LOCALE(msg);
	vboolean shift, caps, num;
    if((MSG_EVENT(msg)!=ui_event_key_down) && (MSG_EVENT(msg)!=ui_event_key_up))
    	return 0;
	if(flags & MSG_KEY_LMETA || flags & MSG_KEY_RMETA ||
		flags & MSG_KEY_LCONTROL || flags & MSG_KEY_RCONTROL)
		return 0;
	shift = (flags & MSG_KEY_LSHIFT || flags & MSG_KEY_RSHIFT) ? true : false;
	caps = (flags & MSG_KEY_CAPS) ? true : false;
	num = (flags & MSG_KEY_NUM) ? true : false;
	if(shift == caps)
		ch = inputKeycodeToCharLower(code);
	else
    	ch = inputKeycodeToCharUpper(code);
	if(ch > 32)
    	ch = inputLocaleChar2(ch,locale);
    else
    {
		if(ch == 0 && num)
    		ch = inputKeypadCodeToChar(code);
    }
	return ch;
}

#endif	/* ARCH_WINDOWING_SUPPORTED */

#endif
