/*
Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial
portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


Except as contained in this notice, the names of The Open Group and/or
Sun Microsystems, Inc. shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior
written authorization from The Open Group and/or Sun Microsystems,
Inc., as applicable.


X Window System is a trademark of The Open Group

OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
logo, LBX, X Window System, and Xinerama are trademarks of the Open
Group. All other trademarks and registered trademarks mentioned herein
are the property of their respective owners. No right, title or
interest in or to any trademark, service mark, logo or trade name of
Sun Microsystems, Inc. or its licensors is granted.

*/

#include "iiimpReq.h"
#include "iiimpIM.h"
#include "guiIM.h"
#include "XimpIm.h"
#include "xiiimp.h"
#include "iiimpAuth.h"
#include "iiimpAuxP.h"
#include "iiimpUtil.h"
#include "iiimpColor.h"
#include "iiimpTr.h"
#include "KeyMap.h"
#include "trace_message.h"
#include <stdio.h>
#include <locale.h>
#include <string.h>
#include <sys/utsname.h>
#include <iiimcf.h>
#include <langinfo.h>

static IIIMCF_language get_input_language (XicCommon ic, char *input_language,
					   Bool exact_match);

static IIIMF_status
process_keyevent(
    XicCommon ic,
    IIIMCF_event ev
)
{
    IIIMF_status st;
    IIIMCF_keyevent kev;
    XKeyEvent xkev;
    XimCommon im = (XimCommon)ic->core.im;

    st = iiimcf_get_keyevent_value(ev, &kev);
    if (st != IIIMF_STATUS_SUCCESS) return st;

    IIIMCF_keyevent_To_XKeyEvent(&kev, im->core.display, ic->core.focus_window, &xkev);
    
    if (ic->forward_event_callback.callback) {
	ic->forward_event_callback.callback((XIC)ic,
					    ic->forward_event_callback.client_data,
					    (XPointer)&xkev);
    } else {
	HoldXKeyEvent(ic, (XEvent*)&xkev);
    }

    return IIIMF_STATUS_SUCCESS;
}

static IIIMF_status
process_trigger_notify(
    XicCommon ic,
    IIIMCF_event ev
)
{
    IIIMF_status st;
    int flag;

    st = iiimcf_get_trigger_notify_flag(ev, &flag);
    if (st != IIIMF_STATUS_SUCCESS) return st;
    if (flag)
	SetConversionMode(ic, True);
    else
	SetConversionMode(ic, False);

    return IIIMF_STATUS_SUCCESS;
}
    
static IIIMF_status
draw_preedit(
    XicCommon ic
)
{
    IIIMF_status st;
    IIIMCF_text text;
    XIMText cbtext;
    int caret, len;
    IMFeedbackList *feedback_list = NULL;
    XICCallback *cb = &ic->core.preedit_attr.draw_callback;
    XIMPreeditDrawCallbackStruct call_data;
    XimCommon im = (XimCommon)ic->core.im;
    Bool useUnicode;

    memset(&call_data, 0, sizeof(XIMPreeditDrawCallbackStruct));
    memset(&cbtext, 0, sizeof(XIMText));

    useUnicode = (XIM_USE_UNICODE(im) && cb->callback &&
		  (ic->core.input_style & XIMPreeditCallbacks));

    st = iiimcf_get_preedit_text(XIC_IIIMP(ic, context), &text, &caret);
    if (st == IIIMF_STATUS_NO_PREEDIT) {
	call_data.text = NULL;
	len = 0;
    } else {
	if (st != IIIMF_STATUS_SUCCESS) return st;
	st = iiimcf_get_text_length(text, &len);
	if (st != IIIMF_STATUS_SUCCESS) return st;
	if (len > 0) {
	    st = IIimpConvertToXIMText(text, &cbtext, &feedback_list, useUnicode);
	    if (st != IIIMF_STATUS_SUCCESS) return st;
	    call_data.text = &cbtext;
	} else {
	    call_data.text = NULL;
	}
    }
    call_data.caret = caret;
    call_data.chg_first = 0;
    call_data.chg_length = XIC_IIIMP(ic, preedit_prev_len);
    CallPreeditDrawCallback(ic, &call_data, feedback_list);
    FreeFeedbackList(feedback_list, cbtext.length);
    XIC_IIIMP(ic, preedit_prev_len) = len;
    if (cbtext.string.multi_byte) Xfree(cbtext.string.multi_byte);
    if (cbtext.feedback) Xfree(cbtext.feedback);

    return IIIMF_STATUS_SUCCESS;
}

