/*
  aux.c
*/

#include <stdlib.h>
#include <string.h>
#include "iiimcfint.h"

static IIIMCF_aux_rec*
create_aux_rec(
    IIIMCF_context_rec *pc,
    int namelen,
    const IIIMP_card16 *name,
    IIIMP_card32 index
);

static IIIMCF_event_rec*
create_aux_event(
    IIIMCF_event_type type,
    IIIMCF_aux_rec *pa
);

void
iiimcf_delete_aux_event(
    IIIMCF_event_rec *pe
);

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

IIIMF_status
iiimcf_enable_aux(
    IIIMCF_context_rec *pc,
    IIIMP_message *pmes
)
{
    IIIMCF_aux_rec *pa;
    IIIMP_aux_start_v *pimas;
    IIIMCF_event_rec *pe;

    ASSERT(pmes->opcode == IM_AUX_START);
    pimas = &pmes->v.aux_start;


    pa = create_aux_rec(pc,
			pimas->input_method_name->len,
			pimas->input_method_name->ptr,
			pimas->class_index);
    if (!pa) return IIIMF_STATUS_MALLOC;
    pa->mode = 1;

    pe = create_aux_event(IIIMCF_EVENT_TYPE_AUX_START, pa);
    if (!pe) return IIIMF_STATUS_MALLOC;

    return iiimcf_store_event(pc, pe);
}

static IIIMF_status
process_aux_data (
    IIIMCF_context_rec *pc,
    IIIMP_message *pmes,
    IIIMCF_event_type aux_event_type
)
{
    IIIMCF_aux_rec *pa;
    IIIMP_aux_draw_v *pimad;
    IIIMCF_event_rec *pe;

    pimad = &pmes->v.aux_draw;

    pa = create_aux_rec(pc,
			pimad->input_method_name->len,
			pimad->input_method_name->ptr,
			pimad->class_index);
    if (!pa) return IIIMF_STATUS_MALLOC;

    /* integer value */
    {
	int n;
	IIIMP_card32 *pc32s;

	if (pimad->integer_value) {
	    n = pimad->integer_value->count;
	    pc32s = pimad->integer_value->ptr;
	} else {
	    n = 0;
	    pc32s = NULL;
	}

	if (n > 0) {
	    IIIMP_card32 *pc32d;

	    pc32d = (IIIMP_card32*) realloc(pa->pintvals, sizeof(*pc32d) * n);
	    if (!pc32d) {
		return IIIMF_STATUS_MALLOC;
	    }
	    pa->pintvals = pc32d;
	    memcpy(pc32d, pc32s, sizeof(*pc32s) * n);
	} else {
	    if (pa->pintvals) free(pa->pintvals);
	    pa->pintvals = NULL;
	}
	pa->num_intvals = n;
    }
    /* string value */
    {
	int i, n;
	IIIMP_string *pimstr;
	IIIMP_card16 **pstrs;

	n = 0;
	for (pimstr = pimad->string_value; pimstr; pimstr = pimstr->next) n++;
	if (n > pa->num_strvals) {
	    pstrs = (IIIMP_card16**) realloc(pa->pstrs, sizeof(*pstrs) * n);
	    if (!pstrs) return IIIMF_STATUS_MALLOC;
	    memset(pstrs + pa->num_strvals, 0, (n - pa->num_strvals) * sizeof(*pstrs));
	    pa->pstrs = pstrs;
	} else if (n < pa->num_strvals) {
	    pstrs = pa->pstrs + n;
	    for (i = n; i < pa->num_strvals; i++, pstrs++) {
		if (*pstrs) {
		  free(*pstrs);
		  *pstrs = NULL;
		}
	    }
	    if (n == 0) {
		free(pa->pstrs);
		pa->pstrs = NULL;
	    }
	    pstrs = pa->pstrs;
	} else {
	    pstrs = pa->pstrs;
	}
	pa->num_strvals = n;
	for (pimstr = pimad->string_value; pimstr;
	     pimstr = pimstr->next, pstrs++) {
	    if (*pstrs) free(*pstrs);
	    *pstrs = iiimcf_make_string(pimstr->ptr, pimstr->len);
	    if (!*pstrs) {
		return IIIMF_STATUS_MALLOC;
	    }
	}
    }

    pe = create_aux_event(aux_event_type, pa);
    if (!pe) return IIIMF_STATUS_MALLOC;

    return iiimcf_store_event(pc, pe);
}

IIIMF_status
iiimcf_update_aux_draw(
    IIIMCF_context_rec *pc,
    IIIMP_message *pmes
)
{
    ASSERT(pmes->opcode == IM_AUX_DRAW);
    return process_aux_data (pc, pmes, IIIMCF_EVENT_TYPE_AUX_DRAW);
}

