#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SunIM.h"

/***************************************
      duplicate handlers
***************************************/

static iml_inst*
imli_duplicate_short(
    iml_inst *pinst
)
{
    const int size = sizeof(iml_inst);
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    return pr;
}

static iml_inst*
imli_duplicate_set_status(
    iml_inst *pinst
)
{
    const int size = sizeof(iml_inst) + sizeof(iml_status_t);
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    return pr;
}

static iml_inst*
imli_duplicate_keypress(
    iml_inst *pinst
)
{
    const int size = sizeof(iml_inst) + sizeof(IMKeyEventStruct) + 1;
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    return pr;
}

static iml_inst*
imli_duplicate_preedit_draw(
    iml_inst *pinst
)
{
    IMPreeditDrawCallbackStruct *pd1, *pd2;
    const int size = sizeof(iml_inst) + sizeof(IMPreeditDrawCallbackStruct);
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    pd1 = (IMPreeditDrawCallbackStruct*) &pr->operand;
    pd2 = (IMPreeditDrawCallbackStruct*) &pinst->operand;
    if (pd2->text) {
	pd1->text = IMText_duplicate(pd2->text);
	if (!pd1->text) {
	    free(pr);
	    return NULL;
	}
    }
    return pr;
}

static iml_inst*
imli_duplicate_status_draw(
    iml_inst *pinst
)
{
    const int size = sizeof(iml_inst) + sizeof(IMStatusDrawCallbackStruct);
    IMStatusDrawCallbackStruct *pd1, *pd2;
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    pd1 = (IMStatusDrawCallbackStruct*) &pr->operand;
    pd2 = (IMStatusDrawCallbackStruct*) &pinst->operand;
    pd1->text = IMText_duplicate(pd2->text);
    if (!pd1->text) {
	free(pr);
	return NULL;
    }
    return pr;
}

static iml_inst*
imli_duplicate_lookup_start(
    iml_inst *pinst
)
{
    const int size = sizeof(iml_inst) + sizeof(IMLookupStartCallbackStruct);
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    return pr;
}

static iml_inst*
imli_duplicate_lookup_draw(
    iml_inst *pinst
)
{
    const int size = sizeof(iml_inst) + sizeof(IMLookupDrawCallbackStruct);
    int i;
    IMLookupDrawCallbackStruct *pd1, *pd2;
    IMChoiceObject *pc1, *pc2;
    iml_inst *pr;

    pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    pd1 = (IMLookupDrawCallbackStruct*) &pr->operand;
    pd2 = (IMLookupDrawCallbackStruct*) &pinst->operand;
    pc1 = (IMChoiceObject*) malloc(sizeof(IMChoiceObject) * pd1->n_choices);
    pc2 = pd2->choices;
    if (!pc1) goto memory_error;
    memset(pc1, 0, sizeof(IMChoiceObject) * pd1->n_choices);
    pd1->choices = pc1;

    for (i = 0; i < pd1->n_choices; i++, pc1++, pc2++) {
	pc1->label = IMText_duplicate(pc2->label);
	if (!pc1->label) goto memory_error;
	pc1->value = IMText_duplicate(pc2->value);
	if (!pc1->value) goto memory_error;
    }

    return pr;

memory_error:
    if (pd1->choices) {
	for (i = 0, pc1 = pd1->choices; i < pd1->n_choices; i++, pc1++) {
	    if (pc1->label) IMText_delete(pc1->label);
	    if (pc1->value) IMText_delete(pc1->value);
	}
	free(pd1->choices);
    }

    free(pr);
    return NULL;
}

static iml_inst*
imli_duplicate_lookup_process(
    iml_inst *pinst
)
{
    const int size = sizeof(iml_inst) + sizeof(IMLookupProcessCallbackStruct);
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    return pr;
}

static iml_inst*
imli_duplicate_aux_basic(
    iml_inst *pinst
)
{
    IMAuxBasicCallbackStruct *pd1, *pd2;
    const int size = sizeof(iml_inst) + sizeof(IMAuxBasicCallbackStruct);
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    pd1 = (IMAuxBasicCallbackStruct*) &pr->operand;
    pd2 = (IMAuxBasicCallbackStruct*) &pinst->operand;
    pd1->aux_name = strdup(pd1->aux_name);
    if (!pd1->aux_name) {
	free(pr);
	return NULL;
    }
    return pr;
}

