/*
  message.c
*/

#include "iiimcfint.h"

static IIIMF_status
iiimcf_reply_to_foward_event_with_operations(
    IIIMCF_context_rec *pc,
    IIIMP_message *pmes
)
{
    IIIMCF_handle_rec *ph = pc->ph;
    IIIMP_operation *pop = pmes->v.forward_event_with_operations_reply.operation;
    IIIMP_message *preply;

    preply = iiimp_forward_event_with_operations_reply_new(ph->data_s,
							   ph->im_id,
							   pc->ic_id,
							   pop);
    if (!preply) return IIIMF_STATUS_MALLOC;

    return iiimcf_send_message(ph, preply, 1);
}

static IIIMF_status
iiimcf_reply_aux_message(
    IIIMCF_handle_rec *ph,
    IIIMP_message *pmes
)
{
    IIIMP_string *pimname;
    IIIMP_message *preply;

    if (pmes->opcode == IM_AUX_START) {
	pimname = iiimp_string_new(ph->data_s,
				   pmes->v.aux_start.input_method_name->len,
				   pmes->v.aux_start.input_method_name->ptr);
	if (!pimname) return IIIMF_STATUS_MALLOC;
	preply = iiimp_aux_start_reply_new(ph->data_s, pmes->im_id, pmes->ic_id,
					   pmes->v.aux_start.class_index,
					   pimname);
    } else if (pmes->opcode == IM_AUX_DRAW) {
	pimname = iiimp_string_new(ph->data_s,
				   pmes->v.aux_draw.input_method_name->len,
				   pmes->v.aux_draw.input_method_name->ptr);
	if (!pimname) return IIIMF_STATUS_MALLOC;
	preply = iiimp_aux_draw_reply_new(ph->data_s, pmes->im_id, pmes->ic_id,
					  pmes->v.aux_draw.class_index,
					  pimname);
    } else if (pmes->opcode == IM_AUX_DONE) {
	pimname = iiimp_string_new(ph->data_s,
				   pmes->v.aux_done.input_method_name->len,
				   pmes->v.aux_done.input_method_name->ptr);
	if (!pimname) return IIIMF_STATUS_MALLOC;
	preply = iiimp_aux_done_reply_new(ph->data_s, pmes->im_id, pmes->ic_id,
					  pmes->v.aux_done.class_index,
					  pimname);
    } else {
	return IIIMF_STATUS_ARGUMENT;
    }

    if (!preply) {
	iiimp_string_delete(ph->data_s, pimname);
	return IIIMF_STATUS_MALLOC;
    }

    return iiimcf_send_message(ph, preply, 1);
}

static IIIMF_status
iiimcf_reply_message(
    IIIMCF_handle_rec *ph,
    IIIMP_message *pmes
)
{
    if (!pmes) return IIIMF_STATUS_MALLOC;
    return iiimcf_send_message(ph, pmes, 1);
}

/*
 * Process the message, pmes.
 * pmes is automatically deleted.
 */
