#include "w3cs_compact_draw.h"

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

static void round_trip_sequence(void)
{
    struct w3cs_compact_draw_state encoder;
    struct w3cs_compact_draw_state decoder;
    struct w3cs_indexed_draw source[] = {
        {4u, -12, 3u, 900u, 24u, 300u, 0x12345678u, 0xfeedbeefu},
        {4u, -10, 3u, 912u, 324u, 4u, 0x12345678u, 0x87654321u},
        {6u, INT32_MAX, UINT32_MAX, 0u, UINT32_MAX, UINT32_MAX,
         0u, 0xffffffffu},
    };
    uint8_t payload[W3CS_COMPACT_DRAW_MAX_PAYLOAD];
    size_t index;
    w3cs_compact_draw_reset(&encoder);
    w3cs_compact_draw_reset(&decoder);
    w3cs_compact_draw_observe_blob(&encoder, 0x12345678u);
    w3cs_compact_draw_observe_blob(&decoder, 0x12345678u);
    for (index = 0; index < sizeof(source) / sizeof(source[0]); ++index) {
        struct w3cs_indexed_draw decoded;
        size_t size = w3cs_compact_draw_encode(
            &encoder, &source[index], payload, sizeof(payload));
        assert(size > 0u && size <= sizeof(payload));
        assert(w3cs_compact_draw_decode(&decoder, payload, size, &decoded));
        assert(memcmp(&decoded, &source[index], sizeof(decoded)) == 0);
    }
}

static void fallback_keeps_predictor_aligned(void)
{
    struct w3cs_compact_draw_state encoder;
    struct w3cs_compact_draw_state decoder;
    struct w3cs_indexed_draw first = {4u, 20, 1u, 3u, 7u, 1u, 8u, 9u};
    struct w3cs_indexed_draw second = {4u, 21, 1u, 6u, 8u, 2u, 8u, 9u};
    struct w3cs_indexed_draw decoded;
    uint8_t tiny[1];
    uint8_t payload[W3CS_COMPACT_DRAW_MAX_PAYLOAD];
    size_t size;
    w3cs_compact_draw_reset(&encoder);
    w3cs_compact_draw_reset(&decoder);
    assert(w3cs_compact_draw_encode(&encoder, &first, tiny, sizeof(tiny)) == 0);
    w3cs_compact_draw_accept_full(&decoder, &first);
    size = w3cs_compact_draw_encode(
        &encoder, &second, payload, sizeof(payload));
    assert(size > 0u);
    assert(w3cs_compact_draw_decode(&decoder, payload, size, &decoded));
    assert(memcmp(&decoded, &second, sizeof(decoded)) == 0);
}

static void rejects_noncanonical_and_unknown_handles(void)
{
    struct w3cs_compact_draw_state decoder;
    struct w3cs_indexed_draw draw;
    /* Eight fields: overlong zero primitive, then seven canonical zeros. */
    const uint8_t overlong[] = {0x80u, 0x00u, 0, 0, 0, 0, 0, 0, 0};
    const uint8_t unknown[] = {0, 0, 0, 0, 0, 0, 1, 0};
    w3cs_compact_draw_reset(&decoder);
    assert(!w3cs_compact_draw_decode(
        &decoder, overlong, sizeof(overlong), &draw));
    assert(!w3cs_compact_draw_decode(
        &decoder, unknown, sizeof(unknown), &draw));
}

int main(void)
{
    _Static_assert(sizeof(struct w3cs_indexed_draw) == 32u,
                   "indexed draw wire layout changed");
    round_trip_sequence();
    fallback_keeps_predictor_aligned();
    rejects_noncanonical_and_unknown_handles();
    puts("compact draw tests passed");
    return 0;
}
