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

#include <iiimp.h>

#include "input-method.h"
#include "stream.h"

IIIMF_status
iiimf_create_stream(
    IIIMF_stream_proc_read	proc_read,
    IIIMF_stream_proc_write	proc_write,
    IIIMF_stream_private	private_data,
    int 			timeout,
    IIIMF_stream **		stream_ret)
{
    IIIMF_stream *		stream;

    stream = (IIIMF_stream *)malloc(sizeof (IIIMF_stream));
    if (NULL == stream) {
	free(stream);
	return IIIMF_STATUS_MALLOC;
    }
    stream->timeout = timeout;
    stream->private_data = private_data;
    stream->proc_read = proc_read;
    stream->proc_write = proc_write;

    *stream_ret = stream;

    return IIIMF_STATUS_SUCCESS;
}

void
iiimf_stream_delete(
    IIIMF_stream *	stream)
{
    if (NULL == stream) return;
    free(stream);

    return;
}

IIIMF_status
iiimf_stream_send(
    IIIMF_stream *	stream,
    IIIMP_data_s *	data_s,
    IIIMP_message *	message)
{
    IIIMF_status	status;
    uchar_t *		ptr;
    size_t		nbyte;

    if (!stream->proc_write) return IIIMF_STATUS_STREAM;

    status = IIIMF_STATUS_SUCCESS;

    ptr = iiimp_message_pack(data_s, message, &nbyte);
    if (NULL == ptr) {
	switch (iiimp_data_status(data_s)) {
	case IIIMP_DATA_MALLOC_ERROR:
	    status = IIIMF_STATUS_MALLOC;
	    break;
	case IIIMP_DATA_INVALID:
	    status = IIIMF_STATUS_PACKET;
	    break;
	case IIIMP_DATA_PROTOCOL_VERSION_ERROR:
	    status = IIIMF_STATUS_PROTOCOL_VERSION;
	    break;
	default:
	    status = IIIMF_STATUS_FAIL;
	    break;
	}
	return status;
    }

    status = (*stream->proc_write)(stream->private_data, ptr, nbyte);

    free(ptr);

    return status;
}


IIIMF_status
iiimf_stream_receive(
    IIIMF_stream *	stream,
    IIIMP_data_s *	data_s,
    IIIMP_message **	message_ret)
{
    IIIMF_status	status;
    const uchar_t *	ptr;
    uchar_t *		p;
    size_t		nbyte;
    IIIMP_message *	message;
    uchar_t		header[8];
    int			header_len;
    int			length;
    int			opcode;
    uchar_t *		buf;

    if (!stream->proc_read) return IIIMF_STATUS_STREAM;
    status = (*stream->proc_read)(stream->private_data, header, 8);
    if (IIIMF_STATUS_SUCCESS != status) return status;

    opcode = header[0];
    if (0x80 & opcode) {
	if ((0xe0 & header[4]) || (0x00 == header[4])) {
	    return IIIMF_STATUS_PACKET;
	}
	length = (((header[4] << 24) +
		   (header[5] << 16) +
		   (header[6] << 8) +
		   (header[7] << 0)) << 2);
	header_len = 8;
    } else {
	length = (((header[1] << 16) +
		   (header[2] << 8) +
		   (header[3] << 0)) << 2);
	header_len = 4;
    }
    if (length < 4) return IIIMF_STATUS_PACKET;

    if (4 == length) {
	buf = (header + 4);
    } else {
	buf = (uchar_t *)malloc(length);
	if (NULL == buf) return IIIMF_STATUS_MALLOC;
	p = buf;

	if (8 != header_len) {
	    (void)memcpy(buf, header + 4, 4);
	    p += 4;
	}

	status = (*stream->proc_read)(stream->private_data, p,
				      length - (8 - header_len));
	if (IIIMF_STATUS_SUCCESS != status) {
	    free(buf);
	    return status;
	}
    }

    ptr = buf;
    nbyte = length;
    message = iiimp_message_unpack(data_s, header[0], &nbyte, &ptr);
    if ((header + 4) != buf) {
	free(buf);
    }
    if (NULL == message) return IIIMF_STATUS_FAIL;

    *message_ret = message;

    return status;
}


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