/*
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 "iiimpIM.h"
#include "switchIM.h"
#include "guiIM.h"
#include "XimpIm.h"
#include "iiimpIC.h"
#include "status.h"
#include <X11/keysym.h>		/* For IsModifierKey marco */
#include <X11/Xutil.h>		/* For IsModifierKey marco */
#include "iiimpUtil.h"
#include "iiimpSwitcher.h"

#include "status.h"

extern char *SetICValueData(
    XicCommon ic,
    XIMArg *values,
    XICSetMode mode,
    XimpChangeaMask change_mask
);
extern char *GetICValueData(
    XicCommon ic,
    XIMArg *values,
    XimpChangeaMask change_mask
);

static void IIIMP_DestroyIC(
    XIC xic
);
static void IIIMP_SetFocus(
    XIC xic
);
static void IIIMP_UnSetFocus(
    XIC xic
);
static char *IIIMP_MbReset(
    XIC xic
);
static wchar_t *IIIMP_WcReset(
    XIC xic
);
extern char *IIIMP_SetICValues(
    XIC xic,
    XIMArg *arg
);
extern char *IIIMP_GetICValues(
    XIC xic,
    XIMArg *arg
);
static int IIIMP_MbLookupString(
    XIC xic,
    XKeyEvent *ev,
    char *buffer,
    int len,
    KeySym *keysym,
    Status *status
);
static int IIIMP_WcLookupString(
    XIC xic,
    XKeyEvent *ev,
    wchar_t *buffer,
    int len,
    KeySym *keysym,
    Status *status
);

extern IIIMCF_language *iiim_get_languages (XimCommon im, int *n_lang);

extern IIIMF_status IIimpConvertToString(IIIMCF_text text,
					 Bool useUnicode,
					 char **pstr);
static XICMethods
get_iiimp_ic_methods()
{
    static XICMethodsRec icm;
    if (!icm.destroy) {
	icm.destroy = IIIMP_DestroyIC;
	icm.set_focus = IIIMP_SetFocus;
	icm.unset_focus = IIIMP_UnSetFocus;
	icm.set_values = IIIMP_SetICValues;
	icm.get_values = IIIMP_GetICValues;
	icm.mb_reset = IIIMP_MbReset;
	icm.wc_reset = IIIMP_WcReset;
	icm.mb_lookup_string = IIIMP_MbLookupString;
	icm.wc_lookup_string = IIIMP_WcLookupString;
    }
    return &icm;
}

static void SWITCH_DestroyIC(XIC);

static
XICMethods
get_switch_ic_methods()
{
  static XICMethodsRec icm;
  if (!icm.destroy) {
    icm.destroy = SWITCH_DestroyIC;
    icm.set_focus = IIIMP_SetFocus;
    icm.unset_focus = IIIMP_UnSetFocus;
  }
  return &icm;
}

Bool
IIIMP_Local_KeyFilter(
    Display *d /*unused*/,
    Window w /* unused */,
    XEvent *ev,
    XPointer client_data
);

void
ChangePreeditState(
    XicCommon ic
)
{
    if (ic->core.preedit_attr.preedit_state == XIMPreeditEnable &&
	!IsConversionMode(ic)) {
	SetConversionMode(ic, True);
	IMTriggerNotify(ic, CONV_ON);
    }
    if (ic->core.preedit_attr.preedit_state == XIMPreeditDisable &&
	IsConversionMode(ic)) {
	IMTriggerNotify(ic, CONV_OFF);
	SetConversionMode(ic, False);
    }
    return;
}

void
SetConversionMode(
    XicCommon ic,
    int mode
)
{
    XIC_IIIMP(ic, filtered) = mode;

    ic->core.preedit_attr.preedit_state = ((mode == True) ?
					   XIMPreeditEnable :
					   XIMPreeditDisable);
    if ((int)ic->core.preedit_attr.state_notify_callback.callback) {
	XIMPreeditStateNotifyCallbackStruct call_data;
	call_data.state = ic->core.preedit_attr.preedit_state;
	ic->core.preedit_attr.state_notify_callback.callback(
	    (XIC)ic,
	    ic->core.preedit_attr.state_notify_callback.client_data,
	    (XPointer)&call_data
	);
    }
    if (XIM_IS_SWITCH(ic->core.im)) {
	if (mode == False) {		/* CONV_OFF */
	    ResetSwitchFilter(ic);
	    Ximp_Local_Status_Set(ic);
	    Ximp_Local_Status_Draw(ic);
	} else {
	    ic->active_filter = IIIMP_Local_KeyFilter;
	    ic->active_methods = get_iiimp_ic_methods();
	}
    }
}

