/*
  iiimcf.c
*/

#define NODE_DEFAULT      "localhost"
#define SERVICE_DEFAULT   "9010"
#define TLS_DEFAULT       "9011"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif

#include "iiimcfint.h"
#include <EIMIL.h>

#define IIIMCF_DEFAULT_IC_HASH_TABLE_SIZE 17
#define IIIMCF_DEFAULT_TIMEOUT 10000

static void free_object_descriptor(
    IIIMCF_object_descriptor *pod,
    int n
);

/********************************************************************************
			       util. functions
********************************************************************************/

IIIMP_card16*
iiimcf_make_string(
    const IIIMP_card16 *str,
    int len
)
{
    IIIMP_card16 *pu16;

    pu16 = (IIIMP_card16*) malloc(sizeof(*pu16) * (len + 1));
    if (!pu16) return NULL;
    if (len > 0)
	memcpy(pu16, str, sizeof(*pu16) * len);
    pu16[len] = 0;

    return pu16;
}

int
iiimcf_string_length(
    const IIIMP_card16 *str
)
{
    int i;
    for (i = 0; *str; str++) i++;
    return i;
}

IIIMP_card16*
iiimcf_duplicate_string(
    const IIIMP_card16 *str
)
{
    if (!str) return NULL;
    return iiimcf_make_string(str, iiimcf_string_length(str));
}

void
iiimcf_lock_handle(
    IIIMCF_handle_rec *ph
)
{
    THREAD_ID_OBJECT tid;

    THREAD_SET_CURRENT_ID(tid);
    if ((ph->thread_lock_count == 0)
	|| (!THREAD_ID_EQUAL(ph->thread_owner_id, tid))) {
	LOCK_SYNC_OBJECT(ph->so);
	ASSERT(ph->thread_lock_count == 0);
	ph->thread_owner_id = tid;
    }
    ph->thread_lock_count++;
}

void
iiimcf_unlock_handle(
    IIIMCF_handle_rec *ph
)
{
    THREAD_ID_OBJECT tid;

    THREAD_SET_CURRENT_ID(tid);

    ASSERT(THREAD_ID_EQUAL(ph->thread_owner_id, tid));
    ASSERT(ph->thread_lock_count > 0);
    ph->thread_lock_count--;
    if (ph->thread_lock_count == 0) {
	UNLOCK_SYNC_OBJECT(ph->so);
    }
}


/********************************************************************************
			       message handlers
********************************************************************************/

void
iiimcf_unregister_langs(
    int n,
    IIIMCF_language_rec **ppl
)
{
    IIIMCF_language_rec **pp, *p;
    int i;

    if (!ppl) return;
    for (pp = ppl, i = 0; i < n; i++, pp++) {
	p = *pp;
	if (p) {
	    if (p->lang_id) free(p->lang_id);
	    if (p->hrn) free(p->hrn);
	    free(p);
	}
    }
    free(ppl);
}

