#include <transport.h>
#include <transport_private.h>
#include <stdlib.h>

static void *alloc_common(void)
{
    struct transport_common *const ret = malloc(sizeof *ret);

    if (ret)
        *ret = (const struct transport_common){0};

    return ret;
}

static void *alloc_ack(void)
{
    struct transport_packet_ack *const ret = malloc(sizeof *ret);

    if (ret)
        *ret = (const struct transport_packet_ack){0};

    return ret;
}

static void *alloc_data(void)
{
    struct transport_packet_data *const ret = malloc(sizeof *ret);

    if (ret)
        *ret = (const struct transport_packet_data){0};

    return ret;
}

union transport_packet *transport_packet_alloc(const enum transport_packet_type t)
{
    static void *(*const alloc[])(void) =
    {
        [TRANSPORT_PACKET_TYPE_CONNECT] = alloc_common,
        [TRANSPORT_PACKET_TYPE_DISCONNECT] = alloc_common,
        [TRANSPORT_PACKET_TYPE_NACK] = alloc_common,
        [TRANSPORT_PACKET_TYPE_ACK] = alloc_ack,
        [TRANSPORT_PACKET_TYPE_DATA] = alloc_data
    };

    union transport_packet *const ret = alloc[t]();

    if (!ret)
        return NULL;

    ret->common.type = t;
    return ret;
}

int transport_append(struct transport_handle *const h,
    union transport_packet *const p)
{
    union transport_packet **const packets = realloc(h->packets,
        (h->n_packets + 1) * sizeof *h->packets);

    if (!packets)
        return -1;

    h->packets = packets;
    h->packets[h->n_packets++] = p;
    return 0;
}

void transport_packet_free(union transport_packet *const p)
{
    free(p);
}

int transport_pop(struct transport_handle *const h)
{
    if (!h->n_packets)
        return -1;

    union transport_packet *const p = h->packets[--h->n_packets];

    if (h->n_packets)
    {
        if (!(h->packets = realloc(h->packets,
            h->n_packets * sizeof *h->packets)))
            return -1;
    }
    else
    {
        free(h->packets);
        h->packets = NULL;
    }

    transport_packet_free(p);
    return 0;
}