IIIMF_status
iiimcf_update_aux_getvaluesreply(
    IIIMCF_context_rec *pc,
    IIIMP_message *pmes
)
{
    ASSERT(pmes->opcode == IM_AUX_GETVALUES_REPLY);
    return process_aux_data (pc, pmes, IIIMCF_EVENT_TYPE_AUX_GETVALUES);
}

IIIMF_status
iiimcf_disable_aux(
    IIIMCF_context_rec *pc,
    IIIMP_message *pmes
)
{
    IIIMCF_aux_rec *pa;
    IIIMP_aux_done_v *pimad;
    IIIMCF_event_rec *pe;

    ASSERT(pmes->opcode == IM_AUX_DONE);
    pimad = &pmes->v.aux_done;


    pa = create_aux_rec(pc,
			pimad->input_method_name->len,
			pimad->input_method_name->ptr,
			pimad->class_index);
    if (!pa) return IIIMF_STATUS_MALLOC;
    pa->mode = 0;

    pe = create_aux_event(IIIMCF_EVENT_TYPE_AUX_DONE, pa);
    if (!pe) return IIIMF_STATUS_MALLOC;

    return iiimcf_store_event(pc, pe);
}

/********************************************************************************
			       internal services
********************************************************************************/

static int
iiimcf_UTF16_strncmp(
    int maxlen,
    const IIIMP_card16 *pstr1,
    const IIIMP_card16 *pstr2
)
{
    int i;
    for (i = 0;i < maxlen; pstr1++, pstr2++, i++) {
	if (*pstr1 > *pstr2) return 1;
	if (*pstr1 < *pstr2) return -1;
	if (*pstr1 == 0) return 0;
    }
    if (*pstr2 == 0) return 0;
    return -1;
}

static IIIMCF_aux_rec*
find_aux_rec(
    IIIMCF_context_rec *pc,
    int namelen,
    const IIIMP_card16 *name,
    IIIMP_card32 index
)
{
    IIIMCF_aux_rec *pa;

    for (pa = pc->paux; pa; pa = pa->pnext) {
	if ((pa->index == index)
	    && (iiimcf_UTF16_strncmp(namelen, name, pa->name) == 0))
	    return pa;
    }
    /* fail */
    return NULL;
}

static IIIMCF_aux_rec*
create_aux_rec(
    IIIMCF_context_rec *pc,
    int namelen,
    const IIIMP_card16 *name,
    IIIMP_card32 index
)
{
    IIIMCF_aux_rec *pa;

    pa = find_aux_rec(pc, namelen, name, index);
    if (pa) return pa;

    pa = (IIIMCF_aux_rec*) malloc(sizeof(*pa));
    if (!pa) return NULL;
    memset(pa, 0, sizeof(*pa));

    {
	IIIMP_card16 *pu;

	pu = (IIIMP_card16*) malloc(sizeof(*pu) * (namelen + 1));
	if (!pu) {
	    free(pa);
	    return NULL;
	}
	memcpy(pu, name, sizeof(*pu) * namelen);
	pu[namelen] = 0;
	pa->name = pu;
    }
    pa->index = index;

    pa->pnext = pc->paux;
    pc->paux = pa;

    return pa;
}

static IIIMCF_event_rec*
create_aux_event(
    IIIMCF_event_type type,
    IIIMCF_aux_rec *pa
)
{
    IIIMCF_event_rec *pe;
    IIIMCF_auxevent *pae;

    ASSERT((type == IIIMCF_EVENT_TYPE_AUX_START)
	   || (type == IIIMCF_EVENT_TYPE_AUX_DONE)
	   || (type == IIIMCF_EVENT_TYPE_AUX_GETVALUES)
	   || (type == IIIMCF_EVENT_TYPE_AUX_DRAW));

    pe = iiimcf_make_event(type);
    if (!pe) return NULL;

    pae = (IIIMCF_auxevent*) malloc(sizeof(*pae));
    if (!pae) goto memory_error;
    memset(pae, 0, sizeof(*pae));
    pe->v.pauxevent = pae;

    pae->name = iiimcf_duplicate_string(pa->name);
    if (!pae->name) goto memory_error;
    pae->index = pa->index;

    if ((type == IIIMCF_EVENT_TYPE_AUX_DRAW)
	   || (type == IIIMCF_EVENT_TYPE_AUX_GETVALUES)) {
	pae->num_intvals = pa->num_intvals;
	if (pa->num_intvals > 0) {
	    int bytelen = sizeof(IIIMP_card32) * pae->num_intvals;
	    pae->pintvals = (IIIMP_card32*) malloc(bytelen);
	    if (!pae->pintvals) goto memory_error;
	    memcpy(pae->pintvals, pa->pintvals, bytelen);
	}
	pae->num_strvals = pa->num_strvals;
	if (pa->num_strvals > 0) {
	    int i;
	    int bytelen = sizeof(IIIMP_card16*) * pae->num_strvals;
	    IIIMP_card16 **ppus, **ppud;

	    pae->pstrs = (IIIMP_card16**) malloc(bytelen);
	    if (!pae->pstrs) goto memory_error;
	    memset(pae->pstrs, 0, bytelen);
	    for (i = 0, ppud = pae->pstrs, ppus = pa->pstrs;
		 i < pa->num_strvals;
		 i++, ppud++, ppus++) {
		if (*ppus) {
		    *ppud = iiimcf_duplicate_string(*ppus);
		    if (!*ppud) goto memory_error;
		}
	    }
	}
    }

    return pe;

memory_error:
    iiimcf_delete_aux_event(pe);
    free(pe);

    return NULL;
}

