#include "byteio.h"
#include "memory.h"

/* ========================== *
 * Writer
 * ========================== */

void bw_seek(struct byte_writer *bw, u64 amount)
{
    if (bw_overflow_check(bw, amount)) {
        return;
    }
    bw->at += amount;
}

void bw_seek_to(struct byte_writer *bw, u64 pos)
{
    if (pos > bw->buff.size) {
        ASSERT(false);
        bw->overflowed = true;
    }
    bw->at = bw->buff.data + pos;
}

INTERNAL void write_unsafe(struct byte_writer *bw, void *v, u64 size)
{
    MEMCPY(bw->at, v, size);
    bw->at += size;
}

INTERNAL void write(struct byte_writer *bw, void *v, u64 size)
{
    if (bw_overflow_check(bw, sizeof(v))) {
        return;
    }
    write_unsafe(bw, v, size);
}

void bw_write_buffer(struct byte_writer *bw, struct buffer buff)
{
    write(bw, buff.data, buff.size);
}

void bw_write_u8(struct byte_writer *bw, u8 v)
{
    write(bw, &v, sizeof(v));
}

void bw_write_u16(struct byte_writer *bw, u16 v)
{
    write(bw, &v, sizeof(v));
}

void bw_write_u32(struct byte_writer *bw, u32 v)
{
    write(bw, &v, sizeof(v));
}

void bw_write_u64(struct byte_writer *bw, u64 v)
{
    write(bw, &v, sizeof(v));
}

void bw_write_i8(struct byte_writer *bw, i8 v)
{
    write(bw, &v, sizeof(v));
}

void bw_write_i16(struct byte_writer *bw, i16 v)
{
    write(bw, &v, sizeof(v));
}

void bw_write_i32(struct byte_writer *bw, i32 v)
{
    write(bw, &v, sizeof(v));
}

void bw_write_i64(struct byte_writer *bw, i64 v)
{
    write(bw, &v, sizeof(v));
}

void bw_write_var_uint(struct byte_writer *bw, u64 v)
{
    /* TODO: real varint write */
    bw_write_u64(bw, v);
}

void bw_write_var_sint(struct byte_writer *bw, i64 v)
{
    /* TODO: real varint write */
    bw_write_i64(bw, v);
}

/* ========================== *
 * Reader
 * ========================== */

void br_seek(struct byte_reader *br, u64 amount)
{
    if (br_overflow_check(br, amount)) {
        return;
    }
    br->at += amount;
}

void br_seek_to(struct byte_reader *br, u64 pos)
{
    if (pos > br->buff.size) {
        ASSERT(false);
        br->overflowed = true;
    }
    br->at = br->buff.data + pos;
}

INTERNAL void *read_unsafe(struct byte_reader *br, u64 size)
{
    void *prev = br->at;
    br->at += size;
    return prev;
}

/* Will return NULL on overflow */
void *br_read_raw(struct byte_reader *br, u64 size)
{
    if (br_overflow_check(br, size)) {
        return NULL;
    }
    return read_unsafe(br, size);
}

/* Will not read any data on overflow */
void br_read_to_buffer(struct byte_reader *br, struct buffer buff)
{
    if (br_overflow_check(br, buff.size)) {
        return;
    }
    u8 *bytes = read_unsafe(br, buff.size);
    MEMCPY(buff.data, bytes, buff.size);
}

u8 br_read_u8(struct byte_reader *br)
{
    if (br_overflow_check(br, sizeof(u8))) {
        return 0;
    }
    u8 res = 0;
    MEMCPY(&res, read_unsafe(br, sizeof(u8)), sizeof(u8));
    return res;
}

u16 br_read_u16(struct byte_reader *br)
{
    if (br_overflow_check(br, sizeof(u16))) {
        return 0;
    }
    u16 res = 0;
    MEMCPY(&res, read_unsafe(br, sizeof(u16)), sizeof(u16));
    return res;
}

u32 br_read_u32(struct byte_reader *br)
{
    if (br_overflow_check(br, sizeof(u32))) {
        return 0;
    }
    u32 res = 0;
    MEMCPY(&res, read_unsafe(br, sizeof(u32)), sizeof(u32));
    return res;
}

u64 br_read_u64(struct byte_reader *br)
{
    if (br_overflow_check(br, sizeof(u64))) {
        return 0;
    }
    u64 res = 0;
    MEMCPY(&res, read_unsafe(br, sizeof(u64)), sizeof(u64));
    return res;
}

i8 br_read_i8(struct byte_reader *br)
{
    if (br_overflow_check(br, sizeof(i8))) {
        return 0;
    }
    i8 res = 0;
    MEMCPY(&res, read_unsafe(br, sizeof(i8)), sizeof(i8));
    return res;
}

i16 br_read_i16(struct byte_reader *br)
{
    if (br_overflow_check(br, sizeof(i16))) {
        return 0;
    }
    i16 res = 0;
    MEMCPY(&res, read_unsafe(br, sizeof(i16)), sizeof(i16));
    return res;
}

i32 br_read_i32(struct byte_reader *br)
{
    if (br_overflow_check(br, sizeof(i32))) {
        return 0;
    }
    i32 res = 0;
    MEMCPY(&res, read_unsafe(br, sizeof(i32)), sizeof(i32));
    return res;
}

i64 br_read_i64(struct byte_reader *br)
{
    if (br_overflow_check(br, sizeof(i64))) {
        return 0;
    }
    i64 res = 0;
    MEMCPY(&res, read_unsafe(br, sizeof(i64)), sizeof(i64));
    return res;
}

u64 br_read_var_uint(struct byte_reader *br)
{
    /* TODO: real varint read */
    return br_read_u64(br);
}

i64 br_read_var_sint(struct byte_reader *br)
{
    /* TODO: real varint read */
    return br_read_i64(br);
}
