#include <config.h>
#include <stdio.h>
#include <vector>
#include "IIIMP_ICState.hh"
#include "IIIMPTrans.hh"
#include "IIIMPUtil.hh"

/*******************************************************************************
                Initial ICState
*******************************************************************************/

IIIMP_ICState_REQUESTED*
IIIMP_ICState::start_request()
{
    IIIMP_ICState_REQUESTED *picsr = new IIIMP_ICState_REQUESTED(this);
    // TODO: throw memory error.
    change_state(*picsr, false);
    return picsr;
}


bool
IIIMP_ICState::message_proc(
    void *x_pmes
)
{
    IIIMP_message *pmes = (IIIMP_message*) x_pmes;

    switch (pmes->opcode) {
      case IM_TRIGGER_NOTIFY:
      {
	  bool flag = (pmes->v.trigger_notify.flag == 0) ? true : false;
          if (ihk->get_lang_selection())
              ihk->set_lang_selection(false);
	  return start_request()->toggle_server_convmode(flag);
      }
      case IM_HOTKEY_NOTIFY:
      {
          int hotkey_id = pmes->v.hotkey_notify.hotkey_id;
          int ikev = pmes->v.hotkey_notify.index;
          ihk->set_hotkey_mode(true);
          return ihk->process_hotkey(this, hotkey_id, ikev);
      }
      case IM_LOOKUP_CHOICE_START_REPLY:
      {
          int keycode = 0;
          return ihk->draw_lang_selection_window(this, keycode);
      }
      case IM_LOOKUP_CHOICE_DRAW_REPLY:
      {
          if (ihk->get_hotkey_mode())
              return ihk->send_hotkey_reply(this);
          return true;
      }
      case IM_LOOKUP_CHOICE_DONE_REPLY:
      {
          if (ihk->get_hotkey_mode())
              return ihk->send_hotkey_reply(this);
          return true;
      }
      case IM_SETICVALUES:
       return start_request()->set_values(pmes);
      case IM_SETICFOCUS:
       return start_request()->toggle_icfocus(true);
      case IM_UNSETICFOCUS:
       return start_request()->toggle_icfocus(false);
      case IM_RESETIC:
       return start_request()->reset();
      case IM_AUX_SETVALUES:
       return start_request()->aux_setvalues(pmes);
      case IM_AUX_GETVALUES:
       return start_request()->aux_getvalues(pmes);
      case IM_FORWARD_EVENT:
      {
	  if (ihk->get_lang_selection()) {
	      ihk->set_forward_event(true);
	      return ihk->process_lang_selection_window(this, pmes);
	  } else
	      return start_request()->forward_event(pmes);
      }
      case IM_FORWARD_EVENT_WITH_OPERATIONS:
       return start_request()->forward_event_with_operations(pmes);
      case IM_DESTROYIC:
       return start_request()->destroyic();

      default:
       LOG_ERROR("Invalid message(IC normal state):%d", pmes->opcode);
    }
    return true;
}

IIIMP_ICState::IIIMP_ICState(
    CARD16BIT ic_id,
    IIIMP_IMState* piiims,
    ICHandler *pich,
    IMLExec_ICState *pimlex
) : ICState(ic_id, piiims, pich, pimlex)
{
    pshared = new SharedState();
    pshared->conversion_mode = false;
    pshared->preedit = false;
    pshared->status = false;
    pshared->lookup_choice = false;

    ihk = new IIIMP_hotkey(this);
}

IIIMP_ICState*
IIIMP_ICState::create(
    CARD16BIT ic_id,
    IIIMP_IMState* piiims,
    ICHandler *pich
)
{
    // The current implementation use the same IMLExec.
    // Note that it will be changed later.
    IMLExec_ICState *pimlex = new IMLExec_ICState();
    // memory error;
    if (!pimlex) return NULL;
    IIIMP_ICState *pics = new IIIMP_ICState(ic_id, piiims, pich, pimlex);
    // memory error;
    if (!pics) {
	delete pimlex;
	return NULL;
    }
    return pics;
}

void
IIIMP_ICState::destroy()
{
    if (get_ichandler()) {
	/* Normally, client must send IM_DESTROYIC before terminating IC,
	   hence, ichandler must be dead here.  But when client abruptly
	   shut down the connection, we have to clean up ichandler.  */
	get_ichandler()->destroy();
	invalidate_ichandler();
    }
    ICState::destroy();
}

IMSvr *
IIIMP_ICState::get_imsvr ()
{
    IMSvr *imsvr;
    IMInputContext *pimic;

    pimic = get_iminputcontext ();
    imsvr = pimic->get_imconnection()->get_imsvr();

    if (imsvr) return imsvr;
    else return NULL;
}

LEMgr *
IIIMP_ICState::get_lemgr ()
{
    IMSvr *pimsvr = get_imsvr ();
    return pimsvr->get_lemgr();

}

