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

#include <iiimp-data.h>

#include "iiimp-dataP.h"


IIIMP_operation *
iiimp_operation_new(
    IIIMP_data_s *	data_s,
    IIIMP_string *	id,
    size_t		value_nbyte,
    uchar_t *		value)
{
    IIIMP_operation *	op;

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

    op->id = id;
    op->next = NULL;

    if (NULL == id) {
	op->nbyte = 4;
    } else {
	op->nbyte = id->nbyte;
    }

    if (0 == value_nbyte) {
	op->nbyte += (2 + 2);
	op->value.nbyte = 0;
	op->value.ptr = NULL;
    } else {
	op->nbyte += (2 + value_nbyte + PAD(2 + value_nbyte));
	op->value.nbyte = value_nbyte;
	op->value.ptr = (uchar_t *)malloc(value_nbyte);
	if (NULL == op->value.ptr) {
	    free(op);
	    data_s->status = IIIMP_DATA_MALLOC_ERROR;
	    return NULL;
	}
	(void)memcpy(op->value.ptr, value, value_nbyte);
    }

    return op;
}


void
iiimp_operation_delete(IIIMP_data_s * data_s, IIIMP_operation * op)
{
    if (NULL == op) return;
    iiimp_string_delete(data_s, op->id);
    free(op->value.ptr);
    free(op);
    return;
}


void
iiimp_operation_list_delete(IIIMP_data_s * data_s, IIIMP_operation * op)
{
    IIIMP_operation *	op_next;
    for (; NULL != op; op = op_next) {
	op_next = op->next;
	iiimp_string_delete(data_s, op->id);
	free(op->value.ptr);
	free(op);
    }
    return;
}


void
iiimp_operation_pack(
    IIIMP_data_s *	data_s,
    IIIMP_operation *	m,
    size_t *		nbyte,
    uchar_t **		ptr)
{
    size_t	rest;
    uchar_t *	p;
    int		pad;
    int		i;

    rest = *nbyte;
    p = *ptr;

    iiimp_string_pack(data_s, m->id, &rest, &p);
    PUTU16(m->value.nbyte, rest, p, data_s->byte_swap);
    pad = PAD(2 + m->value.nbyte);
    (void)memcpy(p, m->value.ptr, m->value.nbyte);
    p += (m->value.nbyte);
    rest -= (m->value.nbyte);
    for (i = 0; i < pad; i++) {
	PUT8(0, rest, p, data_s->byte_swap);
    }

    *nbyte = rest;
    *ptr = p;

    return;
}


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

    rest = *nbyte;
    p = *ptr;

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

    *nbyte = rest;
    *ptr = p;

    return;
}


IIIMP_operation *
iiimp_operation_unpack(
    IIIMP_data_s *	data_s,
    size_t *		nbyte,
    const uchar_t **	ptr,
    size_t		nbyte_max)
{
    IIIMP_operation *	op;
    size_t		rest;
    const uchar_t *	p;
    size_t		len;
    int			pad;

    rest = nbyte_max;
    p = *ptr;

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

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

    op->value.ptr = NULL;
    op->next = NULL;

    op->id = iiimp_string_unpack(data_s, &rest, &p, rest);
    if ((NULL == op->id) || (rest < 4)) {
	iiimp_operation_delete(data_s, op);
	return NULL;
    }

    GETU16(len, rest, p, data_s->byte_swap);
    if (rest <len) {
	iiimp_operation_delete(data_s, op);
	data_s->status = IIIMP_DATA_INVALID;
	return NULL;
    }

    op->value.nbyte = len;
    op->value.ptr = (uchar_t *)malloc(len);
    if (NULL == op->value.ptr) {
	iiimp_operation_delete(data_s, op);
	data_s->status = IIIMP_DATA_MALLOC_ERROR;
	return NULL;
    }
    (void)memcpy(op->value.ptr, p, len);
    pad = PAD(2 + len);
    rest -= (len + pad);
    p += (len + pad);

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

    return op;
}


IIIMP_operation *
iiimp_operation_list_unpack(
    IIIMP_data_s *	data_s,
    size_t *		nbyte,
    const uchar_t **	ptr,
    size_t		nbyte_max)
{
    IIIMP_operation *	op;
    size_t		rest;
    const uchar_t *	p;
    IIIMP_operation *	op_first;
    IIIMP_operation *	op_last;

    rest = nbyte_max;
    p = *ptr;

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

    op_first = NULL;
    op_last = NULL;

    while (0 < rest) {
	op = iiimp_operation_unpack(data_s, &rest, &p, rest);
	if (NULL == op) {
	    iiimp_operation_list_delete(data_s, op_first);
	    return NULL;
	} else {
	    if (NULL == op_first) {
		op_first = op;
	    } else {
		op_last->next = op;
	    }
	    op_last = op;
	}
    }

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

    return op_first;
}


void
iiimp_operation_print(
    IIIMP_data_s *	data_s,
    IIIMP_operation *	m)
{
    if (NULL == m) return;
    (void)fprintf(data_s->print_fp, "\toperation-id=");
    iiimp_string_print(data_s, m->id);
    (void)fputc('\n', data_s->print_fp);
    (void)fprintf(data_s->print_fp, "\toperation-value=\n");
    iiimp_byte_stream_print(data_s, &(m->value));
}


void
iiimp_operation_list_print(
    IIIMP_data_s *	data_s,
    IIIMP_operation *	m)
{
    for (; NULL != m; m = m->next) {
	iiimp_operation_print(data_s, m);
    }
}


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