#include "xk_tab.h"
#include <X11/keysym.h>
#include <string.h>
#include "cgidbg.h"
#include <InterViews/event.h>


typedef char *(*char_func)(uint32 c);

struct X_AliasTableFragment {
	uint32 character ;
	uint32 alias ;
};

struct X_AliasTable {
	int base ;
	int size ;
	X_AliasTableFragment * table ;
};

struct X_CharacterTableFragment {
	uint32 character ;
	char * name ;
};

struct X_CharacterTable {
	int base ;
	int size ;
	X_CharacterTableFragment * table ;
	char_func func ;				// either table or func is null
};

static X_CharacterTableFragment escape_character[] = {
	{XK_Escape, "escape"},
	{0}
};

static X_CharacterTableFragment delete_character[] = {
	{XK_Delete, "delete"},
	{0}
};

static X_CharacterTableFragment cursor_control_and_motion[] = {
	{XK_Home, "home"},
	{XK_Left, "left arrow"},
	{XK_Up, "up arrow"},
	{XK_Right, "right arrow"},
	{XK_Down, "down arrow"},
	{XK_Prior, "page up"},
	{XK_Next, "page down"},
	{XK_End, "end"},
	{XK_Begin, "begin"},
	{0}
};

static X_CharacterTableFragment tty_functions[] = {
	{XK_BackSpace, "backspace"},
	{XK_Tab, "tab"},
	{XK_Linefeed, "linefeed"},
	{XK_Clear, "clear"},
	{XK_Clear+1, "NOT DEFINED"},
	{XK_Return, "return"},
	{0}
};

static X_CharacterTableFragment tty_functions_2[] = {
	{XK_Pause, "pause"},
	{XK_Scroll_Lock, "scroll_lock"},
	// {XK_Sys_Req, "sys_req"},
	{0}
};

static X_CharacterTableFragment misc_functions[] = {
	{XK_Select, "select"},
	{XK_Print, "print"},
	{XK_Execute, "exe"},
	{XK_Insert, "insert"},
	{XK_Insert + 1, 0}, 		// not defined
	{XK_Undo, "undo"},
	{XK_Redo, "redo"},
	{XK_Menu, "menu"},
	{XK_Find, "find"},
	{XK_Cancel, "cancel"},
	{XK_Help, "help"},
	{XK_Break, "break"},
	{0}
};

// static X_AliasTableFragment keypad_cursor_control_and_motion[] = {
	// {XK_KP_Home,  XK_Home},
	// {XK_KP_Left,  XK_Left},
	// {XK_KP_Up, XK_Up},
	// {XK_KP_Right, XK_Right},
	// {XK_KP_Down, XK_Down}, 
	// {XK_KP_Page_Down, XK_Page_Down}, 
	// {XK_KP_Next, XK_Page_Up},
	// {XK_KP_End, XK_End},
	// {XK_KP_Insert, XK_Insert},
	// {XK_KP_Delete, XK_Delete},
	// {0}
// };

static char * ascii_characters(uint32 c) 
{
	if (c == XK_space) return "space" ;
	if (c > XK_space && c <= XK_asciitilde) {
		static char buf[2] ;
		buf[0] = c ;
		buf[1] = '\0' ;
		return buf ;
	}
	return 0 ;
}

static char * function_keys(uint32 c)
{
	static char function_key[4] ;
	strcpy(function_key,"Fxx");
	if (c < XK_F1 || c > XK_F12) return 0 ;
	if (c >= XK_F10) {
		function_key[1] = '1' ;
		function_key[2] = '0' + c - XK_F10 ;
		function_key[3] = '\0' ;
	} else {
		function_key[1] = '1' + c - XK_F1 ;
		function_key[2] = '\0' ;
	}
	return function_key ;
}

static X_CharacterTable character_table[] = {
	{XK_space,XK_asciitilde - XK_space + 1,0, ascii_characters},
	{XK_F1,XK_F12-XK_F1+1,0,function_keys},
	{0, 0, cursor_control_and_motion},
	{0, 0, misc_functions},
	{0, 0, tty_functions},
	{0, 0, tty_functions_2},
	{0, 0, delete_character},
	{0, 0, escape_character},
	{0,0,0,0}
};


