/******************************************************************
 
         Copyright 1994, 1995 by Sun Microsystems, Inc.
         Copyright 1993, 1994 by Hewlett-Packard Company
 
Permission to use, copy, modify, distribute, and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear
in supporting documentation, and that the name of Sun Microsystems, Inc.
and Hewlett-Packard not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.
Sun Microsystems, Inc. and Hewlett-Packard make no representations about
the suitability of this software for any purpose.  It is provided "as is"
without express or implied warranty.
 
SUN MICROSYSTEMS INC. AND HEWLETT-PACKARD COMPANY DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
SUN MICROSYSTEMS, INC. AND HEWLETT-PACKARD COMPANY BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
  Author: Hidetoshi Tajima(tajima@Eng.Sun.COM) Sun Microsystems, Inc.
 
******************************************************************/
#include <X11/Xlib.h>
#include "X11R6IMProtoData.h"
#include "Xi18n.h"
#include "FrameMgr.h"
#include "XIC_Interface.h"

#define IC_SIZE 64

extern int _Xi18nNeedSwap(Xi18n i18n_core, CARD16 connect_id);
extern void _Xi18nSendMessage(XIMS ims, CARD16 connect_id,
                  CARD8 major_opcode, CARD8 minor_opcode,
                  unsigned char *data, long length);
extern void FreeICAttrValues(
        CARD16 preedit_attr_num,
        CARD16 status_attr_num,
        CARD16 ic_attr_num,
        XICAttribute *preedit_attr,
        XICAttribute *status_attr,
        XICAttribute *ic_attr);
extern void _Xi18nSetEventMask(XIMS ims, CARD16 connect_id,
			       CARD16 im_id, CARD16 ic_id,
			       CARD32 forward_mask, CARD32 sync_mask);
extern void FreeICAttrNames(
        CARD16 preedit_attr_num,
        CARD16 status_attr_num,
        CARD16 ic_attr_num,
        XICAttribute *preedit_attr,
        XICAttribute *status_attr,
        XICAttribute *ic_attr);


/* Set IC values */
static void
SetCardAttribute(XICAttribute *value_ret, char *p, XICAttr *ic_attr,
		 int value_length, int need_swap)
{
    char *buf;
    FrameMgr fm;

    if (!(buf = (char *)malloc(value_length)))
      return;

    if (value_length == sizeof(CARD8)) {
	memmove(buf, p, value_length);
    } else if (value_length == sizeof(CARD16)) {
	INT16 value;
	extern XimFrameRec worddata_fr[];
	/* create FrameMgr */
	fm = FrameMgrInit(worddata_fr, (char *)p, need_swap);
	/* get data */
	FrameMgrGetToken(fm, value);
	/* free FrameMgr */
	FrameMgrFree(fm);
	memmove(buf, &value, value_length);
    } else if (value_length == sizeof(CARD32)) {
	INT32 value;
	extern XimFrameRec longdata_fr[];
	/* create FrameMgr */
	fm = FrameMgrInit(longdata_fr, (char *)p, need_swap);
	/* get data */
	FrameMgrGetToken(fm, value);
	/* free FrameMgr */
	FrameMgrFree(fm);
	memmove(buf, &value, value_length);
    }

    value_ret->attribute_id = ic_attr->attribute_id;
    value_ret->name = ic_attr->name;
    value_ret->name_length = ic_attr->length;
    value_ret->type = ic_attr->type;
    value_ret->value_length = value_length;
    value_ret->value = buf;

    return;
}

static void
SetFontAttribute(XICAttribute *value_ret, char *p, XICAttr *ic_attr,
		 int value_length, int need_swap)
{
    char *buf;
    char *base_name;
    INT16 base_length;
    FrameMgr fm;
    extern XimFrameRec xfontset_fr[];

    /* create FrameMgr */
    fm = FrameMgrInit(xfontset_fr, (char *)p, need_swap);
    /* get data */
    FrameMgrGetToken(fm, base_length);
    FrameMgrSetSize(fm, base_length);
    if (!(buf = (char *)malloc(base_length + 1)))
	return;
    FrameMgrGetToken(fm, base_name);
    /* free FrameMgr */
    FrameMgrFree(fm);

    strncpy(buf, base_name, base_length);
    buf[base_length] = (char)0;

    value_ret->attribute_id = ic_attr->attribute_id;
    value_ret->name = ic_attr->name;
    value_ret->name_length = ic_attr->length;
    value_ret->type = ic_attr->type;
    value_ret->value_length = base_length;
    value_ret->value = buf;

    return;
}

