/*
    DO NOT EDIT THIS FILE!
    This file was automatically generated by the bproto generator.
*/

#include <stdint.h>
#include <string.h>

#include <misc/debug.h>
#include <misc/byteorder.h>
#include <bproto/BProto.h>


#define addr_SIZEtype (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint8_s))
#define addr_SIZEip_port (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (2))
#define addr_SIZEipv4_addr (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (4))
#define addr_SIZEipv6_addr (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (16))

typedef struct {
    uint8_t *out;
    int used;
    int type_count;
    int ip_port_count;
    int ipv4_addr_count;
    int ipv6_addr_count;
} addrWriter;

static void addrWriter_Init (addrWriter *o, uint8_t *out);
static int addrWriter_Finish (addrWriter *o);
static void addrWriter_Addtype (addrWriter *o, uint8_t v);
static uint8_t * addrWriter_Addip_port (addrWriter *o);
static uint8_t * addrWriter_Addipv4_addr (addrWriter *o);
static uint8_t * addrWriter_Addipv6_addr (addrWriter *o);

typedef struct {
    uint8_t *buf;
    int buf_len;
    int type_start;
    int type_span;
    int type_pos;
    int ip_port_start;
    int ip_port_span;
    int ip_port_pos;
    int ipv4_addr_start;
    int ipv4_addr_span;
    int ipv4_addr_pos;
    int ipv6_addr_start;
    int ipv6_addr_span;
    int ipv6_addr_pos;
} addrParser;

static int addrParser_Init (addrParser *o, uint8_t *buf, int buf_len);
static int addrParser_GotEverything (addrParser *o);
static int addrParser_Gettype (addrParser *o, uint8_t *v);
static void addrParser_Resettype (addrParser *o);
static void addrParser_Forwardtype (addrParser *o);
static int addrParser_Getip_port (addrParser *o, uint8_t **data);
static void addrParser_Resetip_port (addrParser *o);
static void addrParser_Forwardip_port (addrParser *o);
static int addrParser_Getipv4_addr (addrParser *o, uint8_t **data);
static void addrParser_Resetipv4_addr (addrParser *o);
static void addrParser_Forwardipv4_addr (addrParser *o);
static int addrParser_Getipv6_addr (addrParser *o, uint8_t **data);
static void addrParser_Resetipv6_addr (addrParser *o);
static void addrParser_Forwardipv6_addr (addrParser *o);

void addrWriter_Init (addrWriter *o, uint8_t *out)
{
    o->out = out;
    o->used = 0;
    o->type_count = 0;
    o->ip_port_count = 0;
    o->ipv4_addr_count = 0;
    o->ipv6_addr_count = 0;
}

int addrWriter_Finish (addrWriter *o)
{
    ASSERT(o->used >= 0)
    ASSERT(o->type_count == 1)
    ASSERT(o->ip_port_count >= 0 && o->ip_port_count <= 1)
    ASSERT(o->ipv4_addr_count >= 0 && o->ipv4_addr_count <= 1)
    ASSERT(o->ipv6_addr_count >= 0 && o->ipv6_addr_count <= 1)

    return o->used;
}

void addrWriter_Addtype (addrWriter *o, uint8_t v)
{
    ASSERT(o->used >= 0)
    ASSERT(o->type_count == 0)
    

    struct BProto_header_s header;
    header.id = htol16(1);
    header.type = htol16(BPROTO_TYPE_UINT8);
    memcpy(o->out + o->used, &header, sizeof(header));
    o->used += sizeof(struct BProto_header_s);

    struct BProto_uint8_s data;
    data.v = htol8(v);
    memcpy(o->out + o->used, &data, sizeof(data));
    o->used += sizeof(struct BProto_uint8_s);

    o->type_count++;
}

uint8_t * addrWriter_Addip_port (addrWriter *o)
{
    ASSERT(o->used >= 0)
    ASSERT(o->ip_port_count == 0)
    

    struct BProto_header_s header;
    header.id = htol16(2);
    header.type = htol16(BPROTO_TYPE_CONSTDATA);
    memcpy(o->out + o->used, &header, sizeof(header));
    o->used += sizeof(struct BProto_header_s);

    struct BProto_data_header_s data;
    data.len = htol32(2);
    memcpy(o->out + o->used, &data, sizeof(data));
    o->used += sizeof(struct BProto_data_header_s);

    uint8_t *dest = (o->out + o->used);
    o->used += (2);

    o->ip_port_count++;

    return dest;
}