static iml_inst*
imli_duplicate_ns_listener(
    iml_inst *pinst
)
{
    IMNSListenerStruct *rn1, *rn2;

    const int size = sizeof(iml_inst) + sizeof(IMNSListenerStruct);
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    rn1 = (IMNSListenerStruct *) &pr->operand;
    rn2 = (IMNSListenerStruct *) &pinst->operand;
    rn1->filename = strdup(rn2->filename);
    if (!rn1->filename) {
	free(pr);
	return NULL;
    }
    return pr;
}

static iml_inst*
imli_duplicate_aux_draw(
    iml_inst *pinst
)
{
    IMAuxDrawCallbackStruct *pd1, *pd2;
    const int size = sizeof(iml_inst) + sizeof(IMAuxDrawCallbackStruct);
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    pd1 = (IMAuxDrawCallbackStruct*) &pr->operand;
    pd2 = (IMAuxDrawCallbackStruct*) &pinst->operand;

    pd1->aux_name = strdup(pd1->aux_name);
    if (!pd1->aux_name) goto memory_error1;

    if (pd2->integer_values) {
	pd1->integer_values = malloc(sizeof(int) * pd1->count_integer_values);
	if (!pd1->integer_values) goto memory_error2;
	memcpy(pd1->integer_values, pd2->integer_values, 
	       sizeof(int) * pd1->count_integer_values);
    }

    if (pd2->string_values) {
	int i;
	IMText *ptext1 = malloc(sizeof(IMText) * pd2->count_string_values);
	IMText *ptext2 = pd2->string_values;
	if (!ptext1) goto memory_error3;
	pd1->string_values = ptext1;

	for (i = 0; i < pd2->count_string_values; i++, ptext1++, ptext2++) {
	    if (!IMText_duplicate2(ptext1, ptext2)) {
		int i2;
		ptext1 = pd1->string_values;
		for (i2 = 0; i2 < i; i2++, ptext1++) {
		    IMText_delete2(ptext1);
		}
		free(pd1->string_values);
		goto memory_error3;
	    }
	}
    }
    return pr;

memory_error3:
    free(pd1->integer_values);
memory_error2:
    free(pd1->aux_name);
memory_error1:
    free(pr);
    return NULL;
}

static iml_inst*
imli_duplicate_operand_imtext(
    iml_inst *pinst
)
{
    IMText *pd1, *pd2;
    const int size = sizeof(iml_inst) + sizeof(IMText);
    iml_inst *pr = (iml_inst*) malloc(size);
    if (!pr) return NULL;
    memcpy(pr, pinst, size);
    pd1 = (IMText*) &pr->operand;
    pd2 = (IMText*) &pinst->operand;
    if (!IMText_duplicate2(pd1, pd2)) {
	free(pr);
	return NULL;
    }

    return pr;
}

iml_inst*
iml_duplicate_inst(
    iml_inst *pinst
)
{
    int op = pinst->opcode & ~IMM_CB_RESULT_REQUIRED;

    switch (op) {
      case IMM_KEYPRESS:
       return imli_duplicate_keypress(pinst);
      case IMM_COMMIT:
      case IMM_RESET_RETURN:
       return imli_duplicate_operand_imtext(pinst);
      case IMM_SET_STATUS:
       return imli_duplicate_set_status(pinst);
      case IMM_PREEDIT_DRAW:
       return imli_duplicate_preedit_draw(pinst);
      case IMM_STATUS_DRAW:
       return imli_duplicate_status_draw(pinst);
      case IMM_LOOKUP_START:
       return imli_duplicate_lookup_start(pinst);
      case IMM_LOOKUP_DRAW:
       return imli_duplicate_lookup_draw(pinst);
      case IMM_LOOKUP_PROCESS:
       return imli_duplicate_lookup_process(pinst);
      case IMM_AUX_START_2:
      case IMM_AUX_DONE_2:
       return  imli_duplicate_aux_basic(pinst);
      case IMM_AUX_DRAW_2:
       return imli_duplicate_aux_draw(pinst);
      case IMM_NS_LISTENER:
       return imli_duplicate_ns_listener(pinst);
      default:
       return imli_duplicate_short(pinst);
    }

    /* notreached */
    return NULL;
}