static void
SetPointAttribute(XICAttribute *value_ret, char *p, XICAttr *ic_attr,
		  int value_length, int need_swap)
{
    XPoint *buf;
    FrameMgr fm;
    extern XimFrameRec xpoint_fr[];

    if (!(buf = (XPoint *)malloc(sizeof(XPoint))))
      return;

    /* create FrameMgr */
    fm = FrameMgrInit(xpoint_fr, (char *)p, need_swap);
    /* get data */
    FrameMgrGetToken(fm, buf->x);
    FrameMgrGetToken(fm, buf->y);
    /* free FrameMgr */
    FrameMgrFree(fm);

    memmove(p, &(buf->x), sizeof(INT16)); p += sizeof(INT16);
    memmove(p, &(buf->y), sizeof(INT16));

    value_ret->attribute_id = ic_attr->attribute_id;
    value_ret->name = ic_attr->name;
    value_ret->name_length = ic_attr->length;
    value_ret->type = ic_attr->type;
    value_ret->value_length = value_length;
    value_ret->value = (char *)buf;

    return;
}

static void
SetRectAttribute(XICAttribute *value_ret, char *p, XICAttr *ic_attr,
		  int value_length, int need_swap)
{
    XRectangle *buf;
    FrameMgr fm;
    extern XimFrameRec xrectangle_fr[];

    if (!(buf = (XRectangle *)malloc(sizeof(XRectangle))))
      return;

    /* create FrameMgr */
    fm = FrameMgrInit(xrectangle_fr, (char *)p, need_swap);
    /* get data */
    FrameMgrGetToken(fm, buf->x); FrameMgrGetToken(fm, buf->y);
    FrameMgrGetToken(fm, buf->width); FrameMgrGetToken(fm, buf->height);
    /* free FrameMgr */
    FrameMgrFree(fm);

    value_ret->attribute_id = ic_attr->attribute_id;
    value_ret->name = ic_attr->name;
    value_ret->name_length = ic_attr->length;
    value_ret->type = ic_attr->type;
    value_ret->value_length = value_length;
    value_ret->value = (char *)buf;

    return;
}

#if 0
static void
SetHotKeyAttribute(XICAttribute *value_ret, char *p, XICAttr *ic_attr,
		   int value_length, int need_swap)
{
    INT32 list_number;
    XIMTriggerKey *hotkeys;

    memmove(&list_number, p, sizeof(INT32)); p += sizeof(INT32);

    hotkeys = (XIMTriggerKey *)malloc(list_number * sizeof(XIMTriggerKey));
    if (hotkeys == NULL)
      return;
    memmove(hotkeys, p, list_number * sizeof(XIMTriggerKey));

    value_ret->attribute_id = ic_attr->attribute_id;
    value_ret->name = ic_attr->name;
    value_ret->name_length = ic_attr->length;
    value_ret->type = ic_attr->type;
    value_ret->value_length = value_length;
    value_ret->value = (char *)hotkeys;

    return;
}
#endif

/* get IC values */
static void
GetAttrHeader(unsigned char *rec, XICAttribute *list, int need_swap)
{
    FrameMgr fm;
    extern XimFrameRec attr_head_fr[];

    /* create FrameMgr of attribute header frame */
    fm = FrameMgrInit(attr_head_fr, (char *)rec, need_swap);
    /* put data */
    FrameMgrPutToken(fm, list->attribute_id);
    FrameMgrPutToken(fm, list->value_length);
    /* free FrameMgr */
    FrameMgrFree(fm);
    return;
}

