/* @@@HEAD@@@
// Veil packets buffer manipulation module
*/

#include "config.h"
#include "defs.h"
#include "y.tab.h"
#include "cdc_types.h"
#include "operators.h"
#include "execute.h"
#include "memory.h"

extern Ident pabort_id, pclose_id, popen_id;

#define VEIL_P_PUSH      1
#define VEIL_P_ABORT     2
#define VEIL_P_CLOSE     4
#define VEIL_P_OPEN      8
#define VEIL_P_UNDF1     16
#define VEIL_P_UNDF2     32
#define VEIL_P_UNDF3     64
#define VEIL_P_UNDF4     128

#define HEADER_SIZE    5  /* 1 byte bitfield flags
                             2 bytes session id
                             2 bytes length */

internal list_t * buffer_to_veil_packets(Buffer * buf) {
    int        flags,
               session,
               length,
               blen;
    Buffer   * databuf,
             * incomplete;
    data_t     d,
             * list;
    list_t   * output,
             * packet;
    char     * cbuf;

    output = list_new(0);
    blen = buf->len;
    cbuf = buf->s;

    for (;;) {
        /* 5 bytes will give us the header, we check length again after
           that, because the header gives us the data length */
        if (blen < HEADER_SIZE)
            break;

        flags = (int) cbuf[0];

        session = (((int) cbuf[1]) * 256) + (int) cbuf[2];
        length = (((int) cbuf[3]) * 256) + cbuf[4];
        if (length > (blen - HEADER_SIZE))
            break;

        /* copy the data segment of the packet to the databuf */
        databuf = buffer_new(length);
        cbuf += HEADER_SIZE;
        MEMCPY(databuf->s, cbuf, length);
        cbuf += (length + HEADER_SIZE);
        blen -= (length + HEADER_SIZE);

        /* create the packet list, add it to the output list */

        packet = list_new(4);
        list = list_empty_spaces(packet, 4);
        list[0].type = INTEGER;
        list[0].u.val = (flags & VEIL_P_PUSH);

        /* give abort precedence */
        if (flags & VEIL_P_ABORT) {
            list[1].type     = SYMBOL;
            list[1].u.symbol = pabort_id;
        } else if (flags & VEIL_P_CLOSE) {
            list[1].type     = SYMBOL;
            list[1].u.symbol = pclose_id;
        } else if (flags & VEIL_P_OPEN) {
            list[1].type     = SYMBOL;
            list[1].u.symbol = popen_id;
        } else {
            list[1].type  = INTEGER;
            list[1].u.val = 0;
        }

        list[2].type = INTEGER;
        list[2].u.val = session;

        list[3].type = BUFFER;
        list[3].u.buffer = databuf;

        /* add it to the list */
        d.type = LIST;
        d.u.list = packet;
        output = list_add(output, &d);

        buffer_discard(databuf);
        list_discard(packet);
    }

    /* add the incomplete buffer to the end */
    incomplete = buffer_new(blen);
    if (blen > 0)
        MEMMOVE(incomplete->s, cbuf, blen); \
    d.type = BUFFER;
    d.u.buffer = incomplete;
    output = list_add(output, &d);
    buffer_discard(incomplete);

    return output;
}

void op_buf_to_veil_packets(void) {
    data_t * args;
    int      numargs;
    Buffer * buf;
    list_t * packets;

    if (!func_init_1_or_2(&args, &numargs, BUFFER, BUFFER))
        return;

    /* if they sent us a second argument, concatenate it on the end */
    buf = buffer_dup(args[0].u.buffer);
    if (numargs == 2)
        buffer_append(buf, args[1].u.buffer);
    packets = buffer_to_veil_packets(buf);
    buffer_discard(buf);

    pop(numargs);
    push_list(packets);
    list_discard(packets);
}

void op_buf_from_veil_packets(void) {
    if (!func_init_0()) /* &args, LIST)) */
        return;

    push_int(0);
}