uint8_t * addrWriter_Addipv4_addr (addrWriter *o)
{
    ASSERT(o->used >= 0)
    ASSERT(o->ipv4_addr_count == 0)
    

    struct BProto_header_s header;
    header.id = htol16(3);
    header.type = htol16(BPROTO_TYPE_CONSTDATA);
    memcpy(o->out + o->used, &header, sizeof(header));
    o->used += sizeof(struct BProto_header_s);

    struct BProto_data_header_s data;
    data.len = htol32(4);
    memcpy(o->out + o->used, &data, sizeof(data));
    o->used += sizeof(struct BProto_data_header_s);

    uint8_t *dest = (o->out + o->used);
    o->used += (4);

    o->ipv4_addr_count++;

    return dest;
}

uint8_t * addrWriter_Addipv6_addr (addrWriter *o)
{
    ASSERT(o->used >= 0)
    ASSERT(o->ipv6_addr_count == 0)
    

    struct BProto_header_s header;
    header.id = htol16(4);
    header.type = htol16(BPROTO_TYPE_CONSTDATA);
    memcpy(o->out + o->used, &header, sizeof(header));
    o->used += sizeof(struct BProto_header_s);

    struct BProto_data_header_s data;
    data.len = htol32(16);
    memcpy(o->out + o->used, &data, sizeof(data));
    o->used += sizeof(struct BProto_data_header_s);

    uint8_t *dest = (o->out + o->used);
    o->used += (16);

    o->ipv6_addr_count++;

    return dest;
}

int addrParser_Init (addrParser *o, uint8_t *buf, int buf_len)
{
    ASSERT(buf_len >= 0)

    o->buf = buf;
    o->buf_len = buf_len;
    o->type_start = o->buf_len;
    o->type_span = 0;
    o->type_pos = 0;
    o->ip_port_start = o->buf_len;
    o->ip_port_span = 0;
    o->ip_port_pos = 0;
    o->ipv4_addr_start = o->buf_len;
    o->ipv4_addr_span = 0;
    o->ipv4_addr_pos = 0;
    o->ipv6_addr_start = o->buf_len;
    o->ipv6_addr_span = 0;
    o->ipv6_addr_pos = 0;

    int type_count = 0;
    int ip_port_count = 0;
    int ipv4_addr_count = 0;
    int ipv6_addr_count = 0;

    int pos = 0;
    int left = o->buf_len;

    while (left > 0) {
        int entry_pos = pos;

        if (!(left >= sizeof(struct BProto_header_s))) {
            return 0;
        }
        struct BProto_header_s header;
        memcpy(&header, o->buf + pos, sizeof(header));
        pos += sizeof(struct BProto_header_s);
        left -= sizeof(struct BProto_header_s);
        uint16_t type = ltoh16(header.type);
        uint16_t id = ltoh16(header.id);

        switch (type) {
            case BPROTO_TYPE_UINT8: {
                if (!(left >= sizeof(struct BProto_uint8_s))) {
                    return 0;
                }
                pos += sizeof(struct BProto_uint8_s);
                left -= sizeof(struct BProto_uint8_s);

                switch (id) {
                    case 1:
                        if (o->type_start == o->buf_len) {
                            o->type_start = entry_pos;
                        }
                        o->type_span = pos - o->type_start;
                        type_count++;
                        break;
                    default:
                        return 0;
                }
            } break;
            case BPROTO_TYPE_UINT16: {
                if (!(left >= sizeof(struct BProto_uint16_s))) {
                    return 0;
                }
                pos += sizeof(struct BProto_uint16_s);
                left -= sizeof(struct BProto_uint16_s);

                switch (id) {
                    default:
                        return 0;
                }
            } break;
            case BPROTO_TYPE_UINT32: {
                if (!(left >= sizeof(struct BProto_uint32_s))) {
                    return 0;
                }
                pos += sizeof(struct BProto_uint32_s);
                left -= sizeof(struct BProto_uint32_s);

                switch (id) {
                    default:
                        return 0;
                }
            } break;
            case BPROTO_TYPE_UINT64: {
                if (!(left >= sizeof(struct BProto_uint64_s))) {
                    return 0;
                }
                pos += sizeof(struct BProto_uint64_s);
                left -= sizeof(struct BProto_uint64_s);

                switch (id) {
                    default:
                        return 0;
                }
            } break;
            case BPROTO_TYPE_DATA:
            case BPROTO_TYPE_CONSTDATA:
            {
                if (!(left >= sizeof(struct BProto_data_header_s))) {
                    return 0;
                }
                struct BProto_data_header_s val;
                memcpy(&val, o->buf + pos, sizeof(val));
                pos += sizeof(struct BProto_data_header_s);
                left -= sizeof(struct BProto_data_header_s);

                uint32_t payload_len = ltoh32(val.len);
                if (!(left >= payload_len)) {
                    return 0;
                }
                pos += payload_len;
                left -= payload_len;

                switch (id) {
                    case 2:
                        if (!(type == BPROTO_TYPE_CONSTDATA)) {
                            return 0;
                        }
                        if (!(payload_len == (2))) {
                            return 0;
                        }
                        if (o->ip_port_start == o->buf_len) {
                            o->ip_port_start = entry_pos;
                        }
                        o->ip_port_span = pos - o->ip_port_start;
                        ip_port_count++;
                        break;
                    case 3:
                        if (!(type == BPROTO_TYPE_CONSTDATA)) {
                            return 0;
                        }
                        if (!(payload_len == (4))) {
                            return 0;
                        }
                        if (o->ipv4_addr_start == o->buf_len) {
                            o->ipv4_addr_start = entry_pos;
                        }
                        o->ipv4_addr_span = pos - o->ipv4_addr_start;
                        ipv4_addr_count++;
                        break;
                    case 4:
                        if (!(type == BPROTO_TYPE_CONSTDATA)) {
                            return 0;
                        }
                        if (!(payload_len == (16))) {
                            return 0;
                        }
                        if (o->ipv6_addr_start == o->buf_len) {
                            o->ipv6_addr_start = entry_pos;
                        }
                        o->ipv6_addr_span = pos - o->ipv6_addr_start;
                        ipv6_addr_count++;
                        break;
                    default:
                        return 0;
                }
            } break;
            default:
                return 0;
        }
    }

    if (!(type_count == 1)) {
        return 0;
    }
    if (!(ip_port_count <= 1)) {
        return 0;
    }
    if (!(ipv4_addr_count <= 1)) {
        return 0;
    }
    if (!(ipv6_addr_count <= 1)) {
        return 0;
    }

    return 1;
}