static void
GetCardAttribute(char *rec, XICAttribute *list, int need_swap)
{
    FrameMgr fm;
    unsigned char *recp = (unsigned char *)rec;

    GetAttrHeader(recp, list, need_swap);
    recp += sizeof(CARD16) * 2;

    if (list->value_length == sizeof(CARD8)) {
	memmove(recp, list->value, list->value_length);
    } else if (list->value_length == sizeof(CARD16)) {
	INT16 *value = (INT16 *)list->value;
	extern XimFrameRec worddata_fr[];
	/* create FrameMgr */
	fm = FrameMgrInit(worddata_fr, (char *)recp, need_swap);
	/* put data */
	FrameMgrPutToken(fm, *value);
	/* free FrameMgr */
	FrameMgrFree(fm);
    } else if (list->value_length == sizeof(CARD32)) {
	INT32 *value = (INT32 *)list->value;
	extern XimFrameRec longdata_fr[];
	/* create FrameMgr */
	fm = FrameMgrInit(longdata_fr, (char *)recp, need_swap);
	/* put data */
	FrameMgrPutToken(fm, *value);
	/* free FrameMgr */
	FrameMgrFree(fm);
    }
    return;
}

static void
GetFontAttribute(char *rec, XICAttribute *list, int need_swap)
{
    FrameMgr fm;
    extern XimFrameRec xfontset_fr[];
    CARD8 *base_name = (CARD8*)list->value;
    unsigned char *recp = (unsigned char *)rec;
    INT16 length = list->value_length;

    GetAttrHeader(recp, list, need_swap);
    recp += sizeof(CARD16) * 2;

    /* create FrameMgr of fontset frame */
    fm = FrameMgrInit(xfontset_fr, (char *)recp, need_swap);
    /* put data */
    FrameMgrPutToken(fm, length);
    FrameMgrSetSize(fm, length);
    FrameMgrPutToken(fm, base_name);
    /* free FrameMgr */
    FrameMgrFree(fm);
    return;
}

static void
GetRectAttribute(char *rec, XICAttribute *list, int need_swap)
{
    FrameMgr fm;
    extern XimFrameRec xrectangle_fr[];
    XRectangle *rect = (XRectangle *)list->value;
    unsigned char *recp = (unsigned char *)rec;

    GetAttrHeader(recp, list, need_swap);
    recp += sizeof(CARD16) * 2;

    /* create FrameMgr of xrectangle frame */
    fm = FrameMgrInit(xrectangle_fr, (char *)recp, need_swap);
    /* put data */
    FrameMgrPutToken(fm, rect->x); FrameMgrPutToken(fm, rect->y);
    FrameMgrPutToken(fm, rect->width); FrameMgrPutToken(fm, rect->height);
    /* free FrameMgr */
    FrameMgrFree(fm);
    return;
}

static void
GetPointAttribute(char *rec, XICAttribute *list, int need_swap)
{
    FrameMgr fm;
    extern XimFrameRec xpoint_fr[];
    XPoint *rect = (XPoint *)list->value;
    unsigned char *recp = (unsigned char *)rec;

    GetAttrHeader(recp, list, need_swap);
    recp += sizeof(CARD16) * 2;

    /* create FrameMgr of xpoint frame */
    fm = FrameMgrInit(xpoint_fr, (char *)recp, need_swap);
    /* put data */
    FrameMgrPutToken(fm, rect->x); FrameMgrPutToken(fm, rect->y);
    /* free FrameMgr */
    FrameMgrFree(fm);
    return;
}