static SwitchMode
SwitchFilter(
    XicCommon ic,
    XEvent *ev
)
{
  XimCommon im = (XimCommon)ic->core.im;
  if (IsConversionMode(ic)) {
    if (isConversionOffKey(im, ev)) {
      return Switch_OFF;
    }
  } else {
    if (isConversionOnKey(im, ev)) {
      return Switch_ON;
    }
  }
  return Switch_NOP;
}

Bool
IIIMP_Local_KeyFilter(
    Display *d /*unused*/,
    Window w /* unused */,
    XEvent *ev,
    XPointer client_data
)
{
    XicCommon ic = (XicCommon)client_data;

    if (!IsIMEnabled())
      return False;

    if (ic->ximp_icpart == NULL) {
	/* most likely associated ic has been already destroyed */
	_XUnregisterFilter(d, w, IIIMP_Local_KeyFilter, (XPointer)ic);
	return False;
    }
    if (ev->xkey.keycode == XIM_COMPOSE_KEYCODE) {
	return False;
    }
    if (0 < XIC_IIIMP(ic, key_event_num)) {
	XIC_IIIMP(ic, key_event_num) -= 1;
	return False;
    }

    if (KeyRelease == ev->xkey.type) {
	XIMCallback *cb = &ic->forward_event_callback;
	if (NULL != cb->callback) {
	    (*cb->callback)((XIC)ic, cb->client_data, (XPointer)ev);
	    return True;
	} else {
	    return IsConversionMode(ic);
	}
    }

    return IMForwardEvent(ic, ev);
}

extern char *im_canonicalize_langname(XimCommon im);

/* Public functions */
XIC
CreateIC(
    XimCommon im,
    XIMArg *arg
)
{
    XicCommon ic = (XicCommon)NULL;

    ic = (XicCommon)Xmalloc(sizeof(XicCommonRec));
    if (ic == (XicCommon)NULL) goto Error;
    memset(ic, 0, sizeof(XicCommonRec));

    ic->core.im = (XIM)im;
    ic->methods = get_iiimp_ic_methods();

    if (!CommonCreateIC((XIC)ic, arg)) goto Error;
    
    if (!IIIMP_CreateIC_SWITCH((XIC)ic, arg)) goto Error;

    ic->current_language = im_canonicalize_langname(im);

    _XRegisterFilterByType(ic->core.im->core.display, ic->core.focus_window,
			   KeyPress, KeyRelease,
			   IIIMP_Local_KeyFilter, (XPointer)ic);
    return (XIC)ic;
Error:
    if (ic) Xfree(ic);
    return 0;
}

Status
IIIMP_CreateIC_SWITCH(
    XIC xic,
    XIMArg *arg
)
{
    int id;
    XicCommon ic = (XicCommon)xic;
    XimCommon im = (XimCommon)ic->core.im;
    XimpChangeMaskRec dummy;

    ic->core.filter_events = KeyPressMask|ExposureMask|
	ButtonPressMask |PointerMotionMask;
    if (!(ic->iiimp_icpart = Xmalloc(sizeof(XICIIimpIMRec)))) {
	goto Error;
    }
    memset(ic->iiimp_icpart, 0, sizeof(XICIIimpIMRec));
    XIC_IIIMP(ic, switch_methods) = get_switch_ic_methods();
    if (IMCreateIC(im, &XIC_IIIMP(ic, context))
	!= IIIMF_STATUS_SUCCESS) {
	goto Error;
    }
    id = XIM_IIIMP(im, counter);
    XIC_IIIMP(ic, id) = id;
    XIM_IIIMP(im, counter) = id + 1;
    SetConversionMode(ic, False);

    memset(&dummy, 0, sizeof(dummy));
    if (SetICValueData(ic, arg, CREATE_IC, &dummy)) {
	goto Error;
    }

    /* switch */
    if (XIM_IS_SWITCH(im)) {
	RegisterSwitchFilter(ic, SwitchFilter,
			     IIIMP_Local_KeyFilter,
			     get_iiimp_ic_methods());
    }

    return True;
Error:
    if (ic->iiimp_icpart)
	Xfree(ic->iiimp_icpart);
    return False;
}

