#ifndef PROTOTYPE1_SRC_L1_CORE_CHICKEN_VECU8_H
#define PROTOTYPE1_SRC_L1_CORE_CHICKEN_VECU8_H

#ifndef PROTOTYPE1_L1_CODEGEN_BOOTSTRAP_USE_CHICKEN_VECU8
#error "Use chicken_VecU8.h only during the very first l1 bootstrap stage"
#endif

#include "util.h"

typedef struct {
    U8* buf;
    size_t len;
    size_t capacity;
} VecU8;

NODISCARD VecU8 VecU8_new() {
    return (VecU8){NULL, 0, 0};
}

void VecU8_drop(VecU8 obj) {
    free(obj.buf);
}

VecU8 VecU8_new_reserved(size_t n) {
    return (VecU8){ .buf = safe_calloc(n, sizeof(U8)), .len = 0, .capacity = n };
}

void VecU8_append(VecU8* self, U8 el) {
    size_t new_length = self->len + 1;
    if (new_length > self->capacity) {
        size_t new_capacity = Vec_get_new_capacity(self->capacity, new_length);
        self->buf = safe_realloc(self->buf, new_capacity * sizeof(U8));
        self->capacity = new_capacity;
    }
    self->buf[self->len] = el;
    self->len = new_length;
}

U8* VecU8_mat(VecU8* self, size_t i) {
    assert(i < self->len);
    return self->buf + i;
}

const U8* VecU8_at(const VecU8* self, size_t i) {
    assert(i < self->len);
    return self->buf + i;
}

NODISCARD VecU8 VecU8_clone(const VecU8* self) {
    VecU8 res = (VecU8){.buf = safe_calloc(self->len, sizeof(U8)), .len = self->len, .capacity = self->len};
    memcpy(res.buf, self->buf, self->len);
    return res;
}

void VecU8_append_vec(VecU8* self, VecU8 b) {
    size_t new_length = self->len + b.len;
    if (new_length > self->capacity) {
        size_t new_capacity = Vec_get_new_capacity(self->capacity, new_length);
        self->buf = safe_realloc(self->buf, new_capacity * sizeof(U8));
        self->capacity = new_capacity;
    }
    for (size_t i = 0; i < b.len; i++) {
        self->buf[self->len + i] = b.buf[i];
    }
    self->len = new_length;
    free(b.buf);
}

typedef struct {
    const U8* data;
    size_t len;
} SpanU8;

const U8* SpanU8_at(SpanU8 self, size_t i) {
    assert(i < self.len);
    return self.data + i;
}

NODISCARD VecU8 VecU8_from_span(SpanU8 src) {
    VecU8 res = (VecU8){ .buf = safe_calloc(src.len, sizeof(U8)), .len = src.len, .capacity = src.len };
    memcpy(res.buf, src.data, src.len);
    return res;
}

SpanU8 VecU8_to_span(const VecU8* vec) {
    return (SpanU8){vec->buf, vec->len};
}

void VecU8_append_span(VecU8* self, SpanU8 b) {
    size_t new_length = self->len + b.len;
    if (new_length > self->capacity) {
        size_t new_capacity = Vec_get_new_capacity(self->capacity, new_length);
        self->buf = safe_realloc(self->buf, new_capacity * sizeof(U8));
        self->capacity = new_capacity;
    }
    memcpy(self->buf + self->len, b.data, b.len);
    self->len = new_length;
}

#endif