static IIIMF_status
draw_status(
    XicCommon ic
)
{
    IIIMF_status st;
    IIIMCF_text text;
    XIMText cbtext;
    IMFeedbackList *feedback_list = NULL;
    XICCallback *cb = &ic->core.status_attr.draw_callback;
    XIMStatusDrawCallbackStruct call_data;
    XimCommon im = (XimCommon)ic->core.im;
    Bool useUnicode;

    memset(&call_data, 0, sizeof(XIMStatusDrawCallbackStruct));
    memset(&cbtext, 0, sizeof(XIMText));

    useUnicode = (XIM_USE_UNICODE(im) && cb->callback &&
		  (ic->core.input_style & XIMStatusCallbacks));

    st = iiimcf_get_status_text(XIC_IIIMP(ic, context), &text);
    if (st == IIIMF_STATUS_NO_STATUS_TEXT) {
	/* erase status text.  */
	cbtext.feedback = NULL;
	cbtext.string.multi_byte = NULL;
	cbtext.length = 0;
	call_data.data.text = &cbtext;
    } else {
	if (st != IIIMF_STATUS_SUCCESS) return st;
	st = IIimpConvertToXIMText(text, &cbtext, &feedback_list, useUnicode);
	if (st != IIIMF_STATUS_SUCCESS) return st;
	call_data.data.text = &cbtext;
	call_data.type = XIMTextType;
    }
    CallStatusDrawCallback(ic, &call_data, feedback_list);
    FreeFeedbackList(feedback_list, cbtext.length);
    if (cbtext.string.multi_byte) Xfree(cbtext.string.multi_byte);
    if (cbtext.feedback) Xfree(cbtext.feedback);

    return IIIMF_STATUS_SUCCESS;
}

static IIIMF_status
start_lookup_choice(
    XicCommon ic
)
{
    IIIMF_status st;
    IIIMCF_lookup_choice lc;
    XIMLookupStartCallbackStruct2 call_data;
    int direction;

    st = iiimcf_get_lookup_choice(XIC_IIIMP(ic, context), &lc);
    if (st != IIIMF_STATUS_SUCCESS) return st;
    st = iiimcf_get_lookup_choice_configuration(lc,
						&call_data.choice_per_window,
						&call_data.nrows,
						&call_data.ncolumns,
						&direction);
    if (direction == IIIMCF_LOOKUP_CHOICE_HORIZONTAL_DIRECTION) {
	call_data.draw_up_direction = XIMDrawUpHorizontally;
    } else {
	call_data.draw_up_direction = XIMDrawUpVertically;
    }
    if (st != IIIMF_STATUS_SUCCESS) return st;
    /* libiiimcf rules them.  */
    call_data.which_is_master = IM_LOOKUP_CHOICE_START_SERVER_IS_MASTER;
    call_data.who_owns_label = 0;

    if (ic->lookup_start_callback.callback) {
	XIMCallback *cb = &ic->lookup_start_callback;
	(*cb->callback)((XIC)ic, cb->client_data, (XPointer)&call_data);
    } else {
	XIC_GUI(ic, change_lookup)((XIC)ic, LOOKUP_START,
				   (XPointer)&call_data);
    }

    return IIIMF_STATUS_SUCCESS;
}