static IIIMF_status
iiimcf_register_langs(
    IIIMCF_handle_rec *ph,
    IIIMP_string *pstr
)
{
    IIIMCF_language_rec **ppl, *pl;
    IIIMP_string *p;
    IIIMP_card16 *pu;
    char *plang_id, *pc;
    int i, n;

    iiimcf_unregister_langs(ph->num_langs, ph->pplangs);
    ph->pplangs = NULL;
    ph->num_langs = 0;
    for (n = 0, p = pstr; p; p = p->next) n++;
    ppl = (IIIMCF_language_rec**) malloc(sizeof(IIIMCF_language_rec*) * n);
    if (!ppl) return IIIMF_STATUS_MALLOC;
    memset(ppl, 0, sizeof(IIIMCF_language_rec*) * n);
    for (i = 0; i < n; i++) {
	pl = (IIIMCF_language_rec*) malloc(sizeof(*pl));
	if (!pl) {
	    iiimcf_unregister_langs(n, ppl);
	    return IIIMF_STATUS_MALLOC;
	}
	memset(pl, 0, sizeof(*pl));
	ppl[i] = pl;
    }

    ph->num_langs = n;
    ph->pplangs = ppl;
    for (p = pstr; p; p = p->next, ppl++) {
	pl = *ppl;
	plang_id = (char*) malloc(sizeof(char) * (p->len + 1));
	if (!plang_id) {
	    iiimcf_unregister_langs(ph->num_langs, ph->pplangs);
	    ph->num_langs = 0;
	    ph->pplangs = NULL;
	    return IIIMF_STATUS_MALLOC;
	}
	for (i = 0, pu = p->ptr, pc = plang_id;i < p->len; i++) {
	    *pc++ = (*pu++ & 0x7F);
	}
	*pc = '\0';
	pl->lang_id = plang_id;
    }

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_register_trigger_keys(
    IIIMCF_handle_rec *ph,
    IIIMP_message *pmes
)
{
    int i, n;
    IIIMP_keyevent_list *pimkl;
    IIIMP_keyevent *pimk;
    IIIMCF_keyevent *pkev;
    IIIMP_register_trigger_keys_v *pimrk = &pmes->v.register_trigger_keys;
    ASSERT(pmes->opcode == IM_REGISTER_TRIGGER_KEYS);

    pimkl = pimrk->trigger_on;
    n = pimkl->count;
    if (n > 0) {
	pkev = (IIIMCF_keyevent*) malloc(sizeof(IIIMCF_keyevent) * n);
	if (!pkev) return IIIMF_STATUS_MALLOC;

	if (ph->pon_keys) free(ph->pon_keys);
	ph->pon_keys = pkev;
	pimk = pimkl->keyevent;
	for (i = 0; i < n; i++, pkev++, pimk++) {
	    pkev->keycode = pimk->keycode;
	    pkev->keychar = pimk->keychar;
	    pkev->modifier = pimk->modifier;
	    pkev->time_stamp = pimk->time_stamp;
	}

	ph->num_on_keys = i;
    }

    pimkl = pimrk->trigger_off;
    n = pimkl->count;
    if (n > 0) {
	pkev = (IIIMCF_keyevent*) malloc(sizeof(IIIMCF_keyevent) * n);
	if (!pkev) {
	    if (ph->pon_keys) free(ph->pon_keys);
	    ph->num_on_keys = 0;
	    return IIIMF_STATUS_MALLOC;
	}

	if (ph->poff_keys) free(ph->poff_keys);
	ph->poff_keys = pkev;
	pimk = pimkl->keyevent;
	for (i = 0; i < n; i++, pkev++, pimk++) {
	    pkev->keycode = pimk->keycode;
	    pkev->keychar = pimk->keychar;
	    pkev->modifier = pimk->modifier;
	    pkev->time_stamp = pimk->time_stamp;
	}

	ph->num_off_keys = i;
    }

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_register_hotkeys(
    IIIMCF_handle_rec *ph,
    IIIMP_message *pmes
)
{
    int i, j, n, n_hkp, n_hotkeys;
    HOTKEY_LIST *hklist;
    IIIMCF_hotkey *hk;
    IIIMCF_keyevent *pkev;
    IIIMP_keyevent *pimk;

    IIIMP_register_hotkeys_v *pimrk = &pmes->v.register_hotkeys;

    ASSERT(pmes->opcode == IM_REGISTER_HOTKEYS);

    n_hkp = ph->num_of_hkprofiles;
    hklist = pimrk->hotkeys;
    n_hotkeys = pimrk->hotkeys->count;

    if (!n_hkp) {
      ph->phk_profile = (IIIMCF_hotkey_profile *) malloc(10 * sizeof(IIIMCF_hotkey_profile));
      memset(ph->phk_profile, 0, 10 * sizeof(IIIMCF_hotkey_profile));
    } else if (n_hkp > 10) {
      ph->phk_profile = (IIIMCF_hotkey_profile *) realloc((IIIMCF_hotkey_profile *)ph->phk_profile, (n_hkp + 1) * sizeof(IIIMCF_hotkey_profile));
    }
    if (!ph->phk_profile) return IIIMF_STATUS_MALLOC;
    ph->phk_profile[n_hkp].scope = pimrk->scope;
    ph->phk_profile[n_hkp].profile_id = pimrk->profile_id;

    if (n_hotkeys > 0) {
	hk = (IIIMCF_hotkey *) malloc(sizeof(IIIMCF_hotkey) * n_hotkeys);
	if (!hk) return IIIMF_STATUS_MALLOC;
        ph->phk_profile[n_hkp].phot_keys = hk;

	for(i=0; i<n_hotkeys; i++) {
	    hk[i].hotkey_id = hklist->hotkey[i].hotkeyctrl.hotkey_id;
	    hk[i].state_flag = hklist->hotkey[i].hotkeyctrl.state_flag;
	    hk[i].action_flag = hklist->hotkey[i].hotkeyctrl.action_flag;

	    n = hklist->hotkey[i].hotkeylist->count;
	    if (n > 0) {
		pkev = (IIIMCF_keyevent*) malloc(sizeof(IIIMCF_keyevent) * n);
		if (!pkev) return IIIMF_STATUS_MALLOC;

		hk[i].keys = pkev;
		pimk = hklist->hotkey[i].hotkeylist->keyevent;
		for (j = 0; j < n; j++, pkev++, pimk++) {
		    pkev->keycode = pimk->keycode;
		    pkev->keychar = pimk->keychar;
		    pkev->modifier = pimk->modifier;
		    pkev->time_stamp = pimk->time_stamp;
		}
		hk[i].nkeys = j;
	    }
	}
        ph->phk_profile[n_hkp].num_hotkeys = n_hotkeys;
    }
    ph->num_of_hkprofiles = n_hkp + 1; 
    return IIIMF_STATUS_SUCCESS;
}

static IIIMF_status
iiimcf_register_object_descriptor_list(
    IIIMCF_handle_rec *ph,
    IIIMP_object_descriptor *piiimp_objdesc
)
{
    int n;
    IIIMP_object_descriptor *pimod;
    IIIMCF_object_descriptor *pod, *podh;
    IIIMP_card16 *pu16;

    free_object_descriptor(ph->pobjdesc, ph->object_descriptor_size);
    ph->pobjdesc = NULL;
    ph->object_descriptor_size = 0;
    for (n = 0, pimod = piiimp_objdesc; pimod; pimod = pimod->next) n++;

    pod = podh = (IIIMCF_object_descriptor*) malloc(sizeof(*pod) * n);
    if (!pod) return IIIMF_STATUS_MALLOC;
    memset(podh, 0, sizeof(*podh) * n);

    for (pimod = piiimp_objdesc; pimod; pimod = pimod->next, pod++) {
	pod->category = pimod->category;
	pod->size = pimod->size;
	pod->predefined_id = pimod->id_pre;
	pod->dynamic_id = pimod->id_dyn;
	pu16 = iiimcf_make_string(pimod->rdun->ptr, pimod->rdun->len);
	if (!pu16) {
	    free_object_descriptor(podh, n);
	    return IIIMF_STATUS_MALLOC;
	}
	pod->domain = pu16;

	pu16 = iiimcf_make_string(pimod->hrn->ptr, pimod->hrn->len);
	if (!pu16) {
	    free_object_descriptor(podh, n);
	    return IIIMF_STATUS_MALLOC;
	}
	pod->hrn = pu16;

	pu16 = iiimcf_make_string(pimod->signature->ptr,
				  pimod->signature->len);
	if (!pu16) {
	    free_object_descriptor(podh, n);
	    return IIIMF_STATUS_MALLOC;
	}
	pod->signature = pu16;

	pu16 = iiimcf_make_string(pimod->user->ptr, pimod->user->len);
	if (!pu16) {
	    free_object_descriptor(podh, n);
	    return IIIMF_STATUS_MALLOC;
	}
	pod->user = pu16;
    }
    ph->pobjdesc = podh;
    ph->object_descriptor_size = n;

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_setimvalues(
    IIIMCF_handle_rec *ph,
    IIIMP_message *pmes
)
{
    IIIMF_status st;
    IIIMP_imattribute *pimattrs;
    ASSERT(pmes->opcode == IM_SETIMVALUES);

    for (pimattrs = pmes->v.setimvalues.attr_list;
	 pimattrs;
	 pimattrs = pimattrs->next) {
	switch (pimattrs->id_pre) {
	  case IIIMP_IMATTRIBUTE_INPUT_METHOD_LIST:
	   st = iiimcf_register_input_method_list(ph, pimattrs->value.inputmethod_descriptor);
	   if (st != IIIMF_STATUS_SUCCESS) return st;
	   break;

	  case IIIMP_IMATTRIBUTE_OBJECT_DESCRIPTOR_LIST:
	   st = iiimcf_register_object_descriptor_list(ph, pimattrs->value.object_descriptor);
	   if (st != IIIMF_STATUS_SUCCESS) return st;
	   break;

	  default:
	   /* simply ignore */
	   break;
	}
    }

    return IIIMF_STATUS_SUCCESS;
}

static void
free_object_descriptor(
    IIIMCF_object_descriptor *pod,
    int n
)
{
    int i;
    IIIMCF_object_descriptor *pod2 = pod;

    if (!pod) return;
    for (i = 0; i < n; i++, pod++) {
	if (pod->domain)
	    free((IIIMP_card16*) pod->domain);
	if (pod->hrn)
	    free((IIIMP_card16*) pod->hrn);
	if (pod->signature)
	    free((IIIMP_card16*) pod->signature);
	if (pod->user)
	    free((IIIMP_card16*) pod->user);
    }
    free(pod2);
}


/********************************************************************************
		            connection/send/receive management
********************************************************************************/

static void
iiimcf_delete_stream(
    IIIMCF_handle_rec *ph
)
{
    if (ph->pconf && ph->pconf->use_tls) {
        iiimf_delete_tls_stream(ph->pstream);
    } else {
        iiimf_delete_socket_stream(ph->pstream);
    }
    ph->pstream = NULL;
}

IIIMF_status
iiimcf_connect(
    IIIMCF_handle_rec *ph
)
{
    IIIMF_status st = IIIMF_STATUS_SUCCESS;
    IIIMP_message *pmes;

    if (!ph->data_s) ph->data_s = iiimp_data_s_new();
    if (!ph->data_s) return IIIMF_STATUS_MALLOC;

    if (!ph->pstream) {
        IIIMCF_client_conf *pconf;
        if (ph->pconf) {
            pconf = ph->pconf;
            ph->pconf = NULL;
        } else {
            pconf = ph->penv->pconf;
        }
        for ( ; pconf; pconf = pconf->next) {
            /* use TLS */
            if (pconf->use_tls) {
                /* check whther libiiimp is compiled with TLS support */
                st = iiimf_tls_supported();
                if (st != IIIMF_STATUS_SUCCESS) continue;

                st = iiimf_connect_tls_stream(pconf->server_node ? pconf->server_node : NODE_DEFAULT,
                                              pconf->service ? pconf->service : TLS_DEFAULT,
                                              IIIMCF_DEFAULT_TIMEOUT,
                                              &ph->pstream);
                if (st != IIIMF_STATUS_SUCCESS) continue;

                /* TODO*/
                iiimf_tls_set_certificate(ph->pstream,
                                          pconf->cert_file,
                                          pconf->cert_key,
                                          pconf->ca_file,
                                          pconf->ca_path);
            } else {
	        st = iiimf_connect_socket_stream(pconf->server_node ? pconf->server_node : NODE_DEFAULT,
				                 pconf->service ? pconf->service : SERVICE_DEFAULT,
					         IIIMCF_DEFAULT_TIMEOUT,
        					 &ph->pstream);
            }

            if (st == IIIMF_STATUS_SUCCESS) {
                /* set current configuration to IIIMCF_handle */
                ph->pconf = pconf;
                break;
            }
        }
    }
    if (st != IIIMF_STATUS_SUCCESS) {
        /* finally try to connect to 127.0.0.1:9010 */
        st = iiimf_connect_socket_stream(NODE_DEFAULT, SERVICE_DEFAULT,
                                         IIIMCF_DEFAULT_TIMEOUT,
                                         &ph->pstream);
    }
    if (st != IIIMF_STATUS_SUCCESS) return st;

    st = iiimcf_create_im_connect_message(ph, &pmes);
    if (st != IIIMF_STATUS_SUCCESS) return st;

    st = iiimf_stream_send(ph->pstream, ph->data_s, pmes);
    iiimp_message_delete(ph->data_s, pmes);
    if (st != IIIMF_STATUS_SUCCESS) goto reconnect;

    st = iiimcf_wait_message(ph, NULL, IM_CONNECT_REPLY, &pmes);
    if (st != IIIMF_STATUS_SUCCESS) {
        if (pmes) iiimp_message_delete(ph->data_s, pmes);
        goto reconnect;
    }

    ph->im_id = pmes->im_id;
    st = iiimp_data_s_limit_protocol_version(ph->data_s,
					     ph->server_protocol_version);
    if (st != IIIMF_STATUS_SUCCESS) return st;
    st = iiimcf_register_langs(ph, pmes->v.connect_reply.language);
    iiimp_message_delete(ph->data_s, pmes);

    /* send CLIENTDESCRIPTOR */
    st = iiimcf_create_client_descriptor_message(ph, &pmes);
    if (st != IIIMF_STATUS_SUCCESS) return st;

    st = iiimf_stream_send(ph->pstream, ph->data_s, pmes);
    iiimp_message_delete(ph->data_s, pmes);
    if (st != IIIMF_STATUS_SUCCESS) return st;
    st = iiimcf_wait_message(ph, NULL, IM_SETIMVALUES_REPLY, NULL);

    return st;

reconnect:
    /* 
     * in this case, iiimcf connects iiimsf successfully,
     * but iiimsf refuses the connection.
     *
     * so, try to next iiimsf if any.
     */
    if (!ph->pconf) return st;
    if (ph->pconf->next) {
        /* first, destroy current stream */
        iiimcf_delete_stream(ph);
        /* ph->pconf store current configuration */
        ph->pconf = ph->pconf->next;
        return iiimcf_connect(ph);
    }
    return st;
}

static IIIMF_status
iiimcf_hungup(
    IIIMCF_handle_rec *ph
)
{
    ph->im_id = -1;

    /* Should we delete downloaded objects?  */
    /* iiimcf_delete_all_downloaded_objects(ph); */
    if (ph->pstream) {
        iiimcf_delete_stream(ph);
    }
#if 0
    if (ph->data_s) {
	iiimp_data_s_delete(ph->data_s);
	ph->data_s = NULL;
    }
#endif

    return iiimcf_cleanup_context(ph, 0);
}

static IIIMF_status
iiimcf_disconnect(
    IIIMCF_handle_rec *ph
)
{
    IIIMF_status st1 = IIIMF_STATUS_SUCCESS, st2 = IIIMF_STATUS_SUCCESS;
    IIIMP_message *pmes;

    if (IIIMCF_IS_CONNECTED(ph)) {
	pmes = iiimp_disconnect_new(ph->data_s, ph->im_id);
	if (pmes) {
	    st1 = iiimf_stream_send(ph->pstream, ph->data_s, pmes);
	    iiimp_message_delete(ph->data_s, pmes);
	    if (st1 == IIIMF_STATUS_SUCCESS) {
		st1 = iiimcf_wait_message(ph, NULL, IM_DISCONNECT_REPLY, NULL);
	    }
	} else {
	    st1 = IIIMF_STATUS_MALLOC;
	}
    }

    st2 = iiimcf_cleanup_context(ph, 1);
    if (st2 != IIIMF_STATUS_SUCCESS) return st2;

    return st1;
}

IIIMF_status
iiimcf_send_message(
    IIIMCF_handle_rec *ph,
    IIIMP_message *pmes,
    int deletep
)
{
    IIIMF_status st;

    st = iiimf_stream_send(ph->pstream, ph->data_s, pmes);
    if (deletep) iiimp_message_delete(ph->data_s, pmes);

    if (st == IIIMF_STATUS_SUCCESS)
	return IIIMF_STATUS_SUCCESS;

    if ((st == IIIMF_STATUS_CONNECTION_CLOSED)
	|| (st == IIIMF_STATUS_STREAM_SEND)) {
	iiimcf_hungup(ph);
    }

    return st;
}

IIIMF_status
iiimcf_receive_message(
    IIIMCF_handle_rec *ph,
    IIIMP_message **ppmes
)
{
    IIIMF_status st;

    st = iiimf_stream_receive(ph->pstream, ph->data_s, ppmes);

    if (st == IIIMF_STATUS_SUCCESS) {
	if ((ph->im_id >= 0) && ((*ppmes)->im_id != ph->im_id)) {
	    iiimp_message_delete(ph->data_s, *ppmes);
	    *ppmes = NULL;
	    return IIIMF_STATUS_IM_INVALID;
	}
	return IIIMF_STATUS_SUCCESS;
    }

    if ((st == IIIMF_STATUS_CONNECTION_CLOSED)
	|| (st == IIIMF_STATUS_STREAM_RECEIVE)) {
	iiimcf_hungup(ph);
    }

    return st;
}

/********************************************************************************
			    create & destroy handle
********************************************************************************/

IIIMF_status
iiimcf_create_handle(
    IIIMCF_attr attr,
    IIIMCF_handle *phandle
)
{
    IIIMF_status st;
    int disable_automatic_connection_restoration = 0;
    IIIMCF_handle_rec *ph;
    IIIMCF_context_rec **ppc;

    st = iiimcf_attr_get_integer_value(attr, IIIMCF_ATTR_DISABLE_AUTOMATIC_RESTORATION,
				       &disable_automatic_connection_restoration);
    if ((st != IIIMF_STATUS_SUCCESS)
	&& (st != IIIMF_STATUS_NO_ATTR_VALUE)) {
	return st;
    }

    ph = (IIIMCF_handle_rec*) malloc(sizeof(*ph));
    if (!ph) {
	return IIIMF_STATUS_MALLOC;
    }
    memset(ph, 0, sizeof(*ph));
    ph->context_table_size = IIIMCF_DEFAULT_IC_HASH_TABLE_SIZE;
    ppc = (IIIMCF_context_rec**) malloc(sizeof(*ppc) * ph->context_table_size);
    if (!ppc) {
	free(ph);
	return IIIMF_STATUS_MALLOC;
    }
    memset(ppc, 0, sizeof(*ppc) * ph->context_table_size);
    ph->ppcontext_table = ppc;
    ph->im_id = -1;

    /* When not received IM_PROTOCOL_VERSION,
       we assume the server supports protocol version 2.  */
    ph->server_protocol_version = 2;

    INIT_SYNC_OBJECT(ph->so);
    if (disable_automatic_connection_restoration)
	ph->disable_automatic_connection_restoration = 1;
    else
	ph->disable_automatic_connection_restoration = 0;

    st = iiimcf_create_client_env(attr, &ph->penv);
    if (st != IIIMF_STATUS_SUCCESS) {
	DESTROY_SYNC_OBJECT(ph->so);
	free(ppc);
	free(ph);
	return st;
    }

    st = iiimcf_register_predefined_components(ph);
    if (st != IIIMF_STATUS_SUCCESS) {
	iiimcf_delete_client_env(ph->penv);
	DESTROY_SYNC_OBJECT(ph->so);
	free(ppc);
	free(ph);
	return st;
    }
    
    st = iiimcf_connect(ph);
    if (st != IIIMF_STATUS_SUCCESS) {
        iiimcf_destroy_handle(ph);
        return st;
    }

    *phandle = ph;

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_destroy_handle(
    IIIMCF_handle handle
)
{
    IIIMCF_handle_rec *ph = (IIIMCF_handle_rec*) handle;
    int i,j;

    iiimcf_disconnect(ph);
    iiimcf_unregister_langs(ph->num_langs, ph->pplangs);
    iiimcf_unregister_input_method_list(ph->num_input_methods,
					ph->ppinput_methods);
    /* must be before iiimcf_delete_client_env */
    if (ph->pstream)  iiimcf_delete_stream(ph);
    iiimcf_delete_client_env(ph->penv);
    iiimcf_delete_all_components(ph);
    iiimcf_delete_all_downloaded_objects(ph);
    free_object_descriptor(ph->pobjdesc, ph->object_descriptor_size);
    if (ph->pon_keys) free(ph->pon_keys);
    if (ph->poff_keys) free(ph->poff_keys);

    if (ph->phk_profile) {
	for (i=0; i < ph->num_of_hkprofiles; ++i) {
	    if (ph->phk_profile[i].phot_keys) {
		for (j=0; j < ph->phk_profile[i].num_hotkeys; ++j) {
		    if (ph->phk_profile[i].phot_keys[j].keys) free(ph->phk_profile[i].phot_keys[j].keys);
		}
		free(ph->phk_profile[i].phot_keys);
	    }
	}
        free(ph->phk_profile);
    }
    if (ph->data_s) iiimp_data_s_delete(ph->data_s);
    free(ph->ppcontext_table);
    DESTROY_SYNC_OBJECT(ph->so);
    free(ph);
    return IIIMF_STATUS_SUCCESS;
}

/********************************************************************************
			initialization & finalization
********************************************************************************/

static int iiimcf_inited = 0;

IIIMF_status
iiimcf_initialize(
    IIIMCF_attr attr
)
{
    if (iiimcf_inited) return IIIMF_STATUS_SUCCESS;
    if (!EIMIL_initialize())
	return IIIMF_STATUS_FAIL;
    iiimcf_inited = 1;

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_finalize()
{
    EIMIL_finalize();
    iiimcf_inited = 0;
    return IIIMF_STATUS_SUCCESS;
}

/********************************************************************************
			  misc. information
********************************************************************************/

IIIMF_status
iiimcf_get_version_number(
    IIIMCF_handle handle,
    int flag,
    int *pversion_number
)
{
    IIIMCF_handle_rec *ph = (IIIMCF_handle_rec*) handle;

    if (flag & ~IIIMCF_VERSION_FLAGS_MASK) {
	return IIIMF_STATUS_ARGUMENT;
    }
    if (flag & IIIMCF_LIBRARY_VERSION) {
	if ((flag & ~IIIMCF_VERSION_FLAGS_ITEM_MASK)
	    != IIIMCF_LIBRARY_VERSION) {
	    return IIIMF_STATUS_ARGUMENT;
	}
	if (flag & IIIMCF_MAJOR_VERSION) {
	    if ((flag & ~IIIMCF_VERSION_FLAGS_CATEGORY_MASK)
		!= IIIMCF_MAJOR_VERSION) {
		return IIIMF_STATUS_ARGUMENT;
	    }
	    *pversion_number = LIBIIIMCF_MAJOR_VERSION;
	} else if (flag & IIIMCF_MINOR_VERSION) {
	    if ((flag & ~IIIMCF_VERSION_FLAGS_CATEGORY_MASK)
		!= IIIMCF_MINOR_VERSION) {
		return IIIMF_STATUS_ARGUMENT;
	    }
	    *pversion_number = LIBIIIMCF_MINOR_VERSION;
	} else {
	    return IIIMF_STATUS_ARGUMENT;
	}
    } else if (flag & IIIMCF_PROTOCOL_VERSION) {
	if ((flag & ~IIIMCF_VERSION_FLAGS_ITEM_MASK)
	    != IIIMCF_PROTOCOL_VERSION) {
	    return IIIMF_STATUS_ARGUMENT;
	}
	if (flag & IIIMCF_MAJOR_VERSION) {
	    if ((flag & ~IIIMCF_VERSION_FLAGS_CATEGORY_MASK)
		!= IIIMCF_MAJOR_VERSION) {
		return IIIMF_STATUS_ARGUMENT;
	    }
	    *pversion_number = iiimp_data_s_get_protocol_version(ph->data_s);
	} else if (flag & IIIMCF_MINOR_VERSION) {
	    if ((flag & ~IIIMCF_VERSION_FLAGS_CATEGORY_MASK)
		!= IIIMCF_MINOR_VERSION) {
		return IIIMF_STATUS_ARGUMENT;
	    }
	    /* Now IM_CONNECT has no minor version */
	    *pversion_number = 0;
	} else {
	    return IIIMF_STATUS_ARGUMENT;
	}
    } else {
	return IIIMF_STATUS_ARGUMENT;
    }

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_get_supported_languages(
    IIIMCF_handle handle,
    int *pnum_langs,
    IIIMCF_language **pplangs
)
{
    IIIMCF_handle_rec *ph = (IIIMCF_handle_rec*) handle;

    *pnum_langs = ph->num_langs;
    *pplangs = (IIIMCF_language*) ph->pplangs;

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_get_language_id(
    IIIMCF_language lang,
    const char **plangid
)
{
    IIIMCF_language_rec *pl = (IIIMCF_language_rec*) lang;

    *plangid = pl->lang_id;
    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_get_trigger_keys(
    IIIMCF_handle handle,
    int *pnum_on_keys,
    const IIIMCF_keyevent **pponkeys,
    int *pnum_off_keys,
    const IIIMCF_keyevent **ppoffkeys
)
{
    IIIMCF_handle_rec *ph = (IIIMCF_handle_rec*) handle;

    if ((ph->num_on_keys == 0)
	&& (ph->num_off_keys == 0))
	return IIIMF_STATUS_STATIC_EVENT_FLOW;

    *pnum_on_keys = ph->num_on_keys;
    *pponkeys = ph->pon_keys;
    *pnum_off_keys = ph->num_off_keys;
    *ppoffkeys = ph->poff_keys;

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_get_object_descriptor_list(
    IIIMCF_handle handle,
    int *pnum_object_descriptors,
    const IIIMCF_object_descriptor **ppdesc
)
{
    IIIMCF_handle_rec *ph = (IIIMCF_handle_rec*) handle;

    *pnum_object_descriptors = ph->object_descriptor_size;
    *ppdesc = ph->pobjdesc;

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_get_im_id(
    IIIMCF_handle handle,
    int *pim_id
)
{
    IIIMCF_handle_rec *ph = (IIIMCF_handle_rec*) handle;

    if (!ph) {
	*pim_id = -1;
	return IIIMF_STATUS_IM_INVALID;
    }

    *pim_id = ph->im_id;

    if (ph->im_id < 0)
	return IIIMF_STATUS_IM_INVALID;

    return IIIMF_STATUS_SUCCESS;
}

void
check_pathname(
    char *p
)
{
    char *q;
    int index = 0;

    while (*p) {
        if ((q = (strstr(p, "../")))) {
            index = p - q;
            if (index < 0)
                index = -index;
            strcpy(p+index, p+index+3);
        } else if ((q = (strstr(p, "//")))) {
            index = p - q;
            if (index < 0)
                index = -index;
            strcpy(p+index, p+index+1);
        } else if ((q = (strstr(p, "./")))) {
            index = p - q;
            if (index < 0)
                index = -index;
            strcpy(p+index, p+index+2);
        } else {
            break ;
        }
    }
    return;
}

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