int addrParser_GotEverything (addrParser *o)
{
    return (
        o->type_pos == o->type_span
        &&
        o->ip_port_pos == o->ip_port_span
        &&
        o->ipv4_addr_pos == o->ipv4_addr_span
        &&
        o->ipv6_addr_pos == o->ipv6_addr_span
    );
}

int addrParser_Gettype (addrParser *o, uint8_t *v)
{
    ASSERT(o->type_pos >= 0)
    ASSERT(o->type_pos <= o->type_span)

    int left = o->type_span - o->type_pos;

    while (left > 0) {
        ASSERT(left >= sizeof(struct BProto_header_s))
        struct BProto_header_s header;
        memcpy(&header, o->buf + o->type_start + o->type_pos, sizeof(header));
        o->type_pos += sizeof(struct BProto_header_s);
        left -= sizeof(struct BProto_header_s);
        uint16_t type = ltoh16(header.type);
        uint16_t id = ltoh16(header.id);

        switch (type) {
            case BPROTO_TYPE_UINT8: {
                ASSERT(left >= sizeof(struct BProto_uint8_s))
                struct BProto_uint8_s val;
                memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val));
                o->type_pos += sizeof(struct BProto_uint8_s);
                left -= sizeof(struct BProto_uint8_s);

                if (id == 1) {
                    *v = ltoh8(val.v);
                    return 1;
                }
            } break;
            case BPROTO_TYPE_UINT16: {
                ASSERT(left >= sizeof(struct BProto_uint16_s))
                o->type_pos += sizeof(struct BProto_uint16_s);
                left -= sizeof(struct BProto_uint16_s);
            } break;
            case BPROTO_TYPE_UINT32: {
                ASSERT(left >= sizeof(struct BProto_uint32_s))
                o->type_pos += sizeof(struct BProto_uint32_s);
                left -= sizeof(struct BProto_uint32_s);
            } break;
            case BPROTO_TYPE_UINT64: {
                ASSERT(left >= sizeof(struct BProto_uint64_s))
                o->type_pos += sizeof(struct BProto_uint64_s);
                left -= sizeof(struct BProto_uint64_s);
            } break;
            case BPROTO_TYPE_DATA:
            case BPROTO_TYPE_CONSTDATA:
            {
                ASSERT(left >= sizeof(struct BProto_data_header_s))
                struct BProto_data_header_s val;
                memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val));
                o->type_pos += sizeof(struct BProto_data_header_s);
                left -= sizeof(struct BProto_data_header_s);

                uint32_t payload_len = ltoh32(val.len);
                ASSERT(left >= payload_len)
                o->type_pos += payload_len;
                left -= payload_len;
            } break;
            default:
                ASSERT(0);
        }
    }

    return 0;
}