static X_AliasTable alias_table[] = {
	// {0,0,keypad_cursor_control_and_motion},
	{0,0,0}
};

static X_Character X_character_control(character_table,alias_table);

X_Character& X_Character::table()
{
	return X_character_control ;
}


X_Character::X_Character(X_CharacterTable * char_tab, X_AliasTable * alias_tab):
	character_table(char_tab),
	alias_table(alias_tab)
{
	for (X_CharacterTable * entry = character_table;
		entry->table || entry->func; entry++) if (entry->table) {
			X_CharacterTableFragment * frag_table = entry->table ;
			entry->base = frag_table->character ;
			int size = 0 ;
			for (X_CharacterTableFragment * frag = frag_table; frag->character;
				frag++, size++) if (size) if (frag[-1].character+1 !=
				frag->character) {
					TheLog << hex << "0x" << frag[-1].character << ", 0x"
						<< frag->character << dec << "\n" ;
					DbgError("X_Character::X_Character","bad table");
			}
			entry->size = size ;
	}
	{
	  for (X_AliasTable * entry = alias_table; entry->table; entry++) {
			X_AliasTableFragment * frag_table = entry->table ;
			entry->base = frag_table->character ;
			// LogOut<<"base = 0x"<<hex<<frag_table->character<< dec << "\n" ;
			int size = 0 ;
			for (X_AliasTableFragment * frag = frag_table; frag->character;
				frag++, size++) if (size) if (frag[-1].character+1 !=
				frag->character) {
					TheLog << hex << "0x" << frag[-1].character << ", 0x"
						<< frag->character << dec << "\n" ;
					DbgError("X_Alias::X_Alias","bad table");
			}
			entry->size = size ;
	  }
	}
}

const buttons = 3 ;
const modified_buttons = buttons * 4 ;
const shift_offset = 3 ;
const control_offset = 6 ;

int X_Character::mouse_table_size()
{
	return modified_buttons * 2 ;
}

int32 X_Character::mouse_code(const ivEvent& event, int down_flag)
{
	unsigned int the_button = event.pointer_button();
	int left = the_button == Event::left ;
    int right = the_button == Event::right  ;
    int middle = the_button == Event::middle  ;

	int sum = left + right + middle ;
	if (sum != 1) return -1 ;

	int shift_on = event.shift_is_down() ;
	int control_on = event.control_is_down() ;
/*
 *	LogOut << "X_Character::mouse_code, " << left << "," << middle << "," <<
 *		right << ", df = " << down_flag << ", C S = " << control_on << " "
 *			<< shift_on << "\n" ;
 */

	if (!sum || sum > 1) return -1 ;
	int Return = 0 ;
	if (down_flag) Return += modified_buttons ;
	if (shift_on) Return += shift_offset ;
	if (control_on) Return += control_offset ;
	if (middle) Return += 1 ;
	else if (right) Return += 2 ;
	return Return ;

}

const int * X_Character::mouse_code_to_index_array(uint32 code)
{
	if (!code) return 0 ;
	static int Return[buttons+1] ;
	int shift_on = code & shift ;
	int ctrl_on = code & ctrl ;
	int down_action = code & down ;
	int any_button = code & any ;

	int index = 0 ;
	if (down_action) index =  modified_buttons ;
	if (shift_on) index += shift_offset ;
	if (ctrl_on) index += control_offset ;
	if (any_button) {
		for (int i = 0; i < buttons;i++) Return[i] = index+i;
		Return[i] = -1 ;
		return Return;
	}
	if (code & left) Return[0] = index ;
	else if (code & middle) Return[0] = index + 1 ;
	else if (code & right) Return[0] = index + 2 ;
	else DbgError("X_Character::mouse_code_to_index_array","no button");
	Return[1] = -1;
	return Return ;
}

