#include <stdio.h>

#include "le_info.h"
#include "le_desktop.h"
#include "le_session.h"

extern LeInfoRec *le_info;

int le_session_get_imm_key_type(iml_session_t * s,
				IMKeyEventStruct * key_event);

int le_session_process_key_event_for_ime_module(iml_session_t * s,
						IMKeyEventStruct * key_event);

int le_session_process_key_event_for_ime_manager(iml_session_t * s,
						 IMKeyEventStruct * key_event);

/* process the keyboard event */
LeResult le_session_process_key_event(iml_session_t * s,
				      IMKeyListEvent * keylistevent)
{
    LeSessionContextRec *le_session_context = NULL;
    LeDesktopContextRec *le_desktop_context = NULL;

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

    le_desktop_context =
	(LeDesktopContextRec *) le_session_get_desktop_context(s);

    DEBUG_printf
	(" le_session_proc_key_event: desktop: %p, current_session:%p\n",
	 s->desktop, s);

    le_session_context =
	(LeSessionContextRec *) le_session_get_session_context(s);
    if (le_session_context == NULL) {
	le_iml_sendback_key(s, key_event);
    }

    key_ret = le_session_process_key_event_for_ime_manager(s, key_event);
    if (key_ret == IMM_KEY_USED)
	return (LE_OK);

    key_ret = le_session_process_key_event_for_ime_module(s, key_event);
    if (key_ret == IME_UNUSED_KEY)
	le_iml_sendback_key(s, key_event);

    return (LE_OK);
}

int le_session_process_key_event_for_ime_manager(iml_session_t * s,
						 IMKeyEventStruct *
						 key_event)
{
    int imm_key_type, status;

    status = LE_OK;

    imm_key_type = le_session_get_imm_key_type(s, key_event);
    DEBUG_printf
	("le_session_process_key_event_for_ime_manager, imm_key_type: %d\n",
	 imm_key_type);

    if (imm_key_type == IMM_KEY_NOT_USED)
	return (IMM_KEY_NOT_USED);

    switch (imm_key_type) {
    case IMM_KEY_TOGGLE_CONVERSION:
	status = le_session_toggle_conversion_status(s);
	break;
    case IMM_KEY_SWITCH_IME_ROUND:
	status = le_session_switch_to_next_ime_module(s);
	break;
    case IMM_KEY_TOGGLE_QJBJ:
	status = le_session_toggle_qjbj_status(s);
	break;
    case IMM_KEY_TOGGLE_PUNCTUATION:
	status = le_session_toggle_punct_status(s);
	break;
    }

    return (status == LE_OK) ? (IMM_KEY_USED) : (IMM_KEY_NOT_USED);
}

int le_iml_commit_encode_string(iml_session_t * s, int encoding,
				char *commit_str)
{
    UTFCHAR tmp_buf[1024];
    UTFCHAR *tmp_ptr = tmp_buf;
    int from_len, to_left, ret;

    if (commit_str == NULL)
	return (IME_FAIL);

    if (encoding == ENCODE_INVALID)
	return (IME_FAIL);

    from_len = strlen(commit_str);
    to_left = 1024;
    memset(tmp_buf, 0, sizeof(UTFCHAR) * 1024);
    ret = Convert_Native_To_UTF16(encoding,
				  commit_str,
				  from_len,
				  (char **) &tmp_ptr,
				  (size_t *) & to_left);
    if (ret == -1)
	return (IME_FAIL);

    le_iml_commit(s, tmp_buf);
    return (IME_OK);
}

int le_session_proc_qjbj(iml_session_t * s, IMKeyEventStruct * key_event)
{
    char *commit_buf;
    int keycode, keystatus, keychar;

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

    DEBUG_printf("QJBJ PROCESS\n");
    /* normal ASCII key */
    if ((keychar != 0) && (keystatus == IM_SHIFT_MASK || keystatus == 0)) {
	commit_buf = (char *) get_qj_str(keychar);
	if (commit_buf != NULL) {
	    le_iml_commit_encode_string(s, ENCODE_UTF8, commit_buf);
	    return (IME_OK);
	}
    }

    return (IME_UNUSED_KEY);
}