void addrParser_Resettype (addrParser *o)
{
    o->type_pos = 0;
}

void addrParser_Forwardtype (addrParser *o)
{
    o->type_pos = o->type_span;
}

int addrParser_Getip_port (addrParser *o, uint8_t **data)
{
    ASSERT(o->ip_port_pos >= 0)
    ASSERT(o->ip_port_pos <= o->ip_port_span)

    int left = o->ip_port_span - o->ip_port_pos;

    while (left > 0) {
        ASSERT(left >= sizeof(struct BProto_header_s))
        struct BProto_header_s header;
        memcpy(&header, o->buf + o->ip_port_start + o->ip_port_pos, sizeof(header));
        o->ip_port_pos += sizeof(struct BProto_header_s);
        left -= sizeof(struct BProto_header_s);
        uint16_t type = ltoh16(header.type);
        uint16_t id = ltoh16(header.id);

        switch (type) {
            case BPROTO_TYPE_UINT8: {
                ASSERT(left >= sizeof(struct BProto_uint8_s))
                o->ip_port_pos += sizeof(struct BProto_uint8_s);
                left -= sizeof(struct BProto_uint8_s);
            } break;
            case BPROTO_TYPE_UINT16: {
                ASSERT(left >= sizeof(struct BProto_uint16_s))
                o->ip_port_pos += sizeof(struct BProto_uint16_s);
                left -= sizeof(struct BProto_uint16_s);
            } break;
            case BPROTO_TYPE_UINT32: {
                ASSERT(left >= sizeof(struct BProto_uint32_s))
                o->ip_port_pos += sizeof(struct BProto_uint32_s);
                left -= sizeof(struct BProto_uint32_s);
            } break;
            case BPROTO_TYPE_UINT64: {
                ASSERT(left >= sizeof(struct BProto_uint64_s))
                o->ip_port_pos += sizeof(struct BProto_uint64_s);
                left -= sizeof(struct BProto_uint64_s);
            } break;
            case BPROTO_TYPE_DATA:
            case BPROTO_TYPE_CONSTDATA:
            {
                ASSERT(left >= sizeof(struct BProto_data_header_s))
                struct BProto_data_header_s val;
                memcpy(&val, o->buf + o->ip_port_start + o->ip_port_pos, sizeof(val));
                o->ip_port_pos += sizeof(struct BProto_data_header_s);
                left -= sizeof(struct BProto_data_header_s);

                uint32_t payload_len = ltoh32(val.len);
                ASSERT(left >= payload_len)
                uint8_t *payload = o->buf + o->ip_port_start + o->ip_port_pos;
                o->ip_port_pos += payload_len;
                left -= payload_len;

                if (type == BPROTO_TYPE_CONSTDATA && id == 2) {
                    *data = payload;
                    return 1;
                }
            } break;
            default:
                ASSERT(0);
        }
    }

    return 0;
}

void addrParser_Resetip_port (addrParser *o)
{
    o->ip_port_pos = 0;
}

void addrParser_Forwardip_port (addrParser *o)
{
    o->ip_port_pos = o->ip_port_span;
}

int addrParser_Getipv4_addr (addrParser *o, uint8_t **data)
{
    ASSERT(o->ipv4_addr_pos >= 0)
    ASSERT(o->ipv4_addr_pos <= o->ipv4_addr_span)

    int left = o->ipv4_addr_span - o->ipv4_addr_pos;

    while (left > 0) {
        ASSERT(left >= sizeof(struct BProto_header_s))
        struct BProto_header_s header;
        memcpy(&header, o->buf + o->ipv4_addr_start + o->ipv4_addr_pos, sizeof(header));
        o->ipv4_addr_pos += sizeof(struct BProto_header_s);
        left -= sizeof(struct BProto_header_s);
        uint16_t type = ltoh16(header.type);
        uint16_t id = ltoh16(header.id);

        switch (type) {
            case BPROTO_TYPE_UINT8: {
                ASSERT(left >= sizeof(struct BProto_uint8_s))
                o->ipv4_addr_pos += sizeof(struct BProto_uint8_s);
                left -= sizeof(struct BProto_uint8_s);
            } break;
            case BPROTO_TYPE_UINT16: {
                ASSERT(left >= sizeof(struct BProto_uint16_s))
                o->ipv4_addr_pos += sizeof(struct BProto_uint16_s);
                left -= sizeof(struct BProto_uint16_s);
            } break;
            case BPROTO_TYPE_UINT32: {
                ASSERT(left >= sizeof(struct BProto_uint32_s))
                o->ipv4_addr_pos += sizeof(struct BProto_uint32_s);
                left -= sizeof(struct BProto_uint32_s);
            } break;
            case BPROTO_TYPE_UINT64: {
                ASSERT(left >= sizeof(struct BProto_uint64_s))
                o->ipv4_addr_pos += sizeof(struct BProto_uint64_s);
                left -= sizeof(struct BProto_uint64_s);
            } break;
            case BPROTO_TYPE_DATA:
            case BPROTO_TYPE_CONSTDATA:
            {
                ASSERT(left >= sizeof(struct BProto_data_header_s))
                struct BProto_data_header_s val;
                memcpy(&val, o->buf + o->ipv4_addr_start + o->ipv4_addr_pos, sizeof(val));
                o->ipv4_addr_pos += sizeof(struct BProto_data_header_s);
                left -= sizeof(struct BProto_data_header_s);

                uint32_t payload_len = ltoh32(val.len);
                ASSERT(left >= payload_len)
                uint8_t *payload = o->buf + o->ipv4_addr_start + o->ipv4_addr_pos;
                o->ipv4_addr_pos += payload_len;
                left -= payload_len;

                if (type == BPROTO_TYPE_CONSTDATA && id == 3) {
                    *data = payload;
                    return 1;
                }
            } break;
            default:
                ASSERT(0);
        }
    }

    return 0;
}