static int
ReadICValue(Xi18n i18n_core, CARD16 icvalue_id, int value_length, void *p,
	    XICAttribute *value_ret, CARD16 *number_ret,int need_swap)
{
    XICAttr *ic_attr = i18n_core->address.xic_attr;
    int i;

    *number_ret = (CARD16)0;

    for (i = 0; i < i18n_core->address.ic_attr_num; i++, ic_attr++) {
	if (ic_attr->attribute_id == icvalue_id) {
	    break;
	}
    }
    switch (ic_attr->type) {
      case XimType_NEST:
	{
	    int total_length = 0;
	    CARD16 attribute_ID;
	    INT16 attribute_length;
	    unsigned char *p1 = (unsigned char *) p;
	    CARD16 ic_len = 0;
	    CARD16 number;
	    FrameMgr fm;
	    extern XimFrameRec attr_head_fr[];

	    while (total_length < value_length) {
		/* create FrameMgr */
		fm = FrameMgrInit(attr_head_fr, (char *)p1, need_swap);
		/* get data */
		FrameMgrGetToken(fm, attribute_ID);
		FrameMgrGetToken(fm, attribute_length);
		/* free FrameMgr */
		FrameMgrFree(fm);
		p1 += sizeof(CARD16) * 2;
		ReadICValue(i18n_core,
			    attribute_ID, attribute_length,
			    p1, (value_ret + ic_len), &number, need_swap);
		ic_len++;
		*number_ret += number;
		p1 += attribute_length;
		p1 += IMPAD(attribute_length);
		total_length += (CARD16)sizeof(CARD16) * 2
				  + (INT16)attribute_length
				  + IMPAD(attribute_length);
	    }
	    return ic_len;
	}
      case XimType_CARD8:
      case XimType_CARD16:
      case XimType_CARD32:
      case XimType_Window:
	SetCardAttribute(value_ret, p, ic_attr, value_length, need_swap);
	*number_ret = (CARD16)1;
	return *number_ret;
      case XimType_XFontSet:
	SetFontAttribute(value_ret, p, ic_attr, value_length, need_swap);
	*number_ret = (CARD16)1;
	return *number_ret;
      case XimType_XRectangle:
	SetRectAttribute(value_ret, p, ic_attr, value_length, need_swap);
	*number_ret = (CARD16)1;
	return *number_ret;
      case XimType_XPoint:
	SetPointAttribute(value_ret, p, ic_attr, value_length, need_swap);
	*number_ret = (CARD16)1;
	return *number_ret;
#if 0
      case XimType_XIMHotKeyTriggers:
	SetHotKeyAttribute(value_ret, p, ic_attr, value_length, need_swap);
#endif
      default:
	return 0;
    }
}

static int
GetValueLength(int type, char *value) {
  int length = 0;
  switch (type) {
  case XimType_CARD8:
    length = sizeof(CARD8);
    break;
  case XimType_CARD16:
    length = sizeof(CARD16);
    break;
  case XimType_CARD32:
    length = sizeof(CARD32);
    break;
  case XimType_Window:
    length = sizeof(Window);
    break;
  case XimType_XIMStyles:
    length = sizeof(XIMStyles);
    break;
  case XimType_XRectangle:
    length = sizeof(XRectangle);
    break;
  case XimType_XPoint:
    length = sizeof(XPoint);
    break;
  case XimType_XFontSet:
  case XimType_STRING8:
    length = strlen(value);
    break;
  }
  return length;
}

static XICAttribute *
CreateNestedList(CARD16 attr_id, XICAttribute *list,
		 int number, int need_swap)
{
    XICAttribute *nest_list = NULL;
    register int i;
    char *values = NULL, *valuesp;
    int value_length = 0;

    if (number == 0) return NULL;
    for (i = 0; i < number; i++) {
	value_length += sizeof(CARD16) * 2;
	list[i].value_length = GetValueLength(list[i].type,
					      list[i].value);
	value_length += list[i].value_length;
	value_length += IMPAD(list[i].value_length);
    }
    if ((values = (char *)malloc(value_length)) == NULL)
      return NULL;
    memset(values, 0, value_length);

    valuesp = values;
    for (i = 0; i < number; i++) {
	switch (list[i].type) {
	  case XimType_CARD8:
	  case XimType_CARD16:
	  case XimType_CARD32:
	  case XimType_Window:
	    GetCardAttribute(valuesp, &list[i], need_swap);
	    break;
	  case XimType_XFontSet:
	    GetFontAttribute(valuesp, &list[i], need_swap);
	    break;
	  case XimType_XRectangle:
	    GetRectAttribute(valuesp, &list[i], need_swap);
	    break;
	  case XimType_XPoint:
	    GetPointAttribute(valuesp, &list[i], need_swap);
	    break;
#if 0
	  case XimType_XIMHotKeyTriggers:
	    GetHotKeyAttribute(valuesp, &list[i], need_swap);
	    break;
#endif
	  default:
	    break;
	}
	valuesp += sizeof(CARD16) * 2;
	valuesp += list[i].value_length;
	valuesp += IMPAD(list[i].value_length);
    }

    nest_list = (XICAttribute *)malloc(sizeof(XICAttribute));
    if (nest_list == NULL)
      return NULL;
    memset(nest_list, 0, sizeof(XICAttribute));
    nest_list->value = (void *)malloc(value_length);
    if (nest_list->value == NULL)
      return NULL;
    memset(nest_list->value, 0, sizeof(value_length));

    nest_list->attribute_id = attr_id;
    nest_list->value_length = value_length;
    memmove(nest_list->value, values, value_length);

    XFree(values);
    return (nest_list);
}