int le_session_proc_punct(iml_session_t * s, IMKeyEventStruct * key_event)
{
    char *commit_buf;
    int keycode, keystatus, keychar;

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

    DEBUG_printf("QJBJ PROCESS\n");
    /* normal ASCII key */
    if ((keychar != 0) && (keystatus == IM_SHIFT_MASK || keystatus == 0)) {
	commit_buf = (char *) get_punct_str(keychar);
	if (commit_buf != NULL) {
	    le_iml_commit_encode_string(s, ENCODE_UTF8, commit_buf);
	    return (IME_OK);
	}
    }

    return (IME_UNUSED_KEY);
}

int le_session_process_key_event_for_ime_module(iml_session_t * s,
						IMKeyEventStruct * key_event)
{
    int key_ret = IME_UNUSED_KEY;
    ImeKeyRec ime_keyevent;
    ImeModuleRec *current_ime_module;

    LeSessionContextRec *le_session_context = NULL;

    DEBUG_printf("  le_session_process_key_event_for_ime_module\n");

    current_ime_module =
	(ImeModuleRec *) le_session_get_current_ime_module(s);
    if (current_ime_module == NULL || current_ime_module->methods == NULL
	|| current_ime_module->methods->ImeProcessKeyEvent == NULL)
	return (IME_UNUSED_KEY);

    le_session_context =
	(LeSessionContextRec *) le_session_get_session_context(s);
    if (le_session_context == NULL)
	return (IME_UNUSED_KEY);

    if (le_session_context->current_qjbj_status == QJBJ_FULLWIDTH) {
	key_ret = le_session_proc_qjbj(s, key_event);
	if (key_ret == IME_OK)
	    return (IME_OK);
    }

    /* set ImeKeyRec for ime module to do filter keyevents */
    ime_keyevent.keycode = key_event->keyCode;
    ime_keyevent.keychar = key_event->keyChar;
    ime_keyevent.modifier = key_event->modifier;

    DEBUG_printf("    begin do ImeProcessKeyEvent for ImeModule: %p\n", current_ime_module);
    key_ret = current_ime_module->methods->ImeProcessKeyEvent((ImeInputContext) le_session_context, 
                                                               &ime_keyevent);

    DEBUG_printf("    After do ImeProcessKeyEvent for ImeModule: %p, return %d\n",
                  current_ime_module, key_ret);

    if (key_ret == IME_UNUSED_KEY) {
        if (le_session_context->current_punct_status == PUNCT_CHINESE) {
            key_ret = le_session_proc_punct(s, key_event);
        }
    }
    return (key_ret);
}

ImmKeybindingRec imm_keybindings_default[] = {
    {IME_VK_SPACE, 0, IME_CTRL_MASK, IMM_KEY_TOGGLE_CONVERSION}
    ,
    {IME_VK_SHIFT, 0, IME_CTRL_MASK, IMM_KEY_SWITCH_IME_ROUND}
    ,
    {IME_VK_CONTROL, 0, IME_SHIFT_MASK, IMM_KEY_SWITCH_IME_ROUND}
    ,
    {IME_VK_SPACE, 0, IME_SHIFT_MASK, IMM_KEY_TOGGLE_QJBJ}
    ,
    {IME_VK_PERIOD, 0, IME_CTRL_MASK, IMM_KEY_TOGGLE_PUNCTUATION}
    ,
};

int le_session_get_imm_key_type(iml_session_t * s,
				IMKeyEventStruct * key_event)
{
    int i, num_keybindings;
    ImmKeybindingRec **keybindings;

    int keycode, keychar, modifier;

    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);

    if (le_info == NULL)
	return (IMM_KEY_NOT_USED);

    num_keybindings = le_info->num_imm_keybindings;
    keybindings = le_info->imm_keybindings;

    DEBUG_printf("num_keys: %d\n", num_keybindings);
    if (keybindings != NULL) {
	for (i = 0; i < num_keybindings; i++) {
	    if (keycode == keybindings[i]->keycode &&
		modifier == keybindings[i]->modifier) {
		return (keybindings[i]->defined_type);
	    }
	}
    }

    num_keybindings =
	sizeof(imm_keybindings_default) / sizeof(ImmKeybindingRec);
    for (i = 0; i < num_keybindings; i++) {
	if (keycode == imm_keybindings_default[i].keycode &&
	    modifier == imm_keybindings_default[i].modifier) {
	    return (imm_keybindings_default[i].defined_type);
	}
    }

    return (IMM_KEY_NOT_USED);
}