void addrParser_Resetipv4_addr (addrParser *o)
{
    o->ipv4_addr_pos = 0;
}

void addrParser_Forwardipv4_addr (addrParser *o)
{
    o->ipv4_addr_pos = o->ipv4_addr_span;
}

int addrParser_Getipv6_addr (addrParser *o, uint8_t **data)
{
    ASSERT(o->ipv6_addr_pos >= 0)
    ASSERT(o->ipv6_addr_pos <= o->ipv6_addr_span)

    int left = o->ipv6_addr_span - o->ipv6_addr_pos;

    while (left > 0) {
        ASSERT(left >= sizeof(struct BProto_header_s))
        struct BProto_header_s header;
        memcpy(&header, o->buf + o->ipv6_addr_start + o->ipv6_addr_pos, sizeof(header));
        o->ipv6_addr_pos += sizeof(struct BProto_header_s);
        left -= sizeof(struct BProto_header_s);
        uint16_t type = ltoh16(header.type);
        uint16_t id = ltoh16(header.id);

        switch (type) {
            case BPROTO_TYPE_UINT8: {
                ASSERT(left >= sizeof(struct BProto_uint8_s))
                o->ipv6_addr_pos += sizeof(struct BProto_uint8_s);
                left -= sizeof(struct BProto_uint8_s);
            } break;
            case BPROTO_TYPE_UINT16: {
                ASSERT(left >= sizeof(struct BProto_uint16_s))
                o->ipv6_addr_pos += sizeof(struct BProto_uint16_s);
                left -= sizeof(struct BProto_uint16_s);
            } break;
            case BPROTO_TYPE_UINT32: {
                ASSERT(left >= sizeof(struct BProto_uint32_s))
                o->ipv6_addr_pos += sizeof(struct BProto_uint32_s);
                left -= sizeof(struct BProto_uint32_s);
            } break;
            case BPROTO_TYPE_UINT64: {
                ASSERT(left >= sizeof(struct BProto_uint64_s))
                o->ipv6_addr_pos += sizeof(struct BProto_uint64_s);
                left -= sizeof(struct BProto_uint64_s);
            } break;
            case BPROTO_TYPE_DATA:
            case BPROTO_TYPE_CONSTDATA:
            {
                ASSERT(left >= sizeof(struct BProto_data_header_s))
                struct BProto_data_header_s val;
                memcpy(&val, o->buf + o->ipv6_addr_start + o->ipv6_addr_pos, sizeof(val));
                o->ipv6_addr_pos += sizeof(struct BProto_data_header_s);
                left -= sizeof(struct BProto_data_header_s);

                uint32_t payload_len = ltoh32(val.len);
                ASSERT(left >= payload_len)
                uint8_t *payload = o->buf + o->ipv6_addr_start + o->ipv6_addr_pos;
                o->ipv6_addr_pos += payload_len;
                left -= payload_len;

                if (type == BPROTO_TYPE_CONSTDATA && id == 4) {
                    *data = payload;
                    return 1;
                }
            } break;
            default:
                ASSERT(0);
        }
    }

    return 0;
}

void addrParser_Resetipv6_addr (addrParser *o)
{
    o->ipv6_addr_pos = 0;
}

void addrParser_Forwardipv6_addr (addrParser *o)
{
    o->ipv6_addr_pos = o->ipv6_addr_span;
}