IIIMF_status
iiimcf_forward_aux_setvalues(
    IIIMCF_context_rec *pc,
    IIIMCF_event_rec *pe
)
{
    IIIMF_status st;
    IIIMCF_handle_rec *ph = pc->ph;
    IIIMP_data_s *pds = ph->data_s;
    IIIMCF_auxevent *pae = pe->v.pauxevent;
    IIIMP_string *pimname, *pimstrs;
    IIIMP_card32_list *pimc32l;
    IIIMP_message *pmes;

    pimname = NULL;
    pimc32l = NULL;
    pimstrs = NULL;

    pimname = iiimp_string_new(pds, iiimcf_string_length(pae->name), pae->name);
    if (!pimname) return IIIMF_STATUS_MALLOC;
    pimc32l = iiimp_card32_list_new(pds, pae->num_intvals, pae->pintvals);
    if (!pimc32l) goto memory_error;
    {
	int i;
	IIIMP_card16 **pstr;
	IIIMP_string *pimstr;

	for (i = 0, pstr = (pae->pstrs + pae->num_strvals - 1);
	     i < pae->num_strvals;
	     i++, pstr--) {
	    pimstr = iiimp_string_new(pds, iiimcf_string_length(*pstr), *pstr);
	    if (!pimstr) goto memory_error;
	    pimstr->next = pimstrs;
	    pimstrs = pimstr;
	}
    }
    pmes = iiimp_aux_setvalues_new(pds, ph->im_id, pc->ic_id,
				   pae->index, pimname, pimc32l,
				   pimstrs);
    if (!pmes) goto memory_error;

    st = iiimcf_request_message(ph, pmes, pc, IM_AUX_SETVALUES_REPLY, NULL);

    return st;

memory_error:
    if (pimname) iiimp_string_delete(pds, pimname);
    if (pimc32l) iiimp_card32_list_delete(pds, pimc32l);
    if (pimstrs) iiimp_string_list_delete(pds, pimstrs);

    return IIIMF_STATUS_MALLOC;
}

IIIMF_status
iiimcf_forward_aux_getvalues(
    IIIMCF_context_rec *pc,
    IIIMCF_event_rec *pe
)
{
    IIIMF_status st;
    IIIMCF_handle_rec *ph = pc->ph;
    IIIMP_data_s *pds = ph->data_s;
    IIIMCF_auxevent *pae = pe->v.pauxevent;
    IIIMP_string *pimname, *pimstrs;
    IIIMP_card32_list *pimc32l;
    IIIMP_message *pmes;
    IIIMP_message *prepmes;

    pimname = NULL;
    pimc32l = NULL;
    pimstrs = NULL;

    pimname = iiimp_string_new(pds, iiimcf_string_length(pae->name), pae->name);
    if (!pimname) return IIIMF_STATUS_MALLOC;
    pimc32l = iiimp_card32_list_new(pds, pae->num_intvals, pae->pintvals);
    if (!pimc32l) goto memory_error;
    {
	int i;
	IIIMP_card16 **pstr;
	IIIMP_string *pimstr;

	for (i = 0, pstr = (pae->pstrs + pae->num_strvals - 1);
	     i < pae->num_strvals;
	     i++, pstr--) {
	    pimstr = iiimp_string_new(pds, iiimcf_string_length(*pstr), *pstr);
	    if (!pimstr) goto memory_error;
	    pimstr->next = pimstrs;
	    pimstrs = pimstr;
	}
    }
    pmes = iiimp_aux_getvalues_new(pds, ph->im_id, pc->ic_id,
				   pae->index, pimname, pimc32l,
				   pimstrs);
    if (!pmes) goto memory_error;

    prepmes = NULL;
    st = iiimcf_request_message(ph, pmes, pc, IM_AUX_GETVALUES_REPLY, &prepmes);

    if (prepmes)
    {
	st = iiimcf_update_aux_getvaluesreply(pc, prepmes);
    }

    return st;

memory_error:
    if (pimname) iiimp_string_delete(pds, pimname);
    if (pimc32l) iiimp_card32_list_delete(pds, pimc32l);
    if (pimstrs) iiimp_string_list_delete(pds, pimstrs);

    return IIIMF_STATUS_MALLOC;
}