/***************************************
      delete handlers
***************************************/

static void
imli_delete_short(
    iml_inst *pinst
)
{
    free(pinst);
}

static void
imli_delete_preedit_draw(
    iml_inst *pinst
)
{
    IMPreeditDrawCallbackStruct *pd = (IMPreeditDrawCallbackStruct*) &pinst->operand;
    if (pd->text) IMText_delete(pd->text);
    free(pinst);
    return;
}

static void
imli_delete_status_draw(
    iml_inst *pinst
)
{
    IMStatusDrawCallbackStruct *pd = (IMStatusDrawCallbackStruct*) &pinst->operand;
    if (pd->text) IMText_delete(pd->text);
    free(pinst);
    return;
}

static void
imli_delete_lookup_draw(
    iml_inst *pinst
)
{
    IMLookupDrawCallbackStruct *pd = (IMLookupDrawCallbackStruct*) &pinst->operand;
    if (pd->choices) {
	IMChoiceObject *pc;
	int i;
	for (i = 0, pc = pd->choices; i < pd->n_choices; i++, pc++) {
	    if (pc->label) IMText_delete(pc->label);
	    if (pc->value) IMText_delete(pc->value);
	}
	free(pd->choices);
    }
    free(pinst);
    return;
}

static void
imli_delete_aux_basic(
    iml_inst *pinst
)
{
    IMAuxBasicCallbackStruct *pd = (IMAuxBasicCallbackStruct*) &pinst->operand;
    free(pd->aux_name);
    free(pinst);
    return;
}

static void
imli_delete_aux_draw(
    iml_inst *pinst
)
{
    IMAuxDrawCallbackStruct *pd = (IMAuxDrawCallbackStruct*) &pinst->operand;
    free(pd->aux_name);
    if (pd->integer_values) free(pd->integer_values);
    if (pd->string_values) {
	int i;
	IMText *ptext = pd->string_values;
	for (i = 0; i < pd->count_string_values; i++, ptext++) {
	    IMText_delete2(ptext);
	}
	free(pd->string_values);
    }
    free(pinst);
    return;
}

static void
imli_delete_operand_imtext(
    iml_inst *pinst
)
{
    IMText *pd = (IMText*) &pinst->operand;
    IMText_delete2(pd);
    free(pinst);
    return;
}

void
iml_delete_inst(
    iml_inst *pinst
)
{
    int op = pinst->opcode & ~IMM_CB_RESULT_REQUIRED;

    switch (op) {
      case IMM_COMMIT:
      case IMM_RESET_RETURN:
       imli_delete_operand_imtext(pinst);
       return;
      case IMM_PREEDIT_DRAW:
       imli_delete_preedit_draw(pinst);
       return;
      case IMM_STATUS_DRAW:
       imli_delete_status_draw(pinst);
       return;
      case IMM_LOOKUP_DRAW:
       imli_delete_lookup_draw(pinst);
       return;
      case IMM_AUX_START_2:
      case IMM_AUX_DONE_2:
       imli_delete_aux_basic(pinst);
       return;
      case IMM_AUX_DRAW_2:
       imli_delete_aux_draw(pinst);
       return;
      case IMM_KEYPRESS:
      case IMM_LOOKUP_PROCESS:
      case IMM_LOOKUP_START:
      case IMM_SET_STATUS:
      default:
       imli_delete_short(pinst);
       return;
    }

    /* notreached */
    return;
}

/***************************************
      IMText functions.
***************************************/