static Bool
IsNestedList(Xi18n i18n_core, CARD16 icvalue_id)
{
    XICAttr *ic_attr = i18n_core->address.xic_attr;
    int i;

    for (i = 0; i < i18n_core->address.ic_attr_num; i++, ic_attr++) {
	if (ic_attr->attribute_id == icvalue_id) {
	    if (ic_attr->type == XimType_NEST)
	      return True;
	    else
	      return False;
	}
    }
    return False;
}

static Bool
IsSeparator(Xi18n i18n_core, CARD16 icvalue_id)
{
    return (i18n_core->address.separatorAttr_id == icvalue_id);
}

static void
GetICValue(Xi18n i18n_core, XICAttribute *attr_ret,
	    CARD16 *id_list, int list_num, int *number_ret)
{
    XICAttr *xic_attr = i18n_core->address.xic_attr;
    register int i, j, n;

    i = n = 0;
    if (IsNestedList(i18n_core, id_list[i])) {
	i++;
	while (i < list_num && !IsSeparator(i18n_core, id_list[i])) {
	    for (j = 0; j < i18n_core->address.ic_attr_num; j++) {
		if (xic_attr[j].attribute_id == id_list[i]) {
		    attr_ret[n].attribute_id = xic_attr[j].attribute_id;
		    attr_ret[n].name_length = xic_attr[j].length;
		    attr_ret[n].name = malloc(xic_attr[j].length + 1);
		    strcpy(attr_ret[n].name, xic_attr[j].name);
		    attr_ret[n].type = xic_attr[j].type;
		    n++; i++;
		    break;
		}
	    }
	}
    } else {
	for (j = 0; j < i18n_core->address.ic_attr_num; j++) {
	    if (xic_attr[j].attribute_id == id_list[i]) {
		attr_ret[n].attribute_id = xic_attr[j].attribute_id;
		attr_ret[n].name_length = xic_attr[j].length;
		attr_ret[n].name = malloc(xic_attr[j].length + 1);
		strcpy(attr_ret[n].name, xic_attr[j].name);
		attr_ret[n].type = xic_attr[j].type;
		n++;
		break;
	    }
	}
    }
    *number_ret = n;
    return;
}