IMLangList *
IIIMP_ICState::get_langlist ()
{
    LEMgr *plemgr = get_lemgr ();

    IMLangList *planglist =
        const_cast<IMLangList *>(plemgr->get_all_langlist());
    IMLangList::iterator it;
    for (it = planglist->begin (); it != planglist->end (); it++){
	// LOG_DEBUG("LangID [%s]", it->get_id ());
    }
    return planglist;
}

bool 
IIIMP_ICState::create_langimlist ()
{
    LEMgr *plemgr = get_lemgr ();
    IMDescriptorList::iterator it;
    IMLangList::iterator itt;
    int index = 1;

    IMDescriptorList *pimdlist = const_cast<IMDescriptorList *>(plemgr->get_all_imdesclist());

    langimlist.clear();
    for (it = pimdlist->begin (); it != pimdlist->end (); it++){
        u16string imname;
        u16string hrn;
        imname = it->get_imname();
        hrn = it->get_hrn();
        // LOG_DEBUG("IMName [%s]", imname.get_charstr());
        IMLangList *llist = const_cast<IMLangList *>(it->get_languages());
        for (itt = llist->begin (); itt != llist->end (); itt++) { 
	    // LOG_DEBUG("Supported Languages [%s]", itt->get_id ());
	    langimlist.push_back(LangIM(index, itt->get_id(), u16string(imname), u16string(hrn)));
	    index++;
        }
    }
    return true;
}

LangIMList *
IIIMP_ICState::get_langimlist ()
{
    return &langimlist;
}

IIIMP_ICState::~IIIMP_ICState()
{
    if (!substatep()) {
	delete pshared;
	delete get_imlexec();
        if (ihk) delete ihk;
    }
}

/*******************************************************************************
                ICState_REQUESTED
*******************************************************************************/

bool
IIIMP_ICState_REQUESTED::message_proc(
    void *x_pmes
)
{
    IIIMP_message *pmes = (IIIMP_message*) x_pmes;

    LOG_ERROR("REQUESTED state cannot accept any messages. (%d, %d, %d)",
	      get_im_id(), get_ic_id(), pmes->opcode);

    return false;
}

bool
IIIMP_ICState_REQUESTED::finish()
{
    bool flag;

    if (ihk->get_hotkey_mode()) {
	preply = iiimp_hotkey_notify_reply_new(get_iiimptrans()->get_data_s(), get_im_id(), get_ic_id());
	ihk->set_hotkey_mode(false);
    }

    if (ihk->get_forward_event()) {
	preply = iiimp_forward_event_reply_new(get_iiimptrans()->get_data_s(),
					       get_im_id(),
					       get_ic_id());
        ihk->set_forward_event(false);
    }

    flag = send(preply, true);
    if (pprev_state)
	change_state(*pprev_state, true);
    else
	reset_state();

    return flag;
}

bool
IIIMP_ICState_REQUESTED::dealing()
{
    bool result;
    // execute remaining IML instructions.
    ASSERT(send_avail_p() == true);
    result = get_imlexec()->execute();
    if (send_avail_p()) {
	if (get_imlexec()->empty())
	    result = finish() && result;
    }

    return result;
}

bool
IIIMP_ICState_REQUESTED::wait(
    int opcode
)
{
    IIIMP_ICState_WAITING *picsw = new IIIMP_ICState_WAITING(this, opcode);
    // TODO: throw memory error.
    change_state(*picsw, false);
    return true;
}

bool
IIIMP_ICState_REQUESTED::wait_nested_message(
    int opcode,
    int listener_id,
    u16string fname
)
{
    IIIMP_ICState_WAITING *picsw = new IIIMP_ICState_WAITING(this, opcode, fname, listener_id);
    // TODO: throw memory error.
    change_state(*picsw, false);
    return true;
}

bool
IIIMP_ICState_REQUESTED::wait_aux(
    int opcode,
    int index,
    u16string imname
)
{
    IIIMP_ICState_WAITING *picsw;
    picsw = new IIIMP_ICState_WAITING(this, opcode,
				      index, imname);
    // TODO: throw memory error.
    change_state(*picsw, false);
    return true;
}

IIIMP_ICState_REQUESTED::IIIMP_ICState_REQUESTED(
    IIIMP_ICState *pbase
) : IIIMP_ICState(*pbase)
{
    pprev_state = pbase;
}


/****************************************
           REQUEST functions.
****************************************/

bool
IIIMP_ICState_REQUESTED::set_values(
    IIIMP_message *pmes
)
{
    ICAttribute attr = convert_iiimp_icattr(pmes->v.seticvalues.attr_list);
    bool result = get_ichandler()->set_icattr(attr);
    preply = iiimp_seticvalues_reply_new(get_iiimptrans()->get_data_s(),
					 get_im_id(),
					 get_ic_id());
    result = dealing() && result;
    return result;
}

bool
IIIMP_ICState_REQUESTED::process_read_ns_reply(
    IIIMP_message *pmes
)
{
    int unique_listener_id = pmes->v.im_read_ns_reply.listener_id;
    int file_size = pmes->v.im_read_ns_reply.size;
    void *ptr = pmes->v.im_read_ns_reply.object;
    bool result = false;

    result = get_ichandler()->invoke_desktop_listener(get_imlexec(), unique_listener_id, file_size, ptr);

    return result;
}