int
IMText_duplicate2(
    IMText *pdest, IMText *psrc
)
{
    int i, j;
    IMFeedbackList *pfbl;
    IMFeedbackList *pfbls;

    *pdest = *psrc;
    pdest->text.utf_chars = NULL;
    pdest->feedback = NULL;
    pdest->annotations = NULL;

    if (psrc->char_length > 0) {
	if (psrc->text.utf_chars) {
	    UTFCHAR *pu = (UTFCHAR*) malloc(sizeof(UTFCHAR)
					    * psrc->char_length);
	    if (!pu) {
		IMText_delete2(pdest);
		return 0;
	    }
	    memcpy(pu, psrc->text.utf_chars, sizeof(UTFCHAR) * psrc->char_length);
	    pdest->text.utf_chars = pu;
	}

	if (psrc->feedback) {
	    pfbl = (IMFeedbackList*) malloc(sizeof(IMFeedbackList)
					    * psrc->char_length);
	    if (!pfbl) {
		IMText_delete2(pdest);
		return 0;
	    }
	    memset(pfbl, 0, sizeof(IMFeedbackList) * psrc->char_length);
	    pdest->feedback = pfbl;

	    pfbls = psrc->feedback;
	    for (i = 0;
		 i < psrc->char_length;
		 i++, pfbl++, pfbls++) {
		if (pfbls->count_feedbacks > 0) {
		    pfbl->feedbacks = (IMFeedback*) malloc(sizeof(IMFeedback) * pfbls->count_feedbacks);
		    if (!pfbl->feedbacks) {
			IMText_delete2(pdest);
			return 0;
		    }
		    memcpy(pfbl->feedbacks, pfbls->feedbacks,
			   sizeof(IMFeedback) * pfbls->count_feedbacks);
		    pfbl->count_feedbacks = pfbls->count_feedbacks;
		}
	    }
	}
    }

    if (psrc->count_annotations > 0) {
	IMAnnotation *pima, *pimas;
	IMAnnotationValue *pimavs;

	pima = (IMAnnotation*) malloc(sizeof(IMAnnotation)
				      * psrc->count_annotations);
	if (!pima) {
	    IMText_delete2(pdest);
	    return 0;
	}
	memset(pima, 0, sizeof(IMAnnotation) * psrc->count_annotations);
	pdest->annotations = pima;
	pimas = psrc->annotations;
	for (i = 0;
	     i < psrc->count_annotations;
	     i++, pima++, pimas++) {
	    IMAnnotationValue *pimav = (IMAnnotationValue*) malloc(sizeof(IMAnnotationValue)
								   * pimas->num_values);
	    if (!pimav) {
		IMText_delete2(pdest);
		return 0;
	    }
	    memset(pimav, 0, sizeof(IMAnnotationValue) * pimas->num_values);
	    *pima = *pimas;
	    pima->values = pimav;
	    pimavs = pimas->values;
	    for (j = 0;
		 j < pimas->num_values;
		 j++, pimav++, pimavs++) {
		*pimav = *pimavs;
		pimav->value = malloc(pimav->len);
		if (!pimav->value) {
		    IMText_delete2(pdest);
		    return 0;
		}
		memcpy(pimav->value, pimavs->value, pimav->len);
	    }
	}
    }

    return 1;
}

IMText*
IMText_duplicate(
    IMText *psrc
)
{
    IMText *pdest;

    if (!psrc) return NULL;

    pdest = (IMText*) malloc(sizeof(IMText));
    if (!pdest) return NULL;
    if (!IMText_duplicate2(pdest, psrc)) {
	free(pdest);
	return NULL;
    }
    return pdest;
}

void
IMText_delete2(
    IMText *ptext
)
{
    int i, j;

    if (ptext->text.utf_chars)
	free(ptext->text.utf_chars);

    if (ptext->feedback) {
	IMFeedbackList *pfbl = ptext->feedback;
	for (i = 0;
	     i < ptext->char_length;
	     i++, pfbl++) {
	    if (pfbl->feedbacks) free(pfbl->feedbacks);
	}
	free(ptext->feedback);
    }

    if (ptext->annotations) {
	IMAnnotation *pima = ptext->annotations;
	for (i = 0;
	     i < ptext->count_annotations;
	     i++, pima++) {
	    IMAnnotationValue *pimav = pima->values;
	    for (j = 0;
		 j < pima->num_values;
		 j++, pimav++) {
		if (pimav->value) free(pimav->value);
	    }
	    if (pima->values) free(pima->values);
	}
	free(ptext->annotations);
    }
}

void
IMText_delete(
    IMText *ptext
)
{
    IMText_delete2(ptext);
    free(ptext);
}

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