static IIIMF_status
draw_lookup_choice(
    XicCommon ic
)
{
    IIIMF_status st;
    IIIMCF_lookup_choice lc;
    IIIMCF_text title, label, cand;
    XIMLookupDrawCallbackStruct2 call_data;
    XIMChoiceObject2 choice_object;
    XIMText *choice_list = (XIMText*)NULL, *label_list = (XIMText*)NULL;
    XIMText title_text;
    IMFeedbackList **choice_feedback = (IMFeedbackList**)NULL;
    IMFeedbackList **label_feedback = (IMFeedbackList**)NULL;
    XimCommon im = (XimCommon)ic->core.im;
    Bool useUnicode;
    int size, flag;
    int i;

    st = iiimcf_get_lookup_choice(XIC_IIIMP(ic, context), &lc);
    if (st != IIIMF_STATUS_SUCCESS) return st;
    st = iiimcf_get_lookup_choice_size(lc, &size,
				       &call_data.first_index,
				       &call_data.last_index,
				       &call_data.current_index);
    if (st != IIIMF_STATUS_SUCCESS) return st;
    if (size <= 0) return IIIMF_STATUS_SUCCESS;
    st = iiimcf_get_lookup_choice_title(lc, &title);
    if (st != IIIMF_STATUS_SUCCESS) return st;

    useUnicode = (XIM_USE_UNICODE(im) &&
		  ic->lookup_draw_callback.callback);

    choice_list = (XIMText*) Xmalloc(sizeof(XIMText) * size * 2);
    if (!choice_list) {
	st = IIIMF_STATUS_MALLOC;
	goto cleanup;
    }
    memset(choice_list, 0, sizeof(XIMText) * size * 2);
    label_list = choice_list + size;

    choice_feedback = (IMFeedbackList**) Xmalloc(sizeof(IMFeedbackList*) * size * 2);
    if (!choice_feedback) {
	st = IIIMF_STATUS_MALLOC;
	goto cleanup;
    }
    memset(choice_feedback, 0, sizeof(IMFeedbackList*) * size * 2);
    label_feedback = choice_feedback + size;

    memset(&title_text, 0, sizeof(XIMText));
    st = IIimpConvertToXIMText(title, &title_text, NULL, useUnicode);
    if (st != IIIMF_STATUS_SUCCESS) goto cleanup;

    for (i = 0; i < size; i++) {
	st = iiimcf_get_lookup_choice_item(lc, i, &cand, &label, &flag);
	if (st != IIIMF_STATUS_SUCCESS) goto cleanup;
	st = IIimpConvertToXIMText(cand, &choice_list[i],
				   &choice_feedback[i], useUnicode);
	if (st != IIIMF_STATUS_SUCCESS) goto cleanup;
	st = IIimpConvertToXIMText(label, &label_list[i],
				   &label_feedback[i], useUnicode);
	if (st != IIIMF_STATUS_SUCCESS) goto cleanup;
    }

    call_data.n_choices = size;
    choice_object.label_decoration = NO_DECORATION;
    choice_object.value = choice_list;
    choice_object.label = label_list;
    choice_object.value_feedback = choice_feedback;
    choice_object.label_feedback = label_feedback;
    call_data.choices = &choice_object;
    call_data.title = &title_text;

    if (ic->lookup_draw_callback.callback) {
	XIMCallback *cb = &ic->lookup_draw_callback;
	(*cb->callback)((XIC)ic, cb->client_data, (XPointer)&call_data);
    } else {
	XIC_GUI(ic, change_lookup)((XIC)ic, LOOKUP_DRAW,
				   (XPointer)&call_data);
    }

cleanup:

    for (i = 0; i < size; i++) {
	if (choice_list) {
	    if (choice_feedback) {
		FreeFeedbackList(choice_feedback[i], choice_list[i].length);
	    }
	    if (choice_list[i].string.multi_byte) {
		Xfree(choice_list[i].string.multi_byte);
	    }
	    if (choice_list[i].feedback) {
		Xfree(choice_list[i].feedback);
	    }
	}
	if (label_list) {
	    if (label_feedback) {
		FreeFeedbackList(label_feedback[i], label_list[i].length);
	    }
	    if (label_list[i].string.multi_byte) {
		Xfree(label_list[i].string.multi_byte);
	    }
	    if (label_list[i].feedback) {
		Xfree(label_list[i].feedback);
	    }
	}
    }
    if (choice_list) Xfree(choice_list);
    if (choice_feedback) Xfree(choice_feedback);

    if (title_text.string.multi_byte) {
	Xfree(title_text.string.multi_byte);
    }
    if (title_text.feedback) {
	Xfree(title_text.feedback);
    }

    return st;
}

static IIIMF_status
end_lookup_choice(
    XicCommon ic
)
{
    if (ic->lookup_done_callback.callback) {
	XIMCallback *cb = &ic->lookup_done_callback;
	(*cb->callback)((XIC)ic, cb->client_data, (XPointer)NULL);
    } else {
	XIC_GUI(ic, change_lookup)((XIC)ic, LOOKUP_DONE, (XPointer)NULL);
    }

    return IIIMF_STATUS_SUCCESS;
}