static void
IIIMP_DestroyIC(
    XIC xic
)
{
    XicCommon ic = (XicCommon)xic;

    _XUnregisterFilter(ic->core.im->core.display, ic->core.focus_window,
		       IIIMP_Local_KeyFilter, (XPointer)ic);
    if (ic->current_language)
      Xfree (ic->current_language);
    IIimpDestroryAuxData(ic);
    SWITCH_DestroyIC(xic);
    CommonDestroyIC(xic);
    return;
}

extern Bool GetIMSettings (XicCommon ic);

static void
IIIMP_SetFocus(
    XIC xic
)
{
    XicCommon ic = (XicCommon)xic;
    XimCommon im = (XimCommon)ic->core.im;

    if (XIM_IIIMP(im, current_ic) == xic) {
	IIIMP_UnSetFocus(xic);
    }
    XIM_IIIMP(im, current_ic) = xic;

    XIC_GUI(ic, change_lookup)((XIC)ic, LOOKUP_SETFOCUS, NULL);

    IMChangeFocus(ic, 1);

    ic_switcher_set_focus (ic);

    if (im_switcher_active (im))
      {
	IIIMCF_language *lang_list;
	int n_lang;

	lang_list = iiim_get_languages (im, &n_lang);
	ic_switcher_set_language_list (ic, lang_list, n_lang);
	ic_switcher_set_input_language (ic, NULL);
      }

    GetIMSettings (ic);

    if (!XIM_IS_SWITCH(ic->core.im)) {
	_XRegisterFilterByType(ic->core.im->core.display, ic->core.focus_window,
			       KeyPress, KeyRelease,
			       IIIMP_Local_KeyFilter, (XPointer)ic);
    }
    return;
}

static void
IIIMP_UnSetFocus(
    XIC xic
)
{
    XicCommon ic = (XicCommon)xic;
    XimCommon im = (XimCommon)ic->core.im;

    XIM_IIIMP(im, current_ic) = (XIC)NULL;

    IMChangeFocus(ic, 0);

    _XUnregisterFilter(ic->core.im->core.display, ic->core.focus_window,
		       IIIMP_Local_KeyFilter, (XPointer)ic);
    return;
}

static char*
IIIMP_MbReset(
    XIC xic
)
{
    XicCommon ic = (XicCommon)xic;

    iiimcf_reset_context(XIC_IIIMP(ic, context));
    ic->core.preedit_attr.preedit_state = XIMPreeditDisable;
    ChangePreeditState(ic);
    return (char*)NULL;
}

static wchar_t*
IIIMP_WcReset(
    XIC xic
)
{
    XicCommon ic = (XicCommon)xic;

    iiimcf_reset_context(XIC_IIIMP(ic, context));
    ic->core.preedit_attr.preedit_state = XIMPreeditDisable;
    ChangePreeditState(ic);

    return (wchar_t*)NULL;
}

char*
IIIMP_SetICValues(
    XIC xic,
    XIMArg *arg
)
{
    XicCommon ic = (XicCommon)xic;
    XimpChangeMaskRec dummy;
    char *ret = NULL;

    memset(&dummy, 0, sizeof (dummy));

    ret = SetICValueData(ic, arg, SET_IC, &dummy);

    return ret;
}

char*
IIIMP_GetICValues(
    XIC xic,
    XIMArg *arg
)
{
    XicCommon ic = (XicCommon)xic;
    XimpChangeMaskRec dummy;
    char *ret = NULL;

    memset(&dummy, 0, sizeof (dummy));

    (void)IMGetICValues(ic, NULL, 0);

    ret = GetICValueData(ic, arg, &dummy);

    return ret;
}

int
IIIMP_MbLookupString_SWITCH(
    XIC xic,
    XKeyEvent *ev,
    char *buffer,
    int len,
    KeySym *keysym,
    Status *status
)
{
    if (XIM_COMPOSE_KEYCODE == ev->keycode) {
	return IIIMP_MbLookupString(xic, ev, buffer, len, keysym, status);
    } else {
	return 0;
    }
}