bool
IIIMP_ICState_REQUESTED::toggle_server_convmode(
    bool flag
)
{
    bool result = get_ichandler()->toggle_conversion(get_imlexec(), flag);
    pshared->conversion_mode = flag;
    preply = iiimp_trigger_notify_reply_new(get_iiimptrans()->get_data_s(),
					    get_im_id(),
					    get_ic_id());

    result = dealing() && result;
    return result;
}

bool
IIIMP_ICState_REQUESTED::toggle_icfocus(
    bool flag
)
{
    bool result = get_ichandler()->toggle_focus(get_imlexec(), flag);

    if (flag)
	preply = iiimp_seticfocus_reply_new(get_iiimptrans()->get_data_s(),
					    get_im_id(),
					    get_ic_id());
    else
	preply = iiimp_unseticfocus_reply_new(get_iiimptrans()->get_data_s(),
					      get_im_id(),
					      get_ic_id());

    result = dealing() && result;
    return result;
}

bool
IIIMP_ICState_REQUESTED::reset()
{
    bool result = get_ichandler()->reset(get_imlexec());
    preply = iiimp_resetic_reply_new(get_iiimptrans()->get_data_s(),
				     get_im_id(),
				     get_ic_id());
    result = dealing() && result;
    return result;
}

bool
IIIMP_ICState_REQUESTED::aux_setvalues(
    IIIMP_message *pmes
)
{
    bool result = false;
    IIIMP_aux_setvalues_v *pauxval = &pmes->v.aux_setvalues;

    IMAuxDrawCallbackStruct aux_draw;

    u16string auxnamestr = CONV_IIIMP_STR(pauxval->input_method_name);
    if (auxnamestr.get_charstr()) {
	vector<char> auxnamev;
	auxnamev.reserve(auxnamestr.size() + 1);
	memcpy(&auxnamev[0], auxnamestr.get_charstr(), auxnamestr.size() + 1);
	aux_draw.aux_name = &auxnamev[0];
	aux_draw.aux_index = pauxval->class_index;

	{
	    // integer value array.
	    int i;
	    int num = pauxval->integer_value->count;
	    IIIMP_card32 *pc32 = pauxval->integer_value->ptr;
	    vector<int> intvalv;
	    intvalv.reserve(num);
	    aux_draw.count_integer_values = num;
	    for (i = 0; i < num; i++, pc32++) {
		intvalv.push_back(*pc32);
	    }
	    aux_draw.integer_values = &intvalv[0];

	    // string value array.
	    int count = 0;
	    IIIMP_string *pstr;
	    vector<IMText> imtextvec;

	    for (pstr = pauxval->string_value; pstr; pstr = pstr->next) {
		IMText text;
		memset(&text, 0, sizeof(text));
		text.encoding = UTF16_CODESET;
		text.char_length = pstr->len;
		text.text.utf_chars = pstr->ptr;
		imtextvec.push_back(text);
		count++;
	    }
	    aux_draw.count_string_values = count;
	    aux_draw.string_values = &imtextvec[0];

	    IMAuxEvent aux_event;
	    aux_event.type = IM_EventAuxSet;
	    aux_event.aux = &aux_draw;
	    result = get_ichandler()->send_event(get_imlexec(),
						 (IMInputEvent*) &aux_event);
	}
    }

    IIIMP_string* pimname = iiimp_string_new(get_iiimptrans()->get_data_s(),
					     pauxval->input_method_name->len,
					     pauxval->input_method_name->ptr);
    if (!pimname) {
	// TODO: we should throw memory exception.
	return false;
    }
    preply = iiimp_aux_setvalues_reply_new(get_iiimptrans()->get_data_s(),
					   get_im_id(),
					   get_ic_id(),
					   pauxval->class_index,
					   pimname);
    result = dealing() && result;
    return result;
}