/* called from CreateICMessageProc and SetICValueMessageProc */
void
_Xi18nChangeIC(XIMS ims, X11R6IMProtocol call_data,
	       unsigned char *p, int create_flag) {
    Xi18n i18n_core = ims->protocol;
    FrameMgr fm;
    FmStatus status;
    CARD16 byte_length;
    register int total_size;
    unsigned char *reply = NULL;
    register int i, attrib_num;
    XICAttribute *attrib_list;
    XICAttribute pre_attr[IC_SIZE], sts_attr[IC_SIZE], ic_attr[IC_SIZE];
    CARD16 preedit_ic_num = 0, status_ic_num = 0, ic_num = 0;
    CARD16 connect_id = call_data->any.connect_id;
    IMChangeICStruct *changeic =
      (IMChangeICStruct*)&call_data->changeic;
    extern XimFrameRec create_ic_fr[], create_ic_reply_fr[];
    extern XimFrameRec set_ic_values_fr[], set_ic_values_reply_fr[];
    CARD16 input_method_ID;

    memset(pre_attr, 0, sizeof(XICAttribute) * IC_SIZE);
    memset(sts_attr, 0, sizeof(XICAttribute) * IC_SIZE);
    memset(ic_attr, 0, sizeof(XICAttribute) * IC_SIZE);

    /* create FrameMgr */
    if (create_flag == True) {
	fm = FrameMgrInit(create_ic_fr, (char *)p,
			  _Xi18nNeedSwap(i18n_core, connect_id));
	/* get data */
	FrameMgrGetToken(fm, input_method_ID);
	FrameMgrGetToken(fm, byte_length);
    } else {
	fm = FrameMgrInit(set_ic_values_fr, (char *)p,
			  _Xi18nNeedSwap(i18n_core, connect_id));
	/* get data */
	FrameMgrGetToken(fm, input_method_ID);
	FrameMgrGetToken(fm, changeic->icid);
	FrameMgrGetToken(fm, byte_length);
    }
    attrib_list = (XICAttribute *)malloc(sizeof(XICAttribute) * IC_SIZE);
    if (!attrib_list) {
	_Xi18nSendMessage(ims, connect_id, XIM_ERROR, 0, 0, 0);
	return;
    }
    memset(attrib_list, 0, sizeof(XICAttribute) * IC_SIZE);

    attrib_num = 0;
    while (FrameMgrIsIterLoopEnd(fm, &status) == False) {
	void *value;
	int value_length;
	FrameMgrGetToken(fm, attrib_list[attrib_num].attribute_id);
	FrameMgrGetToken(fm, value_length);
	FrameMgrSetSize(fm, value_length);
	attrib_list[attrib_num].value_length = value_length;
	FrameMgrGetToken(fm, value);
	attrib_list[attrib_num].value = (void *)malloc(value_length + 1);
	memmove(attrib_list[attrib_num].value, value, value_length);
	attrib_num++;
    }

    for (i = 0; i < attrib_num; i++) {
	CARD16 number;
	if (IsNestedList(i18n_core, attrib_list[i].attribute_id)) {
	    if (attrib_list[i].attribute_id == i18n_core->address.preeditAttr_id) {
		ReadICValue(i18n_core, attrib_list[i].attribute_id,
			    attrib_list[i].value_length,
			    attrib_list[i].value,
			    &pre_attr[preedit_ic_num], &number,
			    _Xi18nNeedSwap(i18n_core, connect_id));
		preedit_ic_num += number;
	    } else if (attrib_list[i].attribute_id == i18n_core->address.statusAttr_id) {
		ReadICValue(i18n_core, attrib_list[i].attribute_id,
			    attrib_list[i].value_length,
			    attrib_list[i].value,
			    &sts_attr[status_ic_num], &number,
			    _Xi18nNeedSwap(i18n_core, connect_id));
		status_ic_num += number;
	    } else {
		/* another nested list.. possible? */
	    }
	} else {
	    ReadICValue(i18n_core, attrib_list[i].attribute_id,
			attrib_list[i].value_length,
			attrib_list[i].value,
			&ic_attr[ic_num], &number,
			_Xi18nNeedSwap(i18n_core, connect_id));
	    ic_num +=number;
	}
    }
    for (i = 0; i < attrib_num; i++) {
	XFree(attrib_list[i].value);
    }
    XFree(attrib_list);

    /* free FrameMgr */
    FrameMgrFree(fm);

    changeic->preedit_attr_num = preedit_ic_num;
    changeic->status_attr_num = status_ic_num;
    changeic->ic_attr_num = ic_num;
    changeic->preedit_attr = pre_attr;
    changeic->status_attr = sts_attr;
    changeic->ic_attr = ic_attr;

#ifdef IM_PROTO_HANDLER
    CALL_PROTO_HANDLER;
#else
    if (create_flag == True) {
      changeic->icid = CreateXIC(ims,
				 connect_id,
				 preedit_ic_num,
				 status_ic_num,
				 ic_num,
				 pre_attr,
				 sts_attr,
				 ic_attr);
    } else {
      SetICValues(changeic->icid,
		  preedit_ic_num,
		  status_ic_num,
		  ic_num,
		  pre_attr,
		  sts_attr,
		  ic_attr);
    }
#endif

    /* need to free attr->value of pre_attr, sts_attr, ic_attr */
    FreeICAttrValues(preedit_ic_num, status_ic_num, ic_num,
		  pre_attr, sts_attr, ic_attr);

    /* create FrameMgr */
    if (create_flag == True) {
	fm = FrameMgrInit(create_ic_reply_fr, NULL,
			  _Xi18nNeedSwap(i18n_core, connect_id));
    } else {
	fm = FrameMgrInit(set_ic_values_reply_fr, NULL,
			  _Xi18nNeedSwap(i18n_core, connect_id));
    }
    total_size = FrameMgrGetTotalSize(fm);
    reply = (unsigned char *)malloc(total_size);
    if (!reply) {
	_Xi18nSendMessage(ims, connect_id, XIM_ERROR, 0, 0, 0);
	return;
    }
    memset(reply, 0, total_size);
    FrameMgrSetBuffer(fm, reply);

    FrameMgrPutToken(fm, input_method_ID);
    FrameMgrPutToken(fm, changeic->icid);

    if (create_flag == True) {
	_Xi18nSendMessage(ims, connect_id, XIM_CREATE_IC_REPLY, 0,
			  reply, total_size);
    } else {
	_Xi18nSendMessage(ims, connect_id, XIM_SET_IC_VALUES_REPLY, 0,
			  reply, total_size);
    }
    if (create_flag == True) {
	int on_key_num = i18n_core->address.on_keys.count_keys;
	int off_key_num = i18n_core->address.off_keys.count_keys;

	if (on_key_num == 0 && off_key_num == 0) {
	    long mask;
	    if (i18n_core->address.imvalue_mask & I18N_FILTERMASK)
	      mask = i18n_core->address.filterevent_mask;
	    else
	      mask = DEFAULT_FILTER_MASK;
	    /* static event flow is default */
	    _Xi18nSetEventMask(ims, connect_id,
			       input_method_ID,
			       changeic->icid,
			       mask, ~mask);
	}
    }
    XFree(reply);
    /* free FrameMgr */
    FrameMgrFree(fm);
    return;
}