void
iiimcf_delete_all_aux_data(
    IIIMCF_context_rec *pc
)
{
    int i, n;
    IIIMCF_aux_rec *pa, *pa2;
    IIIMP_card16 **pstrs;

    for (pa = pc->paux; pa; pa = pa2) {
	pa2 = pa->pnext;
	if (pa->name) free(pa->name);
	if (pa->pintvals) free(pa->pintvals);
	n = pa->num_strvals;
	pstrs = pa->pstrs;
	if (pstrs) {
	    for (i = 0; i < n; i++, pstrs++) {
		if (*pstrs) free(*pstrs);
	    }
	    free(pa->pstrs);
	}
	free(pa);
    }
}

void
iiimcf_delete_aux_event(
    IIIMCF_event_rec *pe
)
{
    IIIMCF_auxevent *pae = pe->v.pauxevent;

    if (pae) {
	if (pae->name) free(pae->name);
	if (pae->pintvals) free(pae->pintvals);
	if (pae->pstrs) {
	    int i;
	    for (i = 0; i < pae->num_strvals; i++) {
		if (pae->pstrs[i]) free(pae->pstrs[i]);
	    }
	    free(pae->pstrs);
	}
	free(pae);
    }
}

/********************************************************************************
			            APIs
********************************************************************************/

#if 0 /* Should we implement this feature?  */
IIIMF_status
iiimcf_get_updated_auxes(
    IIIMCF_context context,
    int *pupdated_aux_num,
    const IIIMP_card16 ***ppaux_name
)
{
}
#endif

IIIMF_status
iiimcf_get_aux_draw_data(
    IIIMCF_context context,
    const IIIMP_card16 *aux_name,
    IIIMP_card32 class_idx,
    int *pnum_intvals,
    const IIIMP_card32 **ppintvals,
    int *pnum_strvals,
    const IIIMP_card16 ***ppstrs
)
{
    IIIMCF_aux_rec *pa;
    IIIMCF_context_rec *pc = (IIIMCF_context_rec*) context;
    int len;

    if (IIIMCF_IS_IC_INVALID(pc)) return IIIMF_STATUS_IC_INVALID;
    len = iiimcf_string_length(aux_name);
    pa = find_aux_rec(pc, len, aux_name, class_idx);
    if ((!pa) || (!pa->mode))
	return IIIMF_STATUS_NO_AUX;

    if (pnum_intvals) *pnum_intvals = pa->num_intvals;
    if (ppintvals) *ppintvals = pa->pintvals;
    if (pnum_strvals) *pnum_strvals = pa->num_strvals;
    if (ppstrs) *ppstrs = (const IIIMP_card16**) pa->pstrs;

    return IIIMF_STATUS_SUCCESS;
}