bool
IIIMP_ICState_REQUESTED::aux_getvalues(
    IIIMP_message *pmes
)
{
    bool result = false;
    IIIMP_aux_getvalues_v *pauxval = &pmes->v.aux_getvalues;
    int i;

    IMAuxGetEvent aux_event;
    IMAuxDrawCallbackStruct *aux_draw, aux_data_struct;
    aux_draw = &aux_data_struct;

    vector<char> auxnamev;
    vector<int> intvalv;
    vector<IMText> imtextvec;

    u16string auxnamestr = CONV_IIIMP_STR(pauxval->input_method_name);
    if (auxnamestr.get_charstr()) {
	auxnamev.reserve(auxnamestr.size() + 1);
	memcpy(&auxnamev[0], auxnamestr.get_charstr(), auxnamestr.size() + 1);
	aux_draw->aux_name = &auxnamev[0];
	aux_draw->aux_index = pauxval->class_index;

	{
	    // integer value array.
	    int i;
	    int num = pauxval->integer_value->count;
	    IIIMP_card32 *pc32 = pauxval->integer_value->ptr;
	    intvalv.reserve(num);
	    aux_draw->count_integer_values = num;
	    for (i = 0; i < num; i++, pc32++) {
		intvalv.push_back(*pc32);
	    }
	    aux_draw->integer_values = &intvalv[0];

	    // string value array.
	    int count = 0;
	    IIIMP_string *pstr;

	    for (pstr = pauxval->string_value; pstr; pstr = pstr->next) {
		IMText text;
		memset(&text, 0, sizeof(text));
		text.encoding = UTF16_CODESET;
		text.char_length = pstr->len;
		text.text.utf_chars = pstr->ptr;
		imtextvec.push_back(text);
		count++;
	    }
	    aux_draw->count_string_values = count;
	    aux_draw->string_values = &imtextvec[0];

	    aux_event.type = IM_EventAuxGet;
	    aux_event.fromaux = aux_draw;
	    result = get_ichandler()->send_event_getvalues(get_imlexec(),
							   (IMInputEvent*) &aux_event);
	}
    }

    IIIMP_string* pimname = iiimp_string_new(get_iiimptrans()->get_data_s(),
					     pauxval->input_method_name->len,
					     pauxval->input_method_name->ptr);
    if (!pimname) {
	// TODO: we should throw memory exception.
	get_ichandler()->send_event_getvalues_finished(get_imlexec());
	return false;
    }

    // send aux_getvalues_reply
    aux_draw = aux_event.toaux;

    IIIMP_string *pstrh = NULL;
    IIIMP_card32_list *pilist = NULL;

    if (aux_draw->count_integer_values > 0) {
	// `Int' and `IIIMP_card32' is not always compatible especially
	// in array form.  Thus, it's inevitable to convert `int' array
	// manually.
	IIIMP_card32* pc32h, *pc32;
	pc32h = pc32 = new IIIMP_card32[aux_draw->count_integer_values];
	if (!pc32) {
	    get_ichandler()->send_event_getvalues_finished(get_imlexec());
	    return -1;
	}
	int *pi = aux_draw->integer_values;
	for (i = 0; i < aux_draw->count_integer_values; i++) {
	    *pc32++ = (unsigned) *pi++;
	}
	pilist = iiimp_card32_list_new(get_iiimptrans()->get_data_s(),
				       aux_draw->count_integer_values,
				       pc32h);
	if (!pilist) goto error;
	delete[] pc32h;
    }

    IIIMP_string *pstr, *pcur;
    IMText *pimt;
    for (i = 0, pimt = aux_draw->string_values;
	 i < aux_draw->count_string_values;
	 i++, pimt++) {
	pcur = convert_IMText_to_iiimp_string(get_iiimptrans()->get_data_s(),
					      pimt);
	if (!pcur) goto error;
	if (!pstrh) {
	    pstrh = pcur;
	} else {
	    pstr->next = pcur;
	}
	pstr = pcur;
    }

    preply = iiimp_aux_getvalues_reply_new(get_iiimptrans()->get_data_s(),
					   get_im_id(),
					   get_ic_id(),
					   pauxval->class_index,
					   pimname,
					   pilist,
					   pstrh);

error:
    get_ichandler()->send_event_getvalues_finished(get_imlexec());
    result = dealing() && result;

#if 0
    // FIXME. The pilist and pstrh must be freed, but freeing them will
    // cause htt_server to freeze.
    if (pilist)
	iiimp_card32_list_delete(get_iiimptrans()->get_data_s(), pilist);
    if (pstrh)
	iiimp_string_delete(get_iiimptrans()->get_data_s(), pstrh);
#endif
    return result;
}

bool
IIIMP_ICState_REQUESTED::forward_event(
    IIIMP_message *pmes
)
{
    bool result = false;
    IIIMP_contents *pc = pmes->v.forward_event.contents;

    IMInputEvent ev;
    
    switch (pc->type) {
      case IIIMP_CONTENTS_KEYEVENT:
      {
	  vector<IMKeyEventStruct> keyvec;
	  IIIMP_keyevent *piiimpkey = pc->value.keyevent_list->keyevent;
	  int count = pc->value.keyevent_list->count;
	  int i;
	  for (i = 0; i < count; i++, piiimpkey++) {
	      IMKeyEventStruct kev;
	      kev.keyCode = piiimpkey->keycode;
	      kev.keyChar = piiimpkey->keychar;
	      kev.modifier = piiimpkey->modifier;
	      kev.time_stamp = piiimpkey->time_stamp;
	      keyvec.push_back(kev);
	  }

	  IMKeyListEvent *pimkey = &ev.keylist;
	  memset(pimkey, 0, sizeof(*pimkey));
	  pimkey->type = IM_EventKeyList;
	  pimkey->n_key = count;
	  pimkey->keylist = &keyvec[0];

	  result = get_ichandler()->send_event(get_imlexec(), &ev);
      }
	   
      break;
      case IIIMP_CONTENTS_STRING:
      case IIIMP_CONTENTS_TEXT:
       // ignore
       break;
      default:
       abort();
    }

    preply = iiimp_forward_event_reply_new(get_iiimptrans()->get_data_s(),
					   get_im_id(),
					   get_ic_id());
    result = dealing() && result;
    return result;
}