static IIIMF_status
commit(
    XicCommon ic
)
{
    IIIMF_status st;
    IIIMCF_text text;
    XEvent ev;
    Bool useUnicode;
    XimCommon im = (XimCommon)ic->core.im;

    useUnicode = (XIM_USE_UNICODE(im) &&
		  ic->commit_string_callback.callback);

    /* private XIC extension */
    if (ic->commit_string_callback.callback) {
	XIMText cbtext;
	XIMCallback *cb = &ic->commit_string_callback;

	st = iiimcf_get_committed_text(XIC_IIIMP(ic, context), &text);
	if (st != IIIMF_STATUS_SUCCESS) return st;
	st = IIimpConvertToXIMText(text, &cbtext, NULL, useUnicode);
	if (st != IIIMF_STATUS_SUCCESS) return st;
	(*cb->callback)((XIC)ic,
			cb->client_data,
			(XPointer)&cbtext);	/* for UNICODE1 */
	/* no need to fake a keyevent to be putback */
	if (cbtext.string.multi_byte) Xfree(cbtext.string.multi_byte);
	if (cbtext.feedback) Xfree(cbtext.feedback);
	return IIIMF_STATUS_SUCCESS;
    }

    /* fake a key press event */
    memset(&ev, 0, sizeof(ev));
    ev.type = (int)KeyPress;
    ev.xkey.display = im->core.display;
    ev.xkey.window = ic->core.focus_window;
    ev.xkey.root = None;
    ev.xkey.subwindow = None;
    ev.xkey.keycode = XIM_COMPOSE_KEYCODE;
    ev.xkey.same_screen = True;
    /* XPutBackEvent(im->core.display, &ev); */
    /* ic->ximp_icpart->putback_key_event = True; */
    HoldXKeyEvent(ic, &ev);
    XIC_IIIMP(ic, keysym_return) = NoSymbol;
    XIC_IIIMP(ic, has_keyevent_return) = False;
    XIC_IIIMP(ic, committed_text_consumed) = False;

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
IMProcessIncomingEvent(
    XicCommon ic
)
{
    IIIMF_status st;
    IIIMCF_event ev;
    IIIMCF_event_type et;
    IIIMCF_context c;

    if (!ic) return IIIMF_STATUS_SUCCESS;

    c = XIC_IIIMP(ic, context);
    while ((st = iiimcf_get_next_event(c, &ev))
	   == IIIMF_STATUS_SUCCESS) {
	st = iiimcf_dispatch_event(c, ev);
	if (st != IIIMF_STATUS_SUCCESS) {
	    if (iiimcf_get_event_type(ev, &et) != IIIMF_STATUS_SUCCESS)
		continue;
	    switch (et) {
	      case IIIMCF_EVENT_TYPE_KEYEVENT:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_KEYEVENT\n"));
	       process_keyevent(ic, ev);
	       break;

	      case IIIMCF_EVENT_TYPE_TRIGGER_NOTIFY:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_TRIGGER_NOTIFY\n"));
	       process_trigger_notify(ic, ev);
	       ic_switcher_set_conversion_mode (ic);
	       break;

	      case IIIMCF_EVENT_TYPE_UI_PREEDIT_START:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_UI_PREEDIT_START\n"));
	       CallPreeditStartCallback(ic, NULL);
	       break;
	      case IIIMCF_EVENT_TYPE_UI_PREEDIT_CHANGE:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_UI_PREEDIT_CHANGE\n"));
	       draw_preedit(ic);
	       break;
	      case IIIMCF_EVENT_TYPE_UI_PREEDIT_DONE:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_UI_PREEDIT_DONE\n"));
	       CallPreeditDoneCallback(ic, NULL);
	       break;

	      case IIIMCF_EVENT_TYPE_UI_STATUS_START:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_UI_STATUS_START\n"));
	       CallStatusStartCallback(ic, NULL);
	       break;
	      case IIIMCF_EVENT_TYPE_UI_STATUS_CHANGE:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_UI_STATUS_CHANGE\n"));
	       draw_status(ic);
	       break;
	      case IIIMCF_EVENT_TYPE_UI_STATUS_DONE:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_UI_STATUS_DONE\n"));
	       CallStatusDoneCallback(ic, NULL);
	       break;

	      case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_START:
	       start_lookup_choice(ic);
	       break;
	      case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_CHANGE:
	       draw_lookup_choice(ic);
	       break;
	      case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_DONE:
	       end_lookup_choice(ic);
	       break;

	      case IIIMCF_EVENT_TYPE_AUX_START:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_AUX_START\n"));
	       IIimpAuxStart(ic, ev);
	       break;
	      case IIIMCF_EVENT_TYPE_AUX_DRAW:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_AUX_DRAW\n"));
	       IIimpAuxDraw(ic, ev);
	       break;
	      case IIIMCF_EVENT_TYPE_AUX_DONE:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_AUX_DONE\n"));
	       IIimpAuxDone(ic, ev);
	       break;

	      case IIIMCF_EVENT_TYPE_AUX_GETVALUES:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_AUX_GETVALUES\n"));
	       IIimpAuxGetValuesReply(ic, ev);
	       break;
	      case IIIMCF_EVENT_TYPE_UI_COMMIT:
	       TRACE_MESSAGE('i', ("IIIMCF_EVENT_TYPE_UI_COMMIT\n"));
	       commit(ic);
	       break;
	    }
	    /* discard event */
	    iiimcf_ignore_event(ev);
	}
    }

    PutBackXKeyEvent(ic);

    return IIIMF_STATUS_SUCCESS;
}

Bool
IMTriggerNotify(
    XicCommon ic,
    XICConversionMode conv_mode
)
{
    IIIMCF_event ev;
    if (!ic) return False;

    if (iiimcf_create_trigger_notify_event(conv_mode, &ev)
	!= IIIMF_STATUS_SUCCESS) return False;

    if (iiimcf_forward_event(XIC_IIIMP(ic, context), ev)
	!= IIIMF_STATUS_SUCCESS) return False;
    IMProcessIncomingEvent(ic);

    return True;
}

Bool
IMForwardEvent(
    XicCommon ic,
    XEvent *ev
)
{
    IIIMF_status st;
    XimCommon im = (XimCommon)ic->core.im;
    IIIMCF_keyevent kev;
    IIIMCF_event event;

    if (!XKeyEvent_To_IIIMCF_keyevent((XKeyEvent*) ev, &kev))
	return True;

#if 0 /* SHOULD BE REMOVED */
    fprintf(stderr, "(%d, %d)->(%d, %d, %d)\n",
	    ev->xkey.keycode, ev->xkey.state,
	    kev.keycode, kev.keychar, kev.modifier);
#endif

    if (IsConversionMode(ic)) {
	if (isConversionOffKey(im, ev)) {
	    IMTriggerNotify(ic, CONV_OFF);
	    SetConversionMode(ic, False);
	    return True;
	}
    } else {
	if (isConversionOnKey(im, ev)) {
	    SetConversionMode(ic, True);
	    if (IMTriggerNotify(ic, CONV_ON)) {
		XIC_IIIMP(ic, switch_methods)->set_focus((XIC)ic);
	    }
	    return True;
	}
    }

    st = iiimcf_create_keyevent(&kev, &event);
    if (st != IIIMF_STATUS_SUCCESS) return False;

    st = iiimcf_forward_event(XIC_IIIMP(ic, context), event);

    IMProcessIncomingEvent(ic);

    if (st != IIIMF_STATUS_SUCCESS) return False;

    return True;
}

IIIMF_status
IMChangeFocus(
    XicCommon ic,
    int flag
)
{
    IIIMF_status st;
    IIIMCF_event event;

    if (flag) {
	st = iiimcf_create_seticfocus_event(&event);
	IIimpAuxSetICFocus(ic);
    } else {
	st = iiimcf_create_unseticfocus_event(&event);
	IIimpAuxUnsetICFocus(ic);
    }
    if (st != IIIMF_STATUS_SUCCESS) return st;

    st = iiimcf_forward_event(XIC_IIIMP(ic, context), event);
    IMProcessIncomingEvent(ic);

    return st;
}

void
IM_free_langlist(
    XimCommon im
)
{
    int i;
    int n = XIM_IIIMP(im, count_languages);
    XIMText *language_list = XIM_IIIMP(im, supported_languages);
    char *str;

    if (language_list) {
	for (i = 0; i < n; i++) {
	    str = language_list[i].string.multi_byte;
	    if (str) free(str);
	}
	free(language_list);
    }
    XIM_IIIMP(im, count_languages) = 0;
    XIM_IIIMP(im, supported_languages) = NULL;
}

static void
init_langlist(
    XimCommon im
)
{
    int i, n, len;
    const char *ll;
    char *ll2;
    IIIMCF_language *plangs;
    XIMText *language_list = NULL;

    IM_free_langlist(im);
    if (iiimcf_get_supported_languages(XIM_IIIMP(im, handle), &n, &plangs)
	!= IIIMF_STATUS_SUCCESS)
	return;

    language_list = (XIMText*) Xmalloc(sizeof(XIMText) * n);
    if (!language_list) return;
    memset(language_list, 0, sizeof(XIMText) * n);
    XIM_IIIMP(im, count_languages) = n;
    XIM_IIIMP(im, supported_languages) = language_list;
    for (i = 0; i < n; i++) {
	iiimcf_get_language_id(plangs[i], &ll);
	len = strlen(ll);
	ll2 = (char*) Xmalloc(sizeof(char) * (len + 1));
	if (!ll2) {
	    IM_free_langlist(im);
	    return;
	}
	memcpy(ll2, ll, sizeof(char) * (len + 1));
	language_list[i].length = len;
	language_list[i].string.multi_byte = ll2;
    }

    return;
}

static IIIMF_status
IMSetupAuxObject(
    XimCommon im
);

IIIMF_status
IMCreateHandle(
    XimCommon im
)
{
    IIIMF_status st;
    IIIMCF_attr attr;
    char *address, *service;

    address = NULL;

    st = iiimcf_create_attr(&attr);
    if (st != IIIMF_STATUS_SUCCESS) return st;
    if (!XIM_IIIMP(im, client_type)) {
	XIM_IIIMP(im, client_type) = IIIMP_CLIENT_TYPE; /* default */
    }
    st = iiimcf_attr_put_string_value(attr, IIIMCF_ATTR_CLIENT_TYPE,
				      XIM_IIIMP(im, client_type));
    if (st != IIIMF_STATUS_SUCCESS) goto cleanup;
    if (im->core.display) {
	Display *dpy = im->core.display;

	st = iiimcf_attr_put_string_value(attr, IIIMCF_ATTR_CLIENT_X_DISPLAY_NAME,
					  XDisplayString(dpy));
	if (st != IIIMF_STATUS_SUCCESS) goto cleanup;
	st = iiimcf_attr_put_string_value(attr, IIIMCF_ATTR_CLIENT_X_SERVER_VENDOR,
					  XServerVendor(dpy));
	if (st != IIIMF_STATUS_SUCCESS) goto cleanup;
    }
    

    if (im->core.im_name) {
	address = strdup(im->core.im_name);
	if (!address) goto cleanup;
	service = index(address, ':');
	if (service) {
	    *service = '\0';
	    service++;
	}
	st = iiimcf_attr_put_string_value(attr, IIIMCF_ATTR_SERVER_ADDRESS,
					  address);
	if (st != IIIMF_STATUS_SUCCESS) goto cleanup;
	if (service) {
	    st = iiimcf_attr_put_string_value(attr, IIIMCF_ATTR_SERVER_SERVICE,
					      service);
	    if (st != IIIMF_STATUS_SUCCESS) goto cleanup;
	}
    }
    st = iiimcf_create_handle(attr, &XIM_IIIMP(im, handle));
    if (st != IIIMF_STATUS_SUCCESS) goto cleanup;
    init_langlist(im);
    RegisterTriggerKeys(im);
    IMSetupAuxObject(im);

cleanup:
    iiimcf_destroy_attr(attr);
    if (address) free(address);
    return st;
}

IIIMF_status
IMCreateIC(
    XimCommon im,
    IIIMCF_context *pc
)
{
    IIIMF_status st;
    IIIMCF_attr attr;
    IIIMCF_language lang;
    
    st = iiimcf_create_attr(&attr); 
    if (st != IIIMF_STATUS_SUCCESS) return st;
    st = iiimcf_attr_put_integer_value(attr,
				       IIIMCF_ATTR_DISABLE_AUTOMATIC_TRIGGER_NOTIFY,
				       1);
    if (st != IIIMF_STATUS_SUCCESS) goto exit;
    lang = get_IM_language(im);
    if (lang) {
	st = iiimcf_attr_put_ptr_value(attr,
				       IIIMCF_ATTR_INPUT_LANGUAGE,
				       lang);
	if (st != IIIMF_STATUS_SUCCESS) goto exit;
    }

    if (XIM_IIIMP(im, engine_name)) {
	st = iiimcf_attr_put_string_value(attr,
					  IIIMCF_ATTR_INPUT_METHOD_NAME,
					  XIM_IIIMP(im, engine_name));
	if (st != IIIMF_STATUS_SUCCESS) goto exit;
    }

    st = iiimcf_create_context(XIM_IIIMP(im, handle), attr, pc);
    XIM_IIIMP(im, inited) = True;

exit:
    iiimcf_destroy_attr(attr);
    return st;
}

Bool
IMSetPrimaryLocale(
    XicCommon ic,
    char *input_locale
)
{
    IIIMCF_context c;
    char *orig;
    XimCommon im = (XimCommon)ic->core.im;

    orig = XIM_IIIMP(im, primary_locale);
    XIM_IIIMP(im, primary_locale) = input_locale;
    if (IMCreateIC(im, &c) != IIIMF_STATUS_SUCCESS) {
	XIM_IIIMP(im, primary_locale) = orig;
	return False;
    }
    iiimcf_destroy_context(XIC_IIIMP(ic, context));
    XIC_IIIMP(ic, context) = c;

    return True;
}

Bool
IMChangeClientType(
    XimCommon im,
    char *client_type
)
{
    IIIMCF_handle h;

    XIM_IIIMP(im, client_type) = client_type;
    if (XIM_IIIMP(im, inited)) {
	/* im has already had context,
	   so we cannot reset handle. */
	return False;
    }
    h = XIM_IIIMP(im, handle);
    if (IMCreateHandle(im) != IIIMF_STATUS_SUCCESS)
	return False;
    iiimcf_destroy_handle(h);

    return True;
}

/********************************************************************************
                       AUX load/start/done/draw/setvalues
 ********************************************************************************/

static IIIMF_status
IMSetupAuxObject(
    XimCommon im
)
{
    IIIMF_status st;
    IIIMCF_handle h;
    const IIIMCF_object_descriptor *pdesc;
    const IIIMCF_object_descriptor **ppdescs;
    IIIMCF_downloaded_object *pobjs;
    int i, n1, n2;

    h = XIM_IIIMP(im, handle);
    st = iiimcf_get_object_descriptor_list(h, &n1, &pdesc);
    if (st != IIIMF_STATUS_SUCCESS) return st;
    pobjs = (IIIMCF_downloaded_object*) malloc(sizeof(*pobjs) * n1);
    if (!pobjs) return IIIMF_STATUS_MALLOC;
    ppdescs = (const IIIMCF_object_descriptor**) malloc(sizeof(*ppdescs) * n1);
    if (!ppdescs) {
	free(pobjs);
	return IIIMF_STATUS_MALLOC;
    }

    for (i = 0, n2 = 0; i < n1; i++, pdesc++) {
	if (pdesc->predefined_id == IIIMP_IMATTRIBUTE_BINARY_GUI_OBJECT) {
	    ppdescs[n2++] = pdesc;
	}
    }
    if (n2 > 0) {
	st =  iiimcf_get_downloaded_objects(h, n2, ppdescs, pobjs);
	if (st != IIIMF_STATUS_SUCCESS) {
	    free(ppdescs);
	    free(pobjs);
	    return st;
	}
	for (i = 0; i < n2; i++) {
	    IIimpAuxDownload(pobjs[i]);
	}
    }
    free(ppdescs);
    free(pobjs);

    return IIIMF_STATUS_SUCCESS;
}

Bool
IMAuxSetValues(
    XicCommon ic,
    IIIMCF_event ev
)
{
    if (!ic) return False;

    if (iiimcf_forward_event(XIC_IIIMP(ic, context), ev)
	!= IIIMF_STATUS_SUCCESS) return False;
    IMProcessIncomingEvent(ic);

    return True;
}

Bool
IMAuxGetValues(
    XicCommon ic,
    IIIMCF_event ev
)
{
    if (!ic) return False;

    if (iiimcf_forward_event(XIC_IIIMP(ic, context), ev)
	!= IIIMF_STATUS_SUCCESS) return False;
    IMProcessIncomingEvent(ic);

    return True;
}

void
ic_change_conversion_mode (XicCommon ic, char *conv_mode)
{
  IIIMCF_event ev;
  if (conv_mode && !strcmp ((char*)conv_mode, "on"))
    {
      IIIMF_status st;
      st = iiimcf_create_trigger_notify_event (CONV_ON, &ev);
      if (st != IIIMF_STATUS_SUCCESS)
	return;
    }
  else if (conv_mode && !strcmp ((char*)conv_mode, "off"))
    {
      IIIMF_status st;
      st = iiimcf_create_trigger_notify_event (CONV_OFF, &ev);
      if (st != IIIMF_STATUS_SUCCESS)
	return;
    }
  if (iiimcf_forward_event(XIC_IIIMP(ic, context), ev) != IIIMF_STATUS_SUCCESS)
    return;
  IMProcessIncomingEvent(ic);
}

void
ic_initialize_with_input_language (XicCommon ic, char *new_lang)
{
  IIIMCF_language iiim_lang = NULL;
  IIIMF_status st;
  IIIMCF_attr attr;
  IIIMCF_event ev;
  int conversion_mode = CONV_OFF;
  IIIMCF_context c;
  XimCommon im = (XimCommon)ic->core.im;
  char *encoding = nl_langinfo(CODESET);

  if (XIC_IIIMP(ic, context) == NULL)
    return;

  if (strcmp(encoding, "UTF-8"))
    /* cannot support input language switch in non-UTF-8 locale*/
    return;

  if (new_lang)
    {
      iiim_lang = get_input_language (ic, new_lang, True);
      if (iiim_lang != NULL)
	{
	  Xfree (ic->current_language);
	  ic->current_language = strdup (new_lang);
	}
      else
	/* invalid new lang */
	return;
    }
  else
    iiim_lang = get_input_language (ic, ic->current_language, False);

  /* save conversion mode to restore this after switching */
  st = iiimcf_get_current_conversion_mode (XIC_IIIMP(ic, context),
					   &conversion_mode);
#if 0
  /* to cancel any remaining preedit text */
  if (!context_iiim->finalizing)
    g_signal_emit_by_name (context_iiim, "preedit_changed");

  /* clear candidate if any */
  if (context_iiim->candidate_start == TRUE)
    {
      iiim_destroy_candidate_window (context_iiim);
      context_iiim->candidate_start = FALSE;
    }
#endif

  st = iiimcf_destroy_context(XIC_IIIMP(ic, context));
  XIC_IIIMP(ic, context) = NULL;

  st = iiimcf_create_attr (&attr);
  if (st != IIIMF_STATUS_SUCCESS)
    return;
  iiimcf_attr_put_ptr_value (attr, IIIMCF_ATTR_INPUT_LANGUAGE,
			     iiim_lang);
  st = iiimcf_create_context(XIM_IIIMP(im, handle), attr, &c);
  XIC_IIIMP(ic, context) = c;
  iiimcf_destroy_attr (attr);
  if (st != IIIMF_STATUS_SUCCESS)
    return;

  st = iiimcf_create_seticfocus_event (&ev);

  if (st != IIIMF_STATUS_SUCCESS)
    return;

  iiimcf_forward_event(XIC_IIIMP(ic, context), ev);
  if (conversion_mode)
    {
      st = iiimcf_create_trigger_notify_event (CONV_ON, &ev);
      if (st != IIIMF_STATUS_SUCCESS)
	return;
      iiimcf_forward_event(XIC_IIIMP(ic, context), ev);
    }
  IMProcessIncomingEvent(ic);
}

/* get Lang List */
IIIMCF_language*
iiim_get_languages (XimCommon im, int *n_lang)
{
  IIIMF_status st;
  IIIMCF_handle h = XIM_IIIMP(im, handle);
  IIIMCF_language *lang = NULL;

  if (!h)
    return NULL;

  st = iiimcf_get_supported_languages (h, n_lang, &lang);
  if (st != IIIMF_STATUS_SUCCESS)
    return NULL;

  return lang;
}

static IIIMCF_language
get_input_language (XicCommon ic, char *input_language, Bool exact_match)
{
  int i;
  IIIMF_status st;
  char *langid;
  int n_lang;
  IIIMCF_language *lang_list;
  XimCommon im = (XimCommon)ic->core.im;

  lang_list = iiim_get_languages (im, &n_lang);
  if (lang_list)
    {
      int match_len;
      char *p;
      for (i = 0; i < n_lang; i++)
	{
	  st = iiimcf_get_language_id (lang_list[i],
				       (const char **)&langid);
	  if (st != IIIMF_STATUS_SUCCESS)
	    continue;
	  if (!strncmp (langid, input_language, strlen (langid)))
	    {
	      Xfree (ic->current_language);
	      ic->current_language = strdup (langid);
	      return lang_list[i];
	    }
	}

      if (exact_match)
	return NULL;
      /*
	cannot find exact match, so try a fuzzy matching
	for <lang_region> part of the input_language
      */
      if (strstr (input_language, ".") == NULL &&
	  strstr (input_language, "_") == NULL)
	{
	  for (i = 0; i < n_lang; i++)
	    {
	      st = iiimcf_get_language_id (lang_list[i],
					   (const char **)&langid);
	      if (st != IIIMF_STATUS_SUCCESS)
		continue;
	      if (!strncmp (langid, input_language, strlen (input_language)))
		{
		  Xfree (ic->current_language);
		  ic->current_language = strdup (langid);
		  return lang_list[i];
		}
	    }
	}
      else
	{
	  if ((p = strstr (input_language, ".")) != NULL)
	    {
	      match_len = p - input_language;
	      if (match_len > 0)
		{
		  for (i = 0; i < n_lang; i++)
		    {
		      st = iiimcf_get_language_id (lang_list[i],
						   (const char **)&langid);
		      if (st != IIIMF_STATUS_SUCCESS)
			continue;
		      if (!strncmp (langid, input_language, match_len))
			{
			  Xfree (ic->current_language);
			  ic->current_language = strdup (langid);
			  return lang_list[i];
			}
		    }
		}
	    }
	  /*
	    cannot find a match yet, so try another fuzzy matching
	    for only <lang> part of the input_language
	  */
	  if ((p = strstr (input_language, "_")) != NULL)
	    {
	      match_len = p - input_language;
	      if (match_len > 0)
		{
		  for (i = 0; i < n_lang; i++)
		    {
		      st = iiimcf_get_language_id (lang_list[i],
						   (const char **)&langid);
		      if (st != IIIMF_STATUS_SUCCESS)
			continue;
		      if (!strncmp (langid, input_language, match_len))
			{
			  Xfree (ic->current_language);
			  ic->current_language = strdup (langid);
			  return lang_list[i];
			}
		    }
		}
	    }
	}
      /*
	still cannot find a match. Just take one for "en" as
	a default.
      */
      for (i = 0; i < n_lang; i++)
	{
	  st = iiimcf_get_language_id (lang_list[i],
				       (const char **)&langid);
	  if (st != IIIMF_STATUS_SUCCESS)
	    continue;
	  if (!strncmp (langid, "en", 2))
	    {
	      Xfree (ic->current_language);
	      ic->current_language = strdup (langid);
	      return lang_list[i];
	    }
	}
      /*
	I've given up. Use whatever the 1st in the list as the fallback.
      */
      st = iiimcf_get_language_id (lang_list[0],
				   (const char **)&langid);
      if (st == IIIMF_STATUS_SUCCESS)
	{
	  Xfree (ic->current_language);
	  ic->current_language = strdup (langid);
	  return lang_list[0];
	}
    }
  return NULL;
}

/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */
