#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#include <iiimp-data.h>

#include "iiimp-dataP.h"


static IIIMP_imattribute *
iiimp_imattribute_new(
    IIIMP_data_s *	data_s,
    IIIMP_card16	id,
    IIIMP_card16	id_pre,
    IIIMP_card16	id_dyn,
    void *		value)
{
    IIIMP_imattribute *			attr;
    IIIMP_inputmethod_descriptor *	imd;
    IIIMP_object_descriptor *		od;

    if (NULL == value) {
	data_s->status = IIIMP_DATA_INVALID;
	return NULL;
    }

    attr = (IIIMP_imattribute *)malloc(sizeof (IIIMP_imattribute));
    if (NULL == attr) {
	data_s->status = IIIMP_DATA_MALLOC_ERROR;
	return NULL;
    }

    attr->nbyte = (2 + 2);
    attr->next = NULL;

    attr->id = id;
    attr->id_pre = id_pre;
    attr->id_dyn = id_dyn;

    attr->nbyte += 4;

    attr->value.any = value;

    switch(id_pre) {
    case IIIMP_IMATTRIBUTE_INPUT_METHOD_LIST:
	attr->value_nbyte = 0;
	imd = (IIIMP_inputmethod_descriptor *)value;
	for (; NULL != imd; imd = imd->next) {
	    attr->value_nbyte += imd->nbyte;
	}
	break;
    case IIIMP_IMATTRIBUTE_OBJECT_DESCRIPTOR_LIST:
	attr->value_nbyte = 0;
	od = (IIIMP_object_descriptor *)value;
	for (; NULL != od; od = od->next) {
	    attr->value_nbyte += od->nbyte;
	}
	break;
    case IIIMP_IMATTRIBUTE_CLIENT_DESCRIPTOR:
	attr->value_nbyte = attr->value.client_descriptor->nbyte;
	break;
    case IIIMP_IMATTRIBUTE_CHARACTER_CONVERSION_DEFINITION_RULE:
	attr->value_nbyte = attr->value.ccdef->nbyte;
	break;
    case IIIMP_IMATTRIBUTE_JAR_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_JAR_LIGHT_WEIGHT_ENGINE_OBJECT:
	attr->value_nbyte = attr->value.jarfile_object->nbyte;
	break;
    case IIIMP_IMATTRIBUTE_BINARY_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_BINARY_LIGHT_WEIGHT_ENGINE_OBJECT:
    case IIIMP_IMATTRIBUTE_SCRIPT_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_SCRIPT_LIGHT_WEIGHT_ENGINE_OBJECT:
	attr->value_nbyte = attr->value.binaryfile_object->nbyte;
	break;
    }

    attr->nbyte += (attr->value_nbyte + PAD(attr->value_nbyte));

    return attr;
}


void
iiimp_imattribute_delete(IIIMP_data_s * data_s, IIIMP_imattribute * attr)
{
    switch(attr->id) {
    case IIIMP_IMATTRIBUTE_INPUT_METHOD_LIST:
	iiimp_inputmethod_descriptor_list_delete(
	    data_s, attr->value.inputmethod_descriptor);
	break;
    case IIIMP_IMATTRIBUTE_OBJECT_DESCRIPTOR_LIST:
	iiimp_object_descriptor_list_delete(
	    data_s, attr->value.object_descriptor);
	break;
    case IIIMP_IMATTRIBUTE_CLIENT_DESCRIPTOR:
	iiimp_client_descriptor_delete(data_s, attr->value.client_descriptor);
		break;
    case IIIMP_IMATTRIBUTE_CHARACTER_CONVERSION_DEFINITION_RULE:
	iiimp_ccdef_delete(data_s, attr->value.ccdef);
	break;
    case IIIMP_IMATTRIBUTE_JAR_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_JAR_LIGHT_WEIGHT_ENGINE_OBJECT:
	iiimp_jarfile_object_delete(data_s, attr->value.jarfile_object);
	break;
    case IIIMP_IMATTRIBUTE_BINARY_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_BINARY_LIGHT_WEIGHT_ENGINE_OBJECT:
    case IIIMP_IMATTRIBUTE_SCRIPT_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_SCRIPT_LIGHT_WEIGHT_ENGINE_OBJECT:
	iiimp_binaryfile_object_delete(data_s, attr->value.binaryfile_object);
	break;
    }
    free(attr);

    return;
}


void
iiimp_imattribute_list_delete(IIIMP_data_s * data_s, IIIMP_imattribute * attr)
{
    IIIMP_imattribute *	attr_next;
    for (; NULL != attr; attr = attr_next) {
	attr_next = attr->next;
	iiimp_imattribute_delete(data_s, attr);
    }
    return;
}


IIIMP_imattribute *
iiimp_imattribute_inputmethod_descriptor_new(
    IIIMP_data_s *			data_s,
    IIIMP_card16			id,
    IIIMP_card16			id_dyn,
    IIIMP_inputmethod_descriptor *	value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_INPUT_METHOD_LIST,
				 id_dyn, (void *)value);
}


IIIMP_imattribute *
iiimp_imattribute_object_descriptor_new(
    IIIMP_data_s *		data_s,
    IIIMP_card16		id,
    IIIMP_card16		id_dyn,
    IIIMP_object_descriptor *	value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_OBJECT_DESCRIPTOR_LIST,
				 id_dyn, (void *)value);
}


IIIMP_imattribute *
iiimp_imattribute_client_descriptor_new(
    IIIMP_data_s *		data_s,
    IIIMP_card16		id,
    IIIMP_card16		id_dyn,
    IIIMP_client_descriptor *	value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_CLIENT_DESCRIPTOR,
				 id_dyn, (void *)value);
}


IIIMP_imattribute *
iiimp_imattribute_ccdef_new(
    IIIMP_data_s *		data_s,
    IIIMP_card16		id,
    IIIMP_card16		id_dyn,
    IIIMP_ccdef *		value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_CHARACTER_CONVERSION_DEFINITION_RULE,
				 id_dyn, (void *)value);
}


IIIMP_imattribute *
iiimp_imattribute_jar_gui_object_new(
    IIIMP_data_s *		data_s,
    IIIMP_card16		id,
    IIIMP_card16		id_dyn,
    IIIMP_jarfile_object *	value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_JAR_GUI_OBJECT,
				 id_dyn, (void *)value);
}


IIIMP_imattribute *
iiimp_imattribute_jar_light_weight_engine_object_new(
    IIIMP_data_s *		data_s,
    IIIMP_card16		id,
    IIIMP_card16		id_dyn,
    IIIMP_jarfile_object *	value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_JAR_LIGHT_WEIGHT_ENGINE_OBJECT,
				 id_dyn, (void *)value);
}


IIIMP_imattribute *
iiimp_imattribute_binary_gui_object_new(
    IIIMP_data_s *		data_s,
    IIIMP_card16		id,
    IIIMP_card16		id_dyn,
    IIIMP_binaryfile_object *	value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_BINARY_GUI_OBJECT,
				 id_dyn, (void *)value);
}


IIIMP_imattribute *
iiimp_imattribute_binary_light_weight_engine_new(
    IIIMP_data_s *		data_s,
    IIIMP_card16		id,
    IIIMP_card16		id_dyn,
    IIIMP_binaryfile_object *	value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_BINARY_LIGHT_WEIGHT_ENGINE_OBJECT,
				 id_dyn, (void *)value);
}


IIIMP_imattribute *
iiimp_imattribute_script_gui_object_new(
    IIIMP_data_s *		data_s,
    IIIMP_card16		id,
    IIIMP_card16		id_dyn,
    IIIMP_binaryfile_object *	value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_SCRIPT_GUI_OBJECT,
				 id_dyn, (void *)value);
}


IIIMP_imattribute *
iiimp_imattribute_script_light_weight_engine_new(
    IIIMP_data_s *		data_s,
    IIIMP_card16		id,
    IIIMP_card16		id_dyn,
    IIIMP_binaryfile_object *	value)
{
    return iiimp_imattribute_new(data_s, id,
				 IIIMP_IMATTRIBUTE_SCRIPT_LIGHT_WEIGHT_ENGINE_OBJECT,
				 id_dyn, (void *)value);
}


void
iiimp_imattribute_pack(
    IIIMP_data_s *	data_s,
    IIIMP_imattribute *	m,
    size_t *		nbyte,
    uchar_t **		ptr)
{
    size_t	rest;
    uchar_t *	p;

    rest = *nbyte;
    p = *ptr;

    PUTU16(m->id, rest, p, data_s->byte_swap);
    PUTU16(0, rest, p, data_s->byte_swap);

    PUT32(m->value_nbyte, rest, p, data_s->byte_swap);

    switch(m->id_pre) {
    case IIIMP_IMATTRIBUTE_INPUT_METHOD_LIST:
	iiimp_inputmethod_descriptor_list_pack(data_s,
					       m->value.inputmethod_descriptor,
					       &rest, &p);
	break;
    case IIIMP_IMATTRIBUTE_OBJECT_DESCRIPTOR_LIST:
	iiimp_object_descriptor_list_pack(data_s, m->value.object_descriptor,
					  &rest, &p);
	break;
    case IIIMP_IMATTRIBUTE_CLIENT_DESCRIPTOR:
	iiimp_client_descriptor_pack(data_s,
				     m->value.client_descriptor, &rest, &p);
	break;
    case IIIMP_IMATTRIBUTE_CHARACTER_CONVERSION_DEFINITION_RULE:
	iiimp_ccdef_pack(data_s, m->value.ccdef, &rest, &p);
	break;
    case IIIMP_IMATTRIBUTE_JAR_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_JAR_LIGHT_WEIGHT_ENGINE_OBJECT:
	iiimp_jarfile_object_pack(data_s, m->value.jarfile_object, &rest, &p);
	break;
    case IIIMP_IMATTRIBUTE_BINARY_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_BINARY_LIGHT_WEIGHT_ENGINE_OBJECT:
    case IIIMP_IMATTRIBUTE_SCRIPT_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_SCRIPT_LIGHT_WEIGHT_ENGINE_OBJECT:
	iiimp_binaryfile_object_pack(data_s, m->value.binaryfile_object,
				     &rest, &p);
	break;
    }

    *nbyte = rest;
    *ptr = p;

    return;
}


void
iiimp_imattribute_list_pack(
    IIIMP_data_s *	data_s,
    IIIMP_imattribute *	m,
    size_t *		nbyte,
    uchar_t **		ptr)
{
    size_t	rest;
    uchar_t *	p;

    rest = *nbyte;
    p = *ptr;

    for (; NULL != m; m = m->next) {
	iiimp_imattribute_pack(data_s, m, &rest, &p);
    }

    *nbyte = rest;
    *ptr = p;

    return;
}


IIIMP_imattribute *
iiimp_imattribute_unpack(
    IIIMP_data_s *	data_s,
    size_t *		nbyte,
    const uchar_t **	ptr,
    size_t		nbyte_max)
{
    IIIMP_imattribute *	attr;
    size_t		rest;
    const uchar_t *	p;
    size_t		len;
    size_t		rest2;
    size_t		id;

    rest = nbyte_max;
    p = *ptr;

    if ((*nbyte < rest) || (rest < 8)) {
	data_s->status = IIIMP_DATA_INVALID;
	return NULL;
    }

    GETU16(id, rest, p, data_s->byte_swap);
    SKIP16(rest, p);

    attr = (IIIMP_imattribute *)malloc(sizeof (IIIMP_imattribute));
    if (NULL == attr) {
	data_s->status = IIIMP_DATA_MALLOC_ERROR;
	return NULL;
    }

    attr->next = NULL;

    if (0x1000 & id) {
	attr->id_pre = id;
	attr->id_dyn = 0;
    } else {
	attr->id_dyn = id;
	attr->id_pre = iiimp_attribute_id_search(data_s, id);
	id = attr->id_pre;
	if (0 == attr->id_pre) {
	    free(attr);
	    data_s->status = IIIMP_DATA_INVALID;
	    return NULL;
	}
    }

    GET32(len, rest, p, data_s->byte_swap);
    if ((len < 0) || (rest < len)) {
	free(attr);
	data_s->status = IIIMP_DATA_INVALID;
	return NULL;
    }

    attr->nbyte = (2 + 2 + 4 + len + PAD(len));
    attr->value_nbyte = len;
    rest2 = len;

    switch(id) {
    case IIIMP_IMATTRIBUTE_INPUT_METHOD_LIST:
	attr->value.inputmethod_descriptor =
	    iiimp_inputmethod_descriptor_list_unpack(data_s, &rest2, &p, rest2);
	break;
    case IIIMP_IMATTRIBUTE_OBJECT_DESCRIPTOR_LIST:
	attr->value.object_descriptor =
	    iiimp_object_descriptor_list_unpack(data_s, &rest2, &p, rest2);
	break;
    case IIIMP_IMATTRIBUTE_CLIENT_DESCRIPTOR:
	attr->value.client_descriptor =
	    iiimp_client_descriptor_unpack(data_s, &rest2, &p, rest2);
	break;
    case IIIMP_IMATTRIBUTE_CHARACTER_CONVERSION_DEFINITION_RULE:
	attr->value.ccdef = iiimp_ccdef_unpack(data_s, &rest2, &p, rest2);
	break;
    case IIIMP_IMATTRIBUTE_JAR_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_JAR_LIGHT_WEIGHT_ENGINE_OBJECT:
	attr->value.jarfile_object =
	    iiimp_jarfile_object_unpack(data_s, &rest2, &p, rest2);
	break;
    case IIIMP_IMATTRIBUTE_BINARY_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_BINARY_LIGHT_WEIGHT_ENGINE_OBJECT:
    case IIIMP_IMATTRIBUTE_SCRIPT_GUI_OBJECT:
    case IIIMP_IMATTRIBUTE_SCRIPT_LIGHT_WEIGHT_ENGINE_OBJECT:
	attr->value.binaryfile_object =
	    iiimp_binaryfile_object_unpack(data_s, &rest2, &p, rest2);
	break;
    default:
	attr->value.any = NULL;
	data_s->status = IIIMP_DATA_INVALID;
	break;
    }
    if (NULL == attr->value.any) {
	iiimp_imattribute_delete(data_s, attr);
	return NULL;
    }
    attr->id = id;

    *nbyte = rest - len - PAD(len);
    *ptr = p;

    return attr;
}


IIIMP_imattribute *
iiimp_imattribute_list_unpack(
    IIIMP_data_s *	data_s,
    size_t *		nbyte,
    const uchar_t **	ptr,
    size_t		nbyte_max)
{
    IIIMP_imattribute *	attr;
    size_t		rest;
    const uchar_t *	p;
    IIIMP_imattribute *	attr_first;
    IIIMP_imattribute *	attr_last;

    rest = nbyte_max;
    p = *ptr;
    attr_first = NULL;
    attr_last = NULL;

    data_s->status = IIIMP_DATA_NO_ERROR;

    if (((*nbyte) < nbyte_max) || (0 != (rest & 0x01))) {
	data_s->status = IIIMP_DATA_INVALID;
	return NULL;
    }

    while (0 < rest) {
	attr = iiimp_imattribute_unpack(data_s, &rest, &p, rest);
	if (NULL == attr) {
	    iiimp_imattribute_list_delete(data_s, attr_first);
	    return NULL;
	} else {
	    if (NULL == attr_first) {
		attr_first = attr;
	    } else {
		attr_last->next = attr;
	    }
	    attr_last = attr;
	}
    }

    *nbyte -= (nbyte_max - rest);
    *ptr = p;

    return attr_first;
}


void
iiimp_imattribute_print(
    IIIMP_data_s *	data_s,
    IIIMP_imattribute *	m)
{
    if (NULL == m) return;

    switch (m->id_pre) {
    case IIIMP_IMATTRIBUTE_INPUT_METHOD_LIST:
	(void)fprintf(data_s->print_fp, "\tInput Method Descriptor List:\n");
	iiimp_inputmethod_descriptor_list_print(
	    data_s, m->value.inputmethod_descriptor);
	break;
    case IIIMP_IMATTRIBUTE_OBJECT_DESCRIPTOR_LIST:
	(void)fprintf(data_s->print_fp, "\tObject Descriptor List:\n");
	iiimp_object_descriptor_list_print(data_s, m->value.object_descriptor);
	break;
    case IIIMP_IMATTRIBUTE_CLIENT_DESCRIPTOR:
	(void)fprintf(data_s->print_fp, "\tClient Descriptor:\n");
	iiimp_client_descriptor_print(data_s, m->value.client_descriptor);
	break;
    case IIIMP_IMATTRIBUTE_CHARACTER_CONVERSION_DEFINITION_RULE:
	(void)fprintf(data_s->print_fp,
		      "\tCharacter Conversion Definition Rule\n");
	break;
    case IIIMP_IMATTRIBUTE_JAR_GUI_OBJECT:
	(void)fprintf(data_s->print_fp, "\tJar GUI Object:\n");
	iiimp_jarfile_object_print(data_s, m->value.jarfile_object);
	break;
    case IIIMP_IMATTRIBUTE_JAR_LIGHT_WEIGHT_ENGINE_OBJECT:
	(void)fprintf(data_s->print_fp, "\tJar Light Weight Engine Object:\n");
	iiimp_jarfile_object_print(data_s, m->value.jarfile_object);
	break;
    case IIIMP_IMATTRIBUTE_BINARY_GUI_OBJECT:
	(void)fprintf(data_s->print_fp, "\tBinary GUI Object: ");
	iiimp_binaryfile_object_print(data_s, m->value.binaryfile_object);
	(void)fputc('\n', data_s->print_fp);
	break;
    case IIIMP_IMATTRIBUTE_BINARY_LIGHT_WEIGHT_ENGINE_OBJECT:
	(void)fprintf(data_s->print_fp,
		      "\tBinary Light Weight Engine Object: ");
	iiimp_binaryfile_object_print(data_s, m->value.binaryfile_object);
	(void)fputc('\n', data_s->print_fp);
	break;
    case IIIMP_IMATTRIBUTE_SCRIPT_GUI_OBJECT:
	(void)fprintf(data_s->print_fp, "\tSCRIPT GUI Object:\n");
	iiimp_binaryfile_object_print(data_s, m->value.binaryfile_object);
	break;
    case IIIMP_IMATTRIBUTE_SCRIPT_LIGHT_WEIGHT_ENGINE_OBJECT:
	(void)fprintf(data_s->print_fp,
		      "\tSCRIPT Light Weight Engine Object:\n");
	iiimp_binaryfile_object_print(data_s, m->value.binaryfile_object);
	break;
    }
}


void
iiimp_imattribute_list_print(
    IIIMP_data_s *	data_s,
    IIIMP_imattribute *	m)
{
    for (; NULL != m; m = m->next) {
	iiimp_imattribute_print(data_s, m);
    }
}


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