bool
IIIMP_ICState_REQUESTED::forward_event_with_operations(
    IIIMP_message *pmes
)
{
    IIIMP_operation *pop = pmes->v.forward_event_with_operations_reply.operation;

    // currently do nothing.

    preply = iiimp_forward_event_with_operations_reply_new(get_iiimptrans()->get_data_s(),
							   get_im_id(),
							   get_ic_id(),
							   pop);
    // TODO: check memory error, throw exception.
    return dealing();
}

bool
IIIMP_ICState_REQUESTED::destroyic()
{
    bool result = get_ichandler()->destroy(get_imlexec());
    if (result) invalidate_ichandler();
    preply = iiimp_destroyic_reply_new(get_iiimptrans()->get_data_s(),
				       get_im_id(),
				       get_ic_id());
    mark_terminal();
    result = dealing();
    return result;
}

/****************************************
 IC operation handler for REQUESTED state
****************************************/

int
IIIMP_ICState_REQUESTED::toggle_client_convmode(
    bool flag
)
{
    LOG_DEBUG("Toggle client conversion mode to %s.",
	      flag ? "true" : "false");
    if (conversion_enable_p() != flag) {
	IIIMP_message *pmes = iiimp_trigger_notify_new(get_iiimptrans()->get_data_s(),
						       get_im_id(),
						       get_ic_id(),
						       flag ? 0 : 1);
	if (!send(pmes, true)) return -1;
	pshared->conversion_mode = flag;

	return wait(IM_TRIGGER_NOTIFY_REPLY) ? 0 : -1;
    }
    return 0;
}

