#include <stdio.h>
#include "le_info.h"
#include "le_session.h"

char *iiim_keycode_to_name(short keycode);
MSymbol le_session_map_keyevent_to_m17n_key(IMKeyEventStruct * key_event);

extern LeInfoRec *le_info;

/* process the keyboard event */
void le_session_process_key_event(iml_session_t * s,
				  IMKeyListEvent * keylistevent)
{
    LeSessionContextRec *le_session_context;

    MInputContext *ic;
    MSymbol m17n_key;
    MConverter *converter;
    MText *produced;

    int ret;
    UTFCHAR buf[256];

    IMKeyEventStruct *key_event =
	(IMKeyEventStruct *) keylistevent->keylist;

    le_session_context =
	(LeSessionContextRec *) le_session_get_session_context(s);
    m17n_key = le_session_map_keyevent_to_m17n_key(key_event);

    if (le_session_context == NULL ||
	le_session_context->ic == (MInputContext *) Mnil
	|| m17n_key == Mnil) {
	le_iml_sendback_key(s, key_event);
	return;
    }

    DEBUG_printf("m17n_key: %s\n", msymbol_name(m17n_key));
    if (m17n_key == le_info->Mkey_switch_next_im) {
        le_session_switch_to_next_input_method(s);
        return;
    }

    if (m17n_key == le_info->Mkey_english_native) {
	le_session_set_conversion_status(s, CONVERSION_OFF);
	return;
    }

    ic = le_session_context->ic;
    ret = minput_filter(ic, m17n_key, NULL);
    if (ret) {
	DEBUG_printf("minput_filter return 1\n");
	return;
    }

    produced = mtext();

    ret = minput_lookup(ic, m17n_key, NULL, produced);
    if (mtext_len(produced) <= 0) {
	DEBUG_printf("minput_lookup return -1\n");
	le_iml_sendback_key(s, key_event);
	return;
    }

    converter = le_info->converter;

    memset(buf, 0, sizeof(buf));
    mconv_rebind_buffer(converter, (unsigned char *) buf, sizeof(buf));
    mconv_encode(converter, produced);
    buf[converter->nbytes] = 0;
    m17n_object_unref(produced);

    if (buf[0]) {
	DEBUG_printf("commit buf: 0x%x\n", buf[0]);
	le_iml_commit(s, buf);
    }

    return;
}

MSymbol le_session_map_keyevent_to_m17n_key(IMKeyEventStruct * key_event)
{
    int keycode, keychar, modifier;
    char key_name[256];
    char key_string[256];

    keycode = key_event->keyCode;
    keychar = key_event->keyChar;
    modifier = key_event->modifier;

    DEBUG_printf("keycode: 0x%x, keychar:0x%x, modifier: 0x%x\n",
		 keycode, keychar, modifier);

    /* Ctrl + Space */
    if ((keycode == IM_VK_SPACE) && (modifier & IM_CTRL_MASK))
	return le_info->Mkey_english_native;

    /* Ctrl + Shift */
    if (((keycode == IM_VK_CONTROL) && (modifier & IM_SHIFT_MASK)) ||
        ((keycode == IM_VK_SHIFT) && (modifier & IM_CTRL_MASK)))
	return le_info->Mkey_switch_next_im;

    memset(key_name, 0, sizeof(key_name));

    /* Normal Alphameric key */
    if (keychar >= 0x20 && keychar <= 0x7e) {

            if ((modifier & IM_CTRL_MASK) &&
                (keychar  >= 'a' && keychar <= 'z'))
                keychar += 'A' - 'a';
	    key_name[0] = keychar;

    } else if (keychar == 0) {
        char *key_str;

        if (keycode == IM_VK_SHIFT ||
            keycode == IM_VK_CONTROL ||
            keycode == IM_VK_ALT ||
            keycode == IM_VK_CAPS_LOCK)
            return Mnil;

        key_str = iiim_keycode_to_name(keycode);
        DEBUG_printf("iiim_keycode_to_name: key_str: %s\n", key_str? key_str: "NULL");
        if (! key_str)
            return Mnil;

        strncpy(key_name, key_str, sizeof(key_name));
    }

    if (!*key_name)
        return Mnil;

    memset(key_string, 0, sizeof(key_string));

    if (modifier & IM_SHIFT_MASK)
	strncat(key_string, "S-", sizeof(key_string));
    if (modifier & IM_CTRL_MASK)
	strncat(key_string, "C-", sizeof(key_string));
    if (modifier & IM_META_MASK)
	strncat(key_string, "M-", sizeof(key_string));
    if (modifier & IM_ALT_MASK)
	strncat(key_string, "A-", sizeof(key_string));

    strncat(key_string, key_name, sizeof(key_string));
    return msymbol(key_string);
}