static int
IIIMP_MbLookupString(
    XIC xic,
    XKeyEvent *ev,
    char *buffer,
    int len,
    KeySym *keysym,
    Status *status
)
{
    XicCommon ic = (XicCommon)xic;
    int ret;
    XComposeStatus compose_status;

    if (ev->keycode == XIM_COMPOSE_KEYCODE) { /* Composed Event */
	IIIMF_status st;
	IIIMCF_text text;
	char *str;
	KeySym keysym_return;

	keysym_return = XIC_IIIMP(ic, keysym_return);

	if (XIC_IIIMP(ic, committed_text_consumed)) return 0;

	st = iiimcf_get_committed_text(XIC_IIIMP(ic, context), &text);
	if (st != IIIMF_STATUS_SUCCESS) return 0;
	st = IIimpConvertToString(text, False, &str);
	if (st != IIIMF_STATUS_SUCCESS) return 0;
	ret = strlen(str);

	if (ret > len) {
	    if (status) *status = XBufferOverflow;
	    return 0;
	}
	memcpy(buffer, str, ret * sizeof(char));
	free(str);
	if (keysym) {
	    if (keysym_return)
		*keysym = keysym_return;
	    else
		*keysym = NoSymbol;
	}
	if (status) {
	    if (keysym_return)
		*status = XLookupBoth;
	    else
		*status = XLookupChars;
	}
	XIC_IIIMP(ic, committed_text_consumed) = True;

	return ret;
    } else { /* Throughed Event */
	(void)memset((void *)&compose_status, 0, sizeof(XComposeStatus));
	ret = _Ximp_LookupMBText((Ximp_XIC)ic, ev,
				 (unsigned char *)buffer,
				 len, keysym, &compose_status);
	if (ret > 0) {
	    if (keysym && *keysym != NoSymbol) {
		if (status) *status = XLookupBoth;
	    } else {
		if (status) *status = XLookupChars;
	    }
	} else {
	    if (keysym && *keysym != NoSymbol) {
		if (status) *status = XLookupKeySym;
	    } else {
		if (status) *status = XLookupNone;
	    }
	}
    }

    return ret;
}

int
IIIMP_WcLookupString_SWITCH(
    XIC xic,
    XKeyEvent *ev,
    wchar_t *buffer,
    int len,
    KeySym *keysym,
    Status *status
)
{
    if (XIM_COMPOSE_KEYCODE == ev->keycode) {
	return IIIMP_WcLookupString(xic, ev, buffer, len, keysym, status);
    } else {
	return 0;
    }
}

static int
IIIMP_WcLookupString(
    XIC xic,
    XKeyEvent *ev,
    wchar_t *buffer,
    int len,
    KeySym *keysym,
    Status *status
)
{
    XicCommon ic = (XicCommon)xic;
    XimCommon im = (XimCommon)ic->core.im;
    int ret;
    XComposeStatus compose_status;

    if (ev->keycode == XIM_COMPOSE_KEYCODE) { /* Composed Event */
	IIIMF_status st;
	Status sts;
	IIIMCF_text text;
	char *str;
	int byte_len;
	KeySym keysym_return;

	keysym_return = XIC_IIIMP(ic, keysym_return);

	if (XIC_IIIMP(ic, committed_text_consumed)) return 0;

	st = iiimcf_get_committed_text(XIC_IIIMP(ic, context), &text);
	if (st != IIIMF_STATUS_SUCCESS) return 0;
	st = IIimpConvertToString(text, False, &str);
	if (st != IIIMF_STATUS_SUCCESS) return 0;
	byte_len = strlen(str);

	if ((ret = IIimpMbstoWcs(im, str, byte_len,
				 buffer, len, &sts)) < 0) {
	    ret = 0;
	    if(status) *status = XLookupNone;
	} else if (sts == XBufferOverflow) {
	    *status = XBufferOverflow;
	    return ret; /* Immidiately return */
	}
	if (keysym) {
	    if (keysym_return)
		*keysym = keysym_return;
	    else
		*keysym = NoSymbol;
	}
	if (status) {
	    if (keysym_return)
		*status = XLookupBoth;
	    else
		*status = XLookupChars;
	}

	XIC_IIIMP(ic, committed_text_consumed) = True;

	return ret;
    } else { /* Throughed Event */
	(void)memset((void *)&compose_status, 0, sizeof(XComposeStatus));
	ret = _Ximp_LookupWCText((Ximp_XIC)ic, ev, buffer,
				 len, keysym, &compose_status);
	if (ret > 0) {
	    if (keysym && *keysym != NoSymbol) {
		if (status) *status = XLookupBoth;
	    } else {
		if (status) *status = XLookupChars;
	    }
	} else {
	    if (keysym && *keysym != NoSymbol) {
		if (status) *status = XLookupKeySym;
	    } else {
		if (status) *status = XLookupNone;
	    }
	}
    }
    return ret;
}