int
IIIMP_ICState_REQUESTED::forward_keyevent(
    IMKeyEventStruct *pkeyevent
)
{
    IIIMP_keyevent ikev;

    ikev.keycode = pkeyevent->keyCode;
    ikev.keychar = pkeyevent->keyChar;
    ikev.modifier = pkeyevent->modifier;
    ikev.time_stamp = pkeyevent->time_stamp;

    IIIMP_keyevent_list *pikl = iiimp_keyevent_list_new(get_iiimptrans()->get_data_s(),
							1, &ikev);
    if (!pikl) return -1;

    IIIMP_contents *pcon = iiimp_contents_keyevent_list_new(get_iiimptrans()->get_data_s(),
							    pikl);
    if (!pcon) {
	iiimp_keyevent_list_delete(get_iiimptrans()->get_data_s(), pikl);
	return -1;
    }

    IIIMP_message *pmes = iiimp_forward_event_new(get_iiimptrans()->get_data_s(),
						  get_im_id(),
						  get_ic_id(),
						  pcon);
    if (!pmes) {
	iiimp_contents_delete(get_iiimptrans()->get_data_s(), pcon);
	return -1;
    }
    if (!send(pmes, true)) return -1;

    return wait(IM_FORWARD_EVENT_REPLY) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::commit_string(
    IMText *pimtext
)
{
    IIIMP_contents *pcon;

    pcon = convert_IMText_to_iiimp_contents_string(get_iiimptrans()->get_data_s(),
						   pimtext);
    if (!pcon) return -1;
    IIIMP_message *pmes = iiimp_commit_string_new(get_iiimptrans()->get_data_s(),
						  get_im_id(),
						  get_ic_id(),
						  pcon);
    if (!pmes) {
	iiimp_contents_delete(get_iiimptrans()->get_data_s(), pcon);
	return -1;
    }
    return send(pmes, true) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::preedit_start()
{
    IIIMP_message *pmes = iiimp_preedit_start_new(get_iiimptrans()->get_data_s(),
						  get_im_id(),
						  get_ic_id());
    if (!send(pmes, true)) return -1;
    pshared->preedit = true;

    return wait(IM_PREEDIT_START_REPLY) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::draw_preedit(
    IMPreeditDrawCallbackStruct *pimpdraw
)
{
    IIIMP_contents *pcon;

    if (!pshared->preedit) {
	LOG_ERROR("preedit has not been enabled yet. (%d, %d)",
		  get_im_id(), get_ic_id());
    }

    if (pimpdraw->text) {
	pcon = convert_IMText_to_iiimp_contents_text(get_iiimptrans()->get_data_s(),
						     pimpdraw->text);
    } else {
	// create empty text contents.
	IIIMP_text *ptext = iiimp_text_new(get_iiimptrans()->get_data_s(), NULL, NULL);
	if (!ptext) return -1;
	pcon = iiimp_contents_text_new(get_iiimptrans()->get_data_s(), ptext);
    }
    if (!pcon) return -1;
    IIIMP_message *pmes = iiimp_preedit_draw_new(get_iiimptrans()->get_data_s(),
						 get_im_id(), get_ic_id(),
						 pimpdraw->caret,
						 pimpdraw->chg_first,
						 pimpdraw->chg_length,
						 pcon);
    if (!pmes) {
	iiimp_contents_delete(get_iiimptrans()->get_data_s(), pcon);
	return -1;
    }
    if (!send(pmes, true)) return 0;

    return wait(IM_PREEDIT_DRAW_REPLY) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::preedit_done()
{
    IIIMP_message *pmes = iiimp_preedit_done_new(get_iiimptrans()->get_data_s(),
						 get_im_id(),
						 get_ic_id());
    if (!send(pmes, true)) return -1;
    pshared->preedit = false;

    return wait(IM_PREEDIT_DONE_REPLY) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::status_start()
{
    IIIMP_message *pmes = iiimp_status_start_new(get_iiimptrans()->get_data_s(),
						 get_im_id(),
						 get_ic_id());
    if (!send(pmes, true)) return -1;
    pshared->status = true;

    return wait(IM_STATUS_START_REPLY) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::draw_status(
    IMStatusDrawCallbackStruct *pimsdraw
)
{
    IIIMP_contents *pcon;

    if (!pshared->status) {
	LOG_ERROR("status has not been enabled yet. (%d, %d)",
		  get_im_id(), get_ic_id());
    }
    pcon = convert_IMText_to_iiimp_contents_text(get_iiimptrans()->get_data_s(),
						 pimsdraw->text);
    if (!pcon) return -1;
    IIIMP_message *pmes = iiimp_status_draw_new(get_iiimptrans()->get_data_s(),
						get_im_id(), get_ic_id(),
						pcon);
    if (!pmes) {
	iiimp_contents_delete(get_iiimptrans()->get_data_s(), pcon);
	return -1;
    }
    if (!send(pmes, true)) return 0;

    return wait(IM_STATUS_DRAW_REPLY) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::status_done()
{
    IIIMP_message *pmes = iiimp_status_done_new(get_iiimptrans()->get_data_s(),
						get_im_id(),
						get_ic_id());
    if (!send(pmes, true)) return -1;
    pshared->status = false;

    return wait(IM_STATUS_DONE_REPLY) ? 0 : -1;
}


int
IIIMP_ICState_REQUESTED::lookup_start(
    IMLookupStartCallbackStruct *pimls
)
{
    int master, direction, labelowner;
    LayoutInfo *playout;

    if (pimls->whoIsMaster == IMIsMaster) {
	playout = pimls->IMPreference;
	master = IM_LOOKUP_CHOICE_START_SERVER_IS_MASTER;
    } else {
	ASSERT(pimls->whoIsMaster == CBIsMaster);
	playout = pimls->CBPreference;
	master = IM_LOOKUP_CHOICE_START_CLIENT_IS_MASTER;
    }

    if (playout->drawUpDirection == DrawUpHorizontally) {
	direction = IM_LOOKUP_CHOICE_START_DRAWING_UP_HORIZONTALLY;
    } else {
	ASSERT(playout->drawUpDirection == DrawUpVertically);
	direction = IM_LOOKUP_CHOICE_START_DRAWING_UP_VERTICALLY;
    }

    if (playout->whoOwnsLabel == IMOwnsLabel) {
	labelowner = IM_LOOKUP_CHOICE_START_SERVER_OWNS_LABEL;
    } else {
	ASSERT(playout->whoOwnsLabel == CBOwnsLabel);
	labelowner = IM_LOOKUP_CHOICE_START_CLIENT_OWNS_LABEL;
    }

    IIIMP_message *pmes;
    pmes = iiimp_lookup_choice_start_new(get_iiimptrans()->get_data_s(),
					 get_im_id(),
					 get_ic_id(),
					 master,
					 playout->choice_per_window,
					 playout->nrows,
					 playout->ncolumns,
					 direction,
					 labelowner);
    if (!send(pmes, true)) return -1;
    pshared->lookup_choice = true;

    return wait(IM_LOOKUP_CHOICE_START_REPLY) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::draw_lookup(
    IMLookupDrawCallbackStruct *pimld
)
{
    if (!pshared->lookup_choice) {
	LOG_ERROR("lookup-choice has not been enabled yet. (%d, %d)",
		  get_im_id(), get_ic_id());
    }

    IIIMP_message *pmes;
    IIIMP_text *pchoiceh, *pchoice;
    IIIMP_text *plabelh, *plabel;
    IIIMP_text *ptitle;
    IMChoiceObject *pco;
    int i;
    pchoiceh = plabelh = NULL;

    ptitle = convert_IMText_to_iiimp_text(get_iiimptrans()->get_data_s(),
					  pimld->title);
    if (!ptitle) goto error;

    IIIMP_text *pcur;
    for (i = 0, pco = pimld->choices;
	 i < pimld->n_choices;
	 i++, pco++) {
	// value.
	pcur = convert_IMText_to_iiimp_text(get_iiimptrans()->get_data_s(),
					    pco->value);
	if (!pcur) goto error;
	if (!pchoiceh) {
	    pchoiceh = pcur;
	} else {
	    pchoice->next = pcur;
	}
	pchoice = pcur;

	//label
	pcur = convert_IMText_to_iiimp_text(get_iiimptrans()->get_data_s(),
					    pco->label);
	if (!pcur) goto error;
	if (!plabelh) {
	    plabelh = pcur;
	} else {
	    plabel->next = pcur;
	}
	plabel = pcur;
    }

    pmes = iiimp_lookup_choice_draw_new(get_iiimptrans()->get_data_s(),
					get_im_id(),
					get_ic_id(),
					pimld->index_of_first_candidate,
					pimld->index_of_last_candidate,
					pimld->index_of_current_candidate,
					pchoiceh,
					plabelh,
					ptitle);
    if (!pmes) goto error;

    if (!send(pmes, true)) return -1;

    return wait(IM_LOOKUP_CHOICE_DRAW_REPLY) ? 0 : -1;

error:
    if (ptitle) iiimp_text_delete(get_iiimptrans()->get_data_s(), ptitle);
    if (plabelh) iiimp_text_delete(get_iiimptrans()->get_data_s(), plabelh);
    if (pchoiceh) iiimp_text_delete(get_iiimptrans()->get_data_s(), pchoiceh);

    return -1;
}

int
IIIMP_ICState_REQUESTED::lookup_process(
    IMLookupProcessCallbackStruct *pimlp
)
{
    if (!pshared->lookup_choice) {
	LOG_ERROR("lookup-choice has not been enabled yet. (%d, %d)",
		  get_im_id(), get_ic_id());
    }

    IIIMP_card16 type, value;
    if (pimlp->type == LookupIndex) {
	type = IM_LOOKUP_CHOICE_PROCESS_INDEX;
	value = pimlp->value.index_of_choice_selected;
    } else {
	ASSERT(pimlp->type == LookupPage);
	type = IM_LOOKUP_CHOICE_PROCESS_PAGE;
	// Note that page_operation_id must have the same value
	// as that of libiiimp.
	value = pimlp->value.page_operation_id;
    }
    IIIMP_message *pmes;
    pmes = iiimp_lookup_choice_process_new(get_iiimptrans()->get_data_s(),
					   get_im_id(),
					   get_ic_id(),
					   type,
					   value);

    if (!send(pmes, true)) return -1;

    return wait(IM_LOOKUP_CHOICE_PROCESS_REPLY) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::lookup_done()
{
    IIIMP_message *pmes;
    pmes = iiimp_lookup_choice_done_new(get_iiimptrans()->get_data_s(),
					get_im_id(),
					get_ic_id());
    if (!send(pmes, true)) return -1;
    pshared->lookup_choice = false;

    return wait(IM_LOOKUP_CHOICE_DONE_REPLY) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::aux_start(
    IMAuxStartCallbackStruct *pimauxstart
)
{
    IIIMP_message *pmes;
    u16string auxname(pimauxstart->aux_name);
    IIIMP_string *iiimpauxname;
    int index = pimauxstart->aux_index;

    iiimpauxname = iiimp_string_new(get_iiimptrans()->get_data_s(),
				    auxname.size(),
				    auxname.data());
    if (!iiimpauxname) return -1;

    pmes = iiimp_aux_start_new(get_iiimptrans()->get_data_s(),
			       get_im_id(),
			       get_ic_id(),
			       index,
			       iiimpauxname);
			       
    if (!send(pmes, true)) return -1;

    return wait_aux(IM_AUX_START_REPLY, index, auxname) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::draw_aux(
    IMAuxDrawCallbackStruct *pimauxdraw
)
{
    int i;
    IIIMP_message *pmes;
    u16string auxname(pimauxdraw->aux_name);
    IIIMP_string *iiimpauxname;
    int index = pimauxdraw->aux_index;
    IIIMP_string *pstrh = NULL;
    IIIMP_card32_list *pilist = NULL;

    iiimpauxname = iiimp_string_new(get_iiimptrans()->get_data_s(),
				    auxname.size(),
				    auxname.data());
    if (!iiimpauxname) return -1;

    if (pimauxdraw->count_integer_values > 0) {
	// `int' and `IIIMP_card32' is not always compatible especially
	// in array form.  Thus, it's inevitable to convert `int' array
	// manually.
	IIIMP_card32* pc32h, *pc32;
	pc32h = pc32 = new IIIMP_card32[pimauxdraw->count_integer_values];
	if (!pc32) return -1;
	int *pi = pimauxdraw->integer_values;
	for (i = 0; i < pimauxdraw->count_integer_values; i++) {
	    *pc32++ = (unsigned) *pi++;
	}
	pilist = iiimp_card32_list_new(get_iiimptrans()->get_data_s(),
				       pimauxdraw->count_integer_values,
				       pc32h);
	delete[] pc32h;
	if (!pilist) goto error;
    }

    IIIMP_string *pstr, *pcur;
    IMText *pimt;
    for (i = 0, pimt = pimauxdraw->string_values;
	 i < pimauxdraw->count_string_values;
	 i++, pimt++) {
	pcur = convert_IMText_to_iiimp_string(get_iiimptrans()->get_data_s(),
					      pimt);
	if (!pcur) goto error;
	if (!pstrh) {
	    pstrh = pcur;
	} else {
	    pstr->next = pcur;
	}
	pstr = pcur;
    }

    pmes = iiimp_aux_draw_new(get_iiimptrans()->get_data_s(),
			      get_im_id(),
			      get_ic_id(),
			      index,
			      iiimpauxname,
			      pilist,
			      pstrh);
			      
    if (!send(pmes, true)) return -1;

    return wait_aux(IM_AUX_DRAW_REPLY, index, auxname) ? 0 : -1;

error:
    if (iiimpauxname) iiimp_string_delete(get_iiimptrans()->get_data_s(), iiimpauxname);
    if (pilist) iiimp_card32_list_delete(get_iiimptrans()->get_data_s(), pilist);
    if (pstrh) iiimp_string_delete(get_iiimptrans()->get_data_s(), pstrh);
    return -1;
}

int
IIIMP_ICState_REQUESTED::aux_done(
    IMAuxDoneCallbackStruct *pimauxdone
)
{
    IIIMP_message *pmes;
    u16string auxname(pimauxdone->aux_name);
    IIIMP_string *iiimpauxname;
    int index = pimauxdone->aux_index;

    iiimpauxname = iiimp_string_new(get_iiimptrans()->get_data_s(),
				    auxname.size(),
				    auxname.data());
    if (!iiimpauxname) return -1;

    pmes = iiimp_aux_done_new(get_iiimptrans()->get_data_s(),
			      get_im_id(),
			      get_ic_id(),
			      index,
			      iiimpauxname);
			       
    if (!send(pmes, true)) return -1;

    return wait_aux(IM_AUX_DONE_REPLY, index, auxname) ? 0 : -1;
}

int
IIIMP_ICState_REQUESTED::read_ns(
    IMReadNSStruct *prns
)
{
    IIIMP_message *pmes;
    u16string fname(prns->filename);
    IIIMP_string *iiimp_fname;

    iiimp_fname = iiimp_string_new(get_iiimptrans()->get_data_s(),
				   fname.size(),
				   fname.data());
    if (!iiimp_fname) return -1;
    pmes = iiimp_read_ns_new(get_iiimptrans()->get_data_s(),
			     get_im_id(),
			     get_ic_id(),
			     prns->listener_id,
			     iiimp_fname);
    if (!send(pmes, true)) return -1;
    return wait_nested_message(IM_READ_NS_REPLY, prns->listener_id, fname) ? 0 : -1;
}

/*******************************************************************************
                ICState_WAITING
*******************************************************************************/

bool
IIIMP_ICState_WAITING::match_waiting_message(
    IIIMP_message *pmes
)
{
    bool flag = (opcode == pmes->opcode);
    if (flag && wait_aux_message) {
	int midx = pmes->v.aux_simple.class_index;
	u16string mimname = CONV_IIIMP_STR(pmes->v.aux_simple.input_method_name);
	flag = ((index == midx) && (imname.compare(mimname) == 0));
    } else if (flag && wait_ns_message) {
	return start_request()->process_read_ns_reply(pmes);
    }
    return flag;
}

void
IIIMP_ICState_WAITING::restore_state()
{
    // change_state() will delete `this'.
    IIIMP_ICState_REQUESTED *preq = pstreq;
    change_state(*pstreq);
    preq->dealing();
}

bool
IIIMP_ICState_WAITING::message_proc(
    void *x_pmes
)
{
    IIIMP_message *pmes = (IIIMP_message*) x_pmes;

    if (!match_waiting_message(pmes)) {
#if 0
	LOG_ERROR("Client sends an invalid message. (%d,%d,%d)\n"
		  "Now ICState is waiting for (%d,%d,%d).",
		  pmes->opcode, pmes->im_id, pmes->ic_id,
		  opcode, get_im_id(), get_ic_id());
	return false;
#else
	// It may cause W->R transition.
	return IIIMP_ICState::message_proc(pmes);
#endif
    }
    restore_state();
    
    return true;
}

IIIMP_ICState_WAITING::IIIMP_ICState_WAITING(
    IIIMP_ICState_REQUESTED *pbase,
    int x_opcode
) : IIIMP_ICState(*pbase)
{
    pstreq = pbase;
    opcode = x_opcode;
    wait_aux_message = false;
    wait_ns_message = false;
}

IIIMP_ICState_WAITING::IIIMP_ICState_WAITING(
    IIIMP_ICState_REQUESTED *pbase,
    int x_opcode,
    int x_index,
    u16string x_imname
) : IIIMP_ICState(*pbase)
{
    pstreq = pbase;
    opcode = x_opcode;
    index = x_index;
    imname = x_imname;
    wait_aux_message = true;
    wait_ns_message = false;
}

IIIMP_ICState_WAITING::IIIMP_ICState_WAITING(
    IIIMP_ICState_REQUESTED *pbase,
    int x_opcode,
    u16string x_filename,
    int x_listener_id
) : IIIMP_ICState(*pbase)
{
    pstreq = pbase;
    opcode = x_opcode;
    listener_id = x_listener_id;
    filename = x_filename;
    wait_ns_message = true;
}

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