/* called from GetICValueMessageProc */
void
_Xi18nGetIC(XIMS ims, X11R6IMProtocol call_data, unsigned char *p)
{
    Xi18n i18n_core = ims->protocol;
    FrameMgr fm;
    FmStatus status;
    extern XimFrameRec get_ic_values_fr[], get_ic_values_reply_fr[];
    CARD16 byte_length;
    register int total_size;
    unsigned char *reply = NULL;
    XICAttribute *preedit_ret = NULL, *status_ret = NULL;
    register int i, number;
    int iter_count;
    CARD16 *attrID_list;
    XICAttribute pre_attr[IC_SIZE], sts_attr[IC_SIZE], ic_attr[IC_SIZE];
    CARD16 pre_count = 0, sts_count = 0, ic_count = 0;
    IMChangeICStruct *getic =
      (IMChangeICStruct*)&call_data->changeic;
    CARD16 connect_id = call_data->any.connect_id;
    CARD16 input_method_ID;

    memset(pre_attr, 0, sizeof(XICAttribute) * IC_SIZE);
    memset(sts_attr, 0, sizeof(XICAttribute) * IC_SIZE);
    memset(ic_attr, 0, sizeof(XICAttribute) * IC_SIZE);

    /* create FrameMgr */
    fm = FrameMgrInit(get_ic_values_fr, (char *)p,
		      _Xi18nNeedSwap(i18n_core, connect_id));

    /* get data */
    FrameMgrGetToken(fm, input_method_ID);
    FrameMgrGetToken(fm, getic->icid);
    FrameMgrGetToken(fm, byte_length);

    attrID_list = (CARD16 *)malloc(sizeof(CARD16) * IC_SIZE); /* bogus */
    memset(attrID_list, 0, sizeof(CARD16) * IC_SIZE);

    number = 0;
    while (FrameMgrIsIterLoopEnd(fm, &status) == False) {
	FrameMgrGetToken(fm, attrID_list[number]);
	number++;
    }
    /* free FrameMgr */
    FrameMgrFree(fm);

    i = 0;
    while (i < number) {
	int read_number;
	if (IsNestedList(i18n_core, attrID_list[i])) {
	    if (attrID_list[i] == i18n_core->address.preeditAttr_id) {
		GetICValue(i18n_core, &pre_attr[pre_count],
			   &attrID_list[i], number, &read_number);
		i += read_number + 1;
		pre_count += read_number;
	    } else if (attrID_list[i] == i18n_core->address.statusAttr_id) {
		GetICValue(i18n_core, &sts_attr[sts_count],
			   &attrID_list[i], number, &read_number);
		i += read_number + 1;
		sts_count += read_number;
	    } else {
		/* another nested list.. possible? */
	    }
	} else {
	    GetICValue(i18n_core, &ic_attr[ic_count], &attrID_list[i],
		       number, &read_number);
	    i += read_number;
	    ic_count += read_number;
	}
    }
    getic->preedit_attr_num = pre_count;
    getic->status_attr_num = sts_count;
    getic->ic_attr_num = ic_count;
    getic->preedit_attr = pre_attr;
    getic->status_attr = sts_attr;
    getic->ic_attr = ic_attr;

    XFree(attrID_list);

#ifdef IM_PROTO_HANDLER
    CALL_PROTO_HANDLER;
#else
    GetICValues(getic->icid,
		pre_count, sts_count, ic_count,
		pre_attr, sts_attr, ic_attr);
#endif

    iter_count = getic->ic_attr_num;

    preedit_ret = CreateNestedList(i18n_core->address.preeditAttr_id,
				   getic->preedit_attr,
				   getic->preedit_attr_num,
				   _Xi18nNeedSwap(i18n_core, connect_id));
    if (preedit_ret != NULL)
      iter_count++;
    status_ret = CreateNestedList(i18n_core->address.statusAttr_id,
				  getic->status_attr,
				  getic->status_attr_num,
				  _Xi18nNeedSwap(i18n_core, connect_id));
    if (status_ret != NULL)
      iter_count++;

    /* create FrameMgr */
    fm = FrameMgrInit(get_ic_values_reply_fr, NULL,
		      _Xi18nNeedSwap(i18n_core, connect_id));

    /* set iteration count for list of ic_attribute */
    FrameMgrSetIterCount(fm, iter_count);

    /* set length of BARRAY item in xicattribute_fr*/
    for (i = 0; i < (int)getic->ic_attr_num; i++) {
	FrameMgrSetSize(fm, ic_attr[i].value_length);
    }

    if (preedit_ret != NULL)
      FrameMgrSetSize(fm, preedit_ret->value_length);
    if (status_ret != NULL)
      FrameMgrSetSize(fm, status_ret->value_length);

    total_size = FrameMgrGetTotalSize(fm);
    reply = (unsigned char *)malloc(total_size);
    if (!reply) {
	_Xi18nSendMessage(ims, connect_id, XIM_ERROR, 0, 0, 0);
	return;
    }
    memset(reply, 0, total_size);
    FrameMgrSetBuffer(fm, reply);

    FrameMgrPutToken(fm, input_method_ID);
    FrameMgrPutToken(fm, getic->icid);

    for (i = 0; i < (int)getic->ic_attr_num; i++) {
	FrameMgrPutToken(fm, ic_attr[i].attribute_id);
	FrameMgrPutToken(fm, ic_attr[i].value_length);
	FrameMgrPutToken(fm, ic_attr[i].value);
    }
    if (preedit_ret != NULL) {
	FrameMgrPutToken(fm, preedit_ret->attribute_id);
	FrameMgrPutToken(fm, preedit_ret->value_length);
	FrameMgrPutToken(fm, preedit_ret->value);
    }
    if (status_ret != NULL) {
	FrameMgrPutToken(fm, status_ret->attribute_id);
	FrameMgrPutToken(fm, status_ret->value_length);
	FrameMgrPutToken(fm, status_ret->value);
    }
    _Xi18nSendMessage(ims, connect_id,
		      XIM_GET_IC_VALUES_REPLY, 0, reply, total_size);
    XFree(reply);

    /* need to free attr->name of pre_attr, sts_attr, ic_attr */
    FreeICAttrValues(pre_count, sts_count, ic_count,
		pre_attr, sts_attr, ic_attr);

    /* need to free attr->name of pre_attr, sts_attr, ic_attr */
    FreeICAttrNames(pre_count, sts_count, ic_count,
		pre_attr, sts_attr, ic_attr);

    if (preedit_ret != NULL) {
	XFree(preedit_ret->value);
	XFree(preedit_ret);
    }
    if (status_ret != NULL) {
	XFree(status_ret->value);
	XFree(status_ret);
    }
    /* free FrameMgr */
    FrameMgrFree(fm);
    return;
}