typedef struct {
    short keycode;
    char *name;
} keycode_name_pair_t;

static keycode_name_pair_t keycode_name_pairs[] = {
    { IM_VK_ENTER,         "Return"},
    { IM_VK_BACK_SPACE,    "BackSpace"},
    { IM_VK_TAB,           "Tab"},
    { IM_VK_CANCEL,        "Cancel"},
    { IM_VK_CLEAR,         "Clear"},
    { IM_VK_SHIFT,         "Shift"},
    { IM_VK_CONTROL,       "Control"},
    { IM_VK_ALT,           "Alt"},
    { IM_VK_PAUSE,         "Pause"},
    { IM_VK_CAPS_LOCK,     "Caps_Lock"},
    { IM_VK_ESCAPE,        "Escape"},
    { IM_VK_SPACE,         "Space"},
    { IM_VK_PAGE_UP,       "Prior"},
    { IM_VK_PAGE_DOWN,     "Next"},
    { IM_VK_END,           "End"},
    { IM_VK_HOME,          "Home"},
    { IM_VK_LEFT,          "Left"},
    { IM_VK_UP,            "Up"},
    { IM_VK_RIGHT,         "Right"},
    { IM_VK_DOWN,          "Down"},
    { IM_VK_COMMA,         "comma"},
    { IM_VK_MINUS,         "minus"},
    { IM_VK_PERIOD,        "period"},
    { IM_VK_SLASH,         "slash"},
    { IM_VK_0,             "0"},
    { IM_VK_1,             "1"},
    { IM_VK_2,             "2"},
    { IM_VK_3,             "3"},
    { IM_VK_4,             "4"},
    { IM_VK_5,             "5"},
    { IM_VK_6,             "6"},
    { IM_VK_7,             "7"},
    { IM_VK_8,             "8"},
    { IM_VK_9,             "9"},
    { IM_VK_SEMICOLON,     "semicolon"},
    { IM_VK_EQUALS,        "equal"},
    { IM_VK_A,             "A"},
    { IM_VK_B,             "B"},
    { IM_VK_C,             "C"},
    { IM_VK_D,             "D"},
    { IM_VK_E,             "E"},
    { IM_VK_F,             "F"},
    { IM_VK_G,             "G"},
    { IM_VK_H,             "H"},
    { IM_VK_I,             "I"},
    { IM_VK_J,             "J"},
    { IM_VK_K,             "K"},
    { IM_VK_L,             "L"},
    { IM_VK_M,             "M"},
    { IM_VK_N,             "N"},
    { IM_VK_O,             "O"},
    { IM_VK_P,             "P"},
    { IM_VK_Q,             "Q"},
    { IM_VK_R,             "R"},
    { IM_VK_S,             "S"},
    { IM_VK_T,             "T"},
    { IM_VK_U,             "U"},
    { IM_VK_V,             "V"},
    { IM_VK_W,             "W"},
    { IM_VK_X,             "X"},
    { IM_VK_Y,             "Y"},
    { IM_VK_Z,             "Z"},
    { IM_VK_OPEN_BRACKET,  "bracketleft"},
    { IM_VK_BACK_SLASH,    "backslash"},
    { IM_VK_CLOSE_BRACKET, "bracketright"},
    { IM_VK_NUMPAD0,       "KP_0"},
    { IM_VK_NUMPAD1,       "KP_1"},
    { IM_VK_NUMPAD2,       "KP_2"},
    { IM_VK_NUMPAD3,       "KP_3"},
    { IM_VK_NUMPAD4,       "KP_4"},
    { IM_VK_NUMPAD5,       "KP_5"},
    { IM_VK_NUMPAD6,       "KP_6"},
    { IM_VK_NUMPAD7,       "KP_7"},
    { IM_VK_NUMPAD8,       "KP_8"},
    { IM_VK_NUMPAD9,       "KP_9"},
    { IM_VK_MULTIPLY,      "KP_Multiply"},
    { IM_VK_ADD,           "KP_Add"},
    { IM_VK_SEPARATER,     "KP_Separator"},
    { IM_VK_SUBTRACT,      "KP_Subtract"},
    { IM_VK_DECIMAL,       "KP_Decimal"},
    { IM_VK_DIVIDE,        "KP_Divide"},
    { IM_VK_DELETE,        "KP_Delete"},
    { IM_VK_NUM_LOCK,      "Num_Lock"},
    { IM_VK_SCROLL_LOCK,   "Scroll_Lock"},
    { IM_VK_F1,            "F1"},
    { IM_VK_F2,            "F2"},
    { IM_VK_F3,            "F3"},
    { IM_VK_F4,            "F4"},
    { IM_VK_F5,            "F5"},
    { IM_VK_F6,            "F6"},
    { IM_VK_F7,            "F7"},
    { IM_VK_F8,            "F8"},
    { IM_VK_F9,            "F9"},
    { IM_VK_F10,           "F10"},
    { IM_VK_F11,           "F11"},
    { IM_VK_F12,           "F12"},
    { IM_VK_F13,           "F13"},
    { IM_VK_F14,           "F14"},
    { IM_VK_F15,           "F15"},
    { IM_VK_F16,           "F16"},
    { IM_VK_F17,           "F17"},
    { IM_VK_F18,           "F18"},
    { IM_VK_F19,           "F19"},
    { IM_VK_F20,           "F20"},
    { IM_VK_F21,           "F21"},
    { IM_VK_F22,           "F22"},
    { IM_VK_F23,           "F23"},
    { IM_VK_F24,           "F24"},
    { IM_VK_PRINTSCREEN,   "PrintScreen"},
    { IM_VK_INSERT,        "Insert"},
    { IM_VK_HELP,          "Help"},
    { IM_VK_META,          "Meta_L"},
    { IM_VK_BACK_QUOTE,    "BackQuote"},
    { IM_VK_QUOTE,         "Quote"},
    { IM_VK_KP_UP,         "KP_Up"},
    { IM_VK_KP_DOWN,       "KP_Down"},
    { IM_VK_KP_LEFT,       "KP_Left"},
    { IM_VK_KP_RIGHT,      "KP_Right"},
    { IM_VK_DEAD_GRAVE,              "dead_grave"},
    { IM_VK_DEAD_ACUTE,              "dead_acute"},
    { IM_VK_DEAD_CIRCUMFLEX,         "dead_circumflex"},
    { IM_VK_DEAD_TILDE,              "dead_tilde"},
    { IM_VK_DEAD_MACRON,             "dead_macron"},
    { IM_VK_DEAD_BREVE,              "dead_breve"},
    { IM_VK_DEAD_ABOVEDOT,           "dead_abovedot"},
    { IM_VK_DEAD_DIAERESIS,          "dead_diaeresis"},
    { IM_VK_DEAD_ABOVERING,          "dead_abovering"},
    { IM_VK_DEAD_DOUBLEACUTE,        "dead_doubleacute"},
    { IM_VK_DEAD_CARON,              "dead_caron"},
    { IM_VK_DEAD_CEDILLA,            "dead_cedilla"},
    { IM_VK_DEAD_OGONEK,             "dead_ogonek"},
    { IM_VK_DEAD_IOTA,               "dead_iota"},
    { IM_VK_DEAD_VOICED_SOUND,       "dead_voiced_sound"},
    { IM_VK_DEAD_SEMIVOICED_SOUND,   "dead_semivoiced_sound"},
    { IM_VK_AMPERSAND,               "ampersand"},
    { IM_VK_ASTERISK,                "asterisk"},
    { IM_VK_QUOTEDBL,                "quotedbl"},
    { IM_VK_LESS,                    "less"},
    { IM_VK_GREATER,                 "greater"},
    { IM_VK_BRACELEFT,               "braceleft"},
    { IM_VK_BRACERIGHT,              "braceright"},
    { IM_VK_AT,                      "at"},
    { IM_VK_COLON,                   "colon"},
    { IM_VK_CIRCUMFLEX,              "circumflex"},
    { IM_VK_DOLLAR,                  "dollar"},
    { IM_VK_EURO_SIGN,               "euro"},  /* ? */
    { IM_VK_EXCLAMATION_MARK,        "exclam"},
    { IM_VK_INVERTED_EXCLAMATION_MARK,"inverted_exclam"}, /* ? */
    { IM_VK_LEFT_PARENTHESIS,        "parenleft"},
    { IM_VK_NUMBER_SIGN,             "numbersign"},
    { IM_VK_PLUS,                    "plus"},
    { IM_VK_RIGHT_PARENTHESIS,       "parenright"},
    { IM_VK_UNDERSCORE,              "underscore"},
    { IM_VK_FINAL,                   "final"},         /* ? */
    { IM_VK_CONVERT,                 "convert"},       /* ? */
    { IM_VK_NONCONVERT,              "noconvert"},       /* ? */
    { IM_VK_ACCEPT,                  "accept"},       /* ? */
    { IM_VK_MODECHANGE,              "modechange"},       /* ? */
    { IM_VK_KANA,                    "kana"},       /* ? */
    { IM_VK_HANGUL,	             "hangul"},       /* ? */
    { IM_VK_JUNJA,	             "junja"},       /* ? */
    { IM_VK_FINAL,                   "final"},       /* ? */
    { IM_VK_KANJI,                   "kanji"},       /* ? */
    { IM_VK_HANJA,                   "hanja"},       /* ? */
    { IM_VK_ALPHANUMERIC,            "alphanumberic"},       /* ? */
    { IM_VK_KATAKANA,                "katakana"},       /* ? */
    { IM_VK_HIRAGANA,                "rhkiragana"},       /* ? */
    { IM_VK_FULL_WIDTH,              "fullwidth"},       /* ? */
    { IM_VK_HALF_WIDTH,              "halfwidth"},       /* ? */
    { IM_VK_ROMAN_CHARACTERS,        "romancandidates"},       /* ? */
    { IM_VK_ALL_CANDIDATES,          "allcandidates"},       /* ? */
    { IM_VK_PREVIOUS_CANDIDATE,      "previouscandidate"},       /* ? */
    { IM_VK_CODE_INPUT,              "codeinput"},       /* ? */
    { IM_VK_JAPANESE_KATAKANA,       "japanese_katakana"},       /* ? */
    { IM_VK_JAPANESE_HIRAGANA,       "japanese_hiragana"},       /* ? */
    { IM_VK_JAPANESE_ROMAN,          "japanese_roman"},       /* ? */
    { IM_VK_CUT,                     "cut"},       /* ? */
    { IM_VK_COPY,                    "copy"},       /* ? */
    { IM_VK_PASTE,                   "paste"},       /* ? */
    { IM_VK_UNDO,                    "undo"},       /* ? */
    { IM_VK_AGAIN,                   "again"},       /* ? */
    { IM_VK_FIND,                    "find"},       /* ? */
    { IM_VK_PROPS,                   "props"},       /* ? */
    { IM_VK_STOP,                    "stop"},       /* ? */
    { IM_VK_COMPOSE,                 "compose"},       /* ? */
    { IM_VK_ALT_GRAPH,               "alt_graph"}       /* ? */
};

char *iiim_keycode_to_name(short keycode)
{
    int total, i;

    total = sizeof(keycode_name_pairs)/sizeof(keycode_name_pair_t);

    for (i = 0; i < total; i++) {
	if (keycode_name_pairs[i].keycode == keycode)
	    return (keycode_name_pairs[i].name);
    }

    return NULL;
}