void
HoldXKeyEvent(
    XicCommon ic,
    XEvent * ev
)
{
    XICKeyEventListRec *	kel;

    kel = (XICKeyEventListRec *)Xmalloc(sizeof (XICKeyEventListRec));
    if (NULL == kel) {
	return;
    }

    memcpy(&(kel->key_event), ev, sizeof (XKeyEvent));
    kel->next = XIC_IIIMP(ic, key_event_list);
    XIC_IIIMP(ic, key_event_list) = kel;

    return;
}

void
PutBackXKeyEvent(
    XicCommon ic
)
{
    XICKeyEventListRec *	key_event_list;
    XICKeyEventListRec *	kel;
    int			key_event_num;

    key_event_list = XIC_IIIMP(ic, key_event_list);
    if (NULL == key_event_list) {
	return;
    }

    key_event_num = XIC_IIIMP(ic, key_event_num);
    if (key_event_num < 0) {
	key_event_num = 0;
    }

    while (NULL != key_event_list) {
	if (XIM_COMPOSE_KEYCODE != key_event_list->key_event.keycode) {
	    key_event_num += 1;
	}
	XPutBackEvent(ic->core.im->core.display,
		      (XEvent *)(&key_event_list->key_event));
	kel = key_event_list->next;
	Xfree(key_event_list);
	key_event_list = kel;
    }

    XIC_IIIMP(ic, key_event_list) = NULL;
    XIC_IIIMP(ic, key_event_num) = key_event_num;

    return;
}

void
IIIMPKeyEventFilter(
    XicCommon ic,
    Window old_focus
)
{
    _XUnregisterFilter(ic->core.im->core.display, old_focus,
		       IIIMP_Local_KeyFilter, (XPointer)ic);
    _XRegisterFilterByType(ic->core.im->core.display, ic->core.focus_window,
			   KeyPress, KeyRelease,
			   IIIMP_Local_KeyFilter, (XPointer)ic);
    return;
}

/* Switching */
void
SWITCH_DestroyIC(
    XIC xic
)
{
    XicCommon ic = (XicCommon)xic;
    XimCommon im = (XimCommon)ic->core.im;

    if (XIM_IS_SWITCH(im)) {
	UnRegisterSwitchFilter(ic, SwitchFilter,
			       IIIMP_Local_KeyFilter,
			       get_iiimp_ic_methods());
    }

    if (XIM_IIIMP(im, current_ic) == xic) {
	IIIMP_UnSetFocus(xic);
    }

    iiimcf_destroy_context(XIC_IIIMP(ic, context));

    ic_switcher_finalize (ic);

    _XUnregisterFilter(ic->core.im->core.display,
		       ic->core.focus_window,
		       IIIMP_Local_KeyFilter, (XPointer)ic);

    if (ic->gui_icpart) {
	XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_DESTROY, NULL);
	XIC_GUI(ic, change_status)((XIC)ic, STATUS_DESTROY, NULL);
	XIC_GUI(ic, change_lookup)((XIC)ic, LOOKUP_DESTROY, NULL);
    }

    if (ic->iiimp_icpart) {
	Xfree (ic->iiimp_icpart);
	ic->iiimp_icpart = 0;
    }

    return;
}

Bool
IMGetICValues(
    XicCommon ic,
    XICAttribute *ic_attr,
    int attr_count
)
{
    /* nothing to do from X client */
    return True;
}

Bool
IMCheckIMWindow(
    XicCommon ic,
    Window window
)
{
    return (XIM_IS_IIIMP(ic->core.im)
	    && XIM_IIIMP(ic->core.im, handle)
	    && XFactoryCheckIMWindow(ic->core.im->core.display, window));
}

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