IIIMF_status
iiimcf_create_aux_setvalues_event(
    const IIIMP_card16 *aux_name,
    IIIMP_card32 class_idx,
    int num_intvals,
    const IIIMP_card32 *pintvals,
    int num_strvals,
    const IIIMP_card16 **pstrs,
    IIIMCF_event *pevent
)
{
    IIIMCF_event_rec *pe;
    IIIMCF_auxevent *pae;

    pe = iiimcf_make_event(IIIMCF_EVENT_TYPE_AUX_SETVALUES);
    if (!pe) return IIIMF_STATUS_MALLOC;

    pae = (IIIMCF_auxevent*) malloc(sizeof(*pae));
    if (!pae) goto memory_error;
    memset(pae, 0, sizeof(*pae));
    pe->v.pauxevent = pae;

    pae->index = class_idx;
    pae->name = iiimcf_duplicate_string(aux_name);
    if (!pae->name) goto memory_error;

    /* integer value */
    if (num_intvals > 0) {
	IIIMP_card32 *pc32;

	pc32 = (IIIMP_card32*) malloc(sizeof(*pc32) * num_intvals);
	if (!pc32) goto memory_error;
	memcpy(pc32, pintvals, sizeof(*pc32) * num_intvals);
	pae->pintvals = pc32;
	pae->num_intvals = num_intvals;
    }
    /* string value */
    if (num_strvals > 0) {
	int i;
	IIIMP_card16 **ppu16;

	ppu16 = (IIIMP_card16**) malloc(sizeof(*ppu16) * num_strvals);
	if (!ppu16) goto memory_error;
	pae->pstrs = ppu16;
	pae->num_strvals = num_strvals;
	memset(ppu16, 0, sizeof(*ppu16) * num_strvals);
	for (i = 0; i < num_strvals; i++) {
	    *ppu16 = iiimcf_duplicate_string(*pstrs);
	    if (!*ppu16) goto memory_error;
	    ppu16++;
	    pstrs++;
	}
    }

    *pevent = pe;

    return IIIMF_STATUS_SUCCESS;

memory_error:
    iiimcf_delete_aux_event(pe);
    free(pe);
    return IIIMF_STATUS_MALLOC;
}

IIIMF_status
iiimcf_create_aux_getvalues_event(
    const IIIMP_card16 *aux_name,
    IIIMP_card32 class_idx,
    int num_intvals,
    const IIIMP_card32 *pintvals,
    int num_strvals,
    const IIIMP_card16 **pstrs,
    IIIMCF_event *pevent
)
{
    IIIMCF_event_rec *pe;
    IIIMCF_auxevent *pae;

    pe = iiimcf_make_event(IIIMCF_EVENT_TYPE_AUX_GETVALUES);
    if (!pe) return IIIMF_STATUS_MALLOC;

    pae = (IIIMCF_auxevent*) malloc(sizeof(*pae));
    if (!pae) goto memory_error;
    memset(pae, 0, sizeof(*pae));
    pe->v.pauxevent = pae;

    pae->index = class_idx;
    pae->name = iiimcf_duplicate_string(aux_name);
    if (!pae->name) goto memory_error;

    /* integer value */
    if (num_intvals > 0) {
	IIIMP_card32 *pc32;

	pc32 = (IIIMP_card32*) malloc(sizeof(*pc32) * num_intvals);
	if (!pc32) goto memory_error;
	memcpy(pc32, pintvals, sizeof(*pc32) * num_intvals);
	pae->pintvals = pc32;
	pae->num_intvals = num_intvals;
    }
    /* string value */
    if (num_strvals > 0) {
	int i;
	IIIMP_card16 **ppu16;

	ppu16 = (IIIMP_card16**) malloc(sizeof(*ppu16) * num_strvals);
	if (!ppu16) goto memory_error;
	pae->pstrs = ppu16;
	pae->num_strvals = num_strvals;
	memset(ppu16, 0, sizeof(*ppu16) * num_strvals);
	for (i = 0; i < num_strvals; i++) {
	    *ppu16 = iiimcf_duplicate_string(*pstrs);
	    if (!*ppu16) goto memory_error;
	    ppu16++;
	    pstrs++;
	}
    }

    *pevent = pe;

    return IIIMF_STATUS_SUCCESS;

memory_error:
    iiimcf_delete_aux_event(pe);
    free(pe);
    return IIIMF_STATUS_MALLOC;
}

IIIMF_status
iiimcf_get_aux_event_value(
    IIIMCF_event event,
    const IIIMP_card16 **paux_name,
    IIIMP_card32 *pclass_idx,
    int *pnum_intvals,
    const IIIMP_card32 **ppintvals,
    int *pnum_strvals,
    const IIIMP_card16 ***ppstrs
)
{
    IIIMCF_event_rec *pe = (IIIMCF_event_rec*) event;
    IIIMCF_auxevent *pae;

    if (!((pe->type > IIIMCF_EVENT_TYPE_AUX)
	  || (pe->type < IIIMCF_EVENT_TYPE_AUX_END))) {
	return IIIMF_STATUS_ARGUMENT;
    }

    pae = pe->v.pauxevent;
    if (paux_name) *paux_name = pae->name;
    if (pclass_idx) *pclass_idx = pae->index;
    if (pnum_intvals) *pnum_intvals = pae->num_intvals;
    if (ppintvals) *ppintvals = pae->pintvals;
    if (pnum_strvals) *pnum_strvals = pae->num_strvals;
    if (ppstrs) *ppstrs = (const IIIMP_card16**) pae->pstrs;

    return IIIMF_STATUS_SUCCESS;
}

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