IIIMF_status
iiimcf_process_message(
    IIIMCF_handle_rec *ph,
    IIIMP_message *pmes
)
{
    IIIMF_status st = IIIMF_STATUS_SUCCESS;
    IIIMF_status st_r = IIIMF_STATUS_SUCCESS;
    IIIMP_message *preply;
    IIIMCF_context_rec *pc;

    if (pmes->ic_id >= 0) {
	pc = iiimcf_lookup_context(ph, pmes->ic_id);
	if (!pc) {
            iiimp_message_delete(ph->data_s, pmes);
            return IIIMF_STATUS_IC_INVALID;
        }
    } else {
	pc = NULL;
    }
    switch (pmes->opcode) {
      case IM_CONNECT_REPLY:
       if (ph->im_id >= 0)
	   return IIIMF_STATUS_SEQUENCE_STATE;
       ph->im_id = pmes->im_id;
       break;

      case IM_PROTOCOL_VERSION:
       ph->server_protocol_version = pmes->v.protocol_version.number;
       ph->num_of_hkprofiles = 0;
       ph->num_of_ns = 0;
       ph->curr_profile_id = 1;
       break;

      case IM_REGISTER_TRIGGER_KEYS:
       st = iiimcf_register_trigger_keys(ph, pmes);
       break;

      case IM_REGISTER_HOTKEYS:
       st = iiimcf_register_hotkeys(ph, pmes);
       break;

      case IM_TRIGGER_NOTIFY:
       if (pmes->v.trigger_notify.flag == 0)
	   st = iiimcf_receive_trigger_notify(pc, 1);
       else
	   st = iiimcf_receive_trigger_notify(pc, 0);

       preply = iiimp_trigger_notify_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;

      case IM_HOTKEY_NOTIFY:
       st = iiimcf_receive_hotkey_notify(pc);

       preply = iiimp_hotkey_notify_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;

      case IM_HOTKEY_STATE_NOTIFY:
       if (pmes->v.hotkey_state_notify.current_state_flag) {
           st = iiimcf_receive_trigger_notify(pc, 1);
       } else {
           st = iiimcf_receive_trigger_notify(pc, 0);
       }

       /*
	 preply = iiimp_hotkey_state_notify_reply_new(ph->data_s, ph->im_id, pc->ic_id);
	 st_r = iiimcf_reply_message(ph, preply);
       */
       break;

      case IM_SELECT_HOTKEY_PROFILE:
       ph->curr_scope = pmes->v.select_hotkey_profile.scope;
       ph->curr_profile_id = pmes->v.select_hotkey_profile.profile_id;

       /* maybe we sould reply this message */
       break;

      case IM_READ_NS:
       st = iiimcf_process_ns(ph, pmes, pc->ic_id);
       break;

      case IM_FORWARD_EVENT:
       st = iiimcf_receive_forwarded_event(pc, pmes);
       preply = iiimp_forward_event_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;

      case IM_FORWARD_EVENT_WITH_OPERATIONS:
       st_r = iiimcf_reply_to_foward_event_with_operations(pc, pmes);
       break;

      case IM_PREEDIT_START:
       st = iiimcf_toggle_preedit(pc, 1);
       preply = iiimp_preedit_start_reply_new(ph->data_s, ph->im_id, pc->ic_id, -1);
       st_r = iiimcf_reply_message(ph, preply);
       break;
      case IM_PREEDIT_DRAW:
       st = iiimcf_update_preedit(pc, pmes);
       preply = iiimp_preedit_draw_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;
      case IM_PREEDIT_DONE:
       st = iiimcf_toggle_preedit(pc, 0);
       preply = iiimp_preedit_done_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;

      case IM_STATUS_START:
       st = iiimcf_toggle_status(pc, 1);
       preply = iiimp_status_start_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;
      case IM_STATUS_DRAW:
       st = iiimcf_update_status(pc, pmes);
       preply = iiimp_status_draw_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;
      case IM_STATUS_DONE:
       st = iiimcf_toggle_status(pc, 0);
       preply = iiimp_status_done_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;

      case IM_LOOKUP_CHOICE_START:
       st = iiimcf_lookup_choice_start(pc, pmes);
       preply = iiimp_lookup_choice_start_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;
      case IM_LOOKUP_CHOICE_DRAW:
       st = iiimcf_update_lookup_choice(pc, pmes);
       preply = iiimp_lookup_choice_draw_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;
      case IM_LOOKUP_CHOICE_PROCESS:
       st = iiimcf_process_lookup_choice(pc, pmes);
       preply = iiimp_lookup_choice_process_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;
      case IM_LOOKUP_CHOICE_DONE:
       st = iiimcf_lookup_choice_done(pc);
       preply = iiimp_lookup_choice_done_reply_new(ph->data_s, ph->im_id, pc->ic_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;

      case IM_AUX_START:
       st = iiimcf_enable_aux(pc, pmes);
       st_r = iiimcf_reply_aux_message(ph, pmes);
       break;
      case IM_AUX_DRAW:
       st = iiimcf_update_aux_draw(pc, pmes);
       st_r = iiimcf_reply_aux_message(ph, pmes);
       break;
      case IM_AUX_DONE:
       st = iiimcf_disable_aux(pc, pmes);
       st_r = iiimcf_reply_aux_message(ph, pmes);
       break;

      case IM_COMMIT_STRING:
       st = iiimcf_commit_string(pc, pmes);
       break;

      case IM_SETIMVALUES:
       st = iiimcf_setimvalues(ph, pmes);
       preply = iiimp_setimvalues_reply_new(ph->data_s, ph->im_id);
       st_r = iiimcf_reply_message(ph, preply);
       break;
    }

    iiimp_message_delete(ph->data_s, pmes);
    if (st_r != IIIMF_STATUS_SUCCESS) return st_r;

    return st;
}

static int
match_message(
    IIIMCF_handle_rec *ph,
    IIIMCF_context_rec *pc,
    int opcode,
    IIIMP_message *pmes
)
{
    if (opcode != pmes->opcode) return 0;
    if ((ph->im_id < 0) && (opcode == IM_CONNECT_REPLY)) return 1;
    if (ph->im_id != pmes->im_id) return 0;
    if (!pc) return 1;
    if (pc->ic_id != pmes->ic_id) return 0;

    return 1;
}

IIIMF_status
iiimcf_wait_message(
    IIIMCF_handle_rec *ph,
    IIIMCF_context_rec *pc,
    int opcode,
    IIIMP_message **ppmes
)
{
    IIIMF_status st;
    IIIMP_message *pmes;

    for (;;) {
	st = iiimcf_receive_message(ph, &pmes);
	if (st != IIIMF_STATUS_SUCCESS) return st;

	if (match_message(ph, pc, opcode, pmes)) {
	    if (ppmes) {
		*ppmes = pmes;
	    } else {
		iiimp_message_delete(ph->data_s, pmes);
	    }
	    return IIIMF_STATUS_SUCCESS;
	}

	st = iiimcf_process_message(ph, pmes);
	if (st != IIIMF_STATUS_SUCCESS) return st;
    }

    return IIIMF_STATUS_SUCCESS;
}

/*
 * Send pmes, which is deleted, and then wait for the responce of
 * (pc, opcode), return set it to ppmes if it is not NULL.
 * Notice that this function allows pmes == *ppmes!!!
 */
IIIMF_status
iiimcf_request_message(
    IIIMCF_handle_rec *ph,
    IIIMP_message *pmes,
    IIIMCF_context_rec *pc,
    int opcode,
    IIIMP_message **ppmes
)
{
    IIIMF_status st;

    IIIMCF_LOCK_HANDLE(ph);

    /* By other threads, IC may be invalidated.
       So check it first because valid IC is indispensable
       for reply message.  */
    if (pc && IIIMCF_IS_IC_INVALID(pc)) {
        iiimp_message_delete(ph->data_s, pmes);
	IIIMCF_UNLOCK_HANDLE(ph);
	return IIIMF_STATUS_IC_INVALID;
    }

    if (!IIIMCF_IS_CONNECTED(ph)) {
	if (ph->disable_automatic_connection_restoration) {
            iiimp_message_delete(ph->data_s, pmes);
            IIIMCF_UNLOCK_HANDLE(ph);
	    return IIIMF_STATUS_CONNECTION_CLOSED;
        }

	st = iiimcf_connect(ph);
	if (st != IIIMF_STATUS_SUCCESS) {
            iiimp_message_delete(ph->data_s, pmes);
	    IIIMCF_UNLOCK_HANDLE(ph);
	    return st;
	}
    }

    st = iiimcf_send_message(ph, pmes, 1);
    if (st == IIIMF_STATUS_SUCCESS) {
	st = iiimcf_wait_message(ph, NULL, opcode, ppmes);
    }
    IIIMCF_UNLOCK_HANDLE(ph);

    return st;
}

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