const char * X_Character::name_of_mouse_code(uint32 code)
{
	static uint32 buttons = (left | middle | right) ;
	if (!code) return 0 ;
	const buf_size = 64 ;
	static char buf[buf_size] ;
	int shift_on = code & shift ;
	int ctrl_on = code & ctrl ;
	int down_action = code & down ;
	int any_button = code & any ;
	strcpy(buf," [");
	if (shift_on && ctrl_on) strcat(buf,"ctrl-shift-") ;
	else if (shift_on) strcat(buf,"shift-") ;
	else if (ctrl_on) strcat(buf,"ctrl-") ;
	if (down_action) {
		strcat(buf,"D");
		if (code | buttons) strcat(buf," ");
	} else if (! (code | buttons) && ! any_button) DbgError(
		"X_Character::name_of_mouse_code","bad code");
	if (code & any) strcat(buf,"ANY");
	else switch (code & buttons) {
case 0:
		break;
default:
		TheLog << "code `" << code << "',  buttons `" << buttons <<
			"'\n" ;
		DbgError("X_Character::name_of_mouse_code","bad button");
case left:
		strcat(buf,"L");
		break ;
case middle:
		strcat(buf,"M");
		break ;
case right:
		strcat(buf,"R");
		break ;
	}
	if (strlen(buf) < 2) DbgError("X_Character::name_of_mouse_code",
		"too short");
	strcat(buf,"]");
	return buf ;
}

const char * X_Character::name(uint32 c)
{
	uint32 save_opt = c & XK_mask ;
	// LogOut<<hex<<"c = 0x"<<c<<", save_opt = " << save_opt << dec << "\n" ; 
	c &= ~XK_mask ;
	// LogOut << " X_Character::name(0x" << hex << c << dec << ")\n" ;
	uint32 d = alias_of(c);
	// LogOut << "alias_of = 0x" << hex << d << dec << "\n" ;
	if (d) c=d ;
	const char * found = 0 ;
	for(X_CharacterTable * table = character_table; table->table || table->func;
		table++) if (c >= table->base && c < table->base + table->size) {
			if (table->table) found = table->table[c-table->base].name;
			else found = table->func(c) ;
			break ;
	}
	// LogOut << "found = " << (void *) found << "\n" ;
	if (!found) return 0 ;
	// LogOut << "found is `" << found << "'\n" ;

	static const char * ctr_opt[] = {
		"#", "ctrl", "shift", "ctrl-shift"
	};

	static int shift[] = {
		2, // shift
		3, // ctrl-shift
		2, // shift
		3  // ctrl-shift 
	};

	save_opt >>= 16 ;
	if (found[1] == '\0') if (X_Character::shift_table.un_shift(found[0]))
		save_opt=shift[save_opt];
	if (!save_opt) return found ;
	const buf_size = 32 ;
	// LogOut << "save_opt = " << save_opt << "\n" ;
	if (save_opt < 1 || save_opt > 3) DbgError("X_Character::name", "bad mod");
	const char * mdfr = ctr_opt[save_opt];
	// LogOut << "mdfr = `" << mdfr << "'\n" ;
	static const char * dash = "-" ;
	if (strlen(found) + strlen(mdfr) + strlen(dash) >= buf_size)
		DbgError( "X_Character::name", "too big");
	static char buf[buf_size] ; 
	strcpy(buf,mdfr);
	strcat(buf,dash);
	strcat(buf,found);
	return buf ;
}

uint32 X_Character::alias_of(uint32 c)
{
	// LogOut << "X_Character::alias_of(0x" << hex << c << dec << ")\n" ;
	uint32 save_opt = c & XK_mask ;
	c &= ~XK_mask ;
	if (!alias_table) {
		// LogOut << " no table\n" ;
		return 0 ;
	}
	for (X_AliasTable * table = alias_table ; table->table;table++) {
		// LogOut << "table = " << (void *) table << "\n" ;
		// LogOut << "table->table = " << (void *) table->table << "\n" ;
		for (X_AliasTableFragment * entry = table->table ; entry->character;
			entry++) {
/*
 *			LogOut << "entry->character = 0x" << hex << entry->character <<
 *				dec << "\n" ;
 */
			if (c == entry->character) {
/*
 *				LogOut << "Returning 0x" << hex << (entry->alias | save_opt) <<
 *					dec << "\n" ;
 */
				return entry->alias | save_opt ;
			}
		}
	}
	// LogOut << "no alias\n" ;
	return 0 ;
}

