#include "pxl8_bytes.h"
#include <string.h>

void pxl8_pack_u8(u8* buf, size_t offset, u8 val) {
    buf[offset] = val;
}

void pxl8_pack_u16_le(u8* buf, size_t offset, u16 val) {
    buf[offset]     = (u8)(val);
    buf[offset + 1] = (u8)(val >> 8);
}

void pxl8_pack_u16_be(u8* buf, size_t offset, u16 val) {
    buf[offset]     = (u8)(val >> 8);
    buf[offset + 1] = (u8)(val);
}

void pxl8_pack_u32_le(u8* buf, size_t offset, u32 val) {
    buf[offset]     = (u8)(val);
    buf[offset + 1] = (u8)(val >> 8);
    buf[offset + 2] = (u8)(val >> 16);
    buf[offset + 3] = (u8)(val >> 24);
}

void pxl8_pack_u32_be(u8* buf, size_t offset, u32 val) {
    buf[offset]     = (u8)(val >> 24);
    buf[offset + 1] = (u8)(val >> 16);
    buf[offset + 2] = (u8)(val >> 8);
    buf[offset + 3] = (u8)(val);
}

void pxl8_pack_u64_le(u8* buf, size_t offset, u64 val) {
    buf[offset]     = (u8)(val);
    buf[offset + 1] = (u8)(val >> 8);
    buf[offset + 2] = (u8)(val >> 16);
    buf[offset + 3] = (u8)(val >> 24);
    buf[offset + 4] = (u8)(val >> 32);
    buf[offset + 5] = (u8)(val >> 40);
    buf[offset + 6] = (u8)(val >> 48);
    buf[offset + 7] = (u8)(val >> 56);
}

void pxl8_pack_u64_be(u8* buf, size_t offset, u64 val) {
    buf[offset]     = (u8)(val >> 56);
    buf[offset + 1] = (u8)(val >> 48);
    buf[offset + 2] = (u8)(val >> 40);
    buf[offset + 3] = (u8)(val >> 32);
    buf[offset + 4] = (u8)(val >> 24);
    buf[offset + 5] = (u8)(val >> 16);
    buf[offset + 6] = (u8)(val >> 8);
    buf[offset + 7] = (u8)(val);
}

void pxl8_pack_i8(u8* buf, size_t offset, i8 val) {
    buf[offset] = (u8)val;
}

void pxl8_pack_i16_le(u8* buf, size_t offset, i16 val) {
    pxl8_pack_u16_le(buf, offset, (u16)val);
}

void pxl8_pack_i16_be(u8* buf, size_t offset, i16 val) {
    pxl8_pack_u16_be(buf, offset, (u16)val);
}

void pxl8_pack_i32_le(u8* buf, size_t offset, i32 val) {
    pxl8_pack_u32_le(buf, offset, (u32)val);
}

void pxl8_pack_i32_be(u8* buf, size_t offset, i32 val) {
    pxl8_pack_u32_be(buf, offset, (u32)val);
}

void pxl8_pack_i64_le(u8* buf, size_t offset, i64 val) {
    pxl8_pack_u64_le(buf, offset, (u64)val);
}

void pxl8_pack_i64_be(u8* buf, size_t offset, i64 val) {
    pxl8_pack_u64_be(buf, offset, (u64)val);
}

void pxl8_pack_f32_le(u8* buf, size_t offset, f32 val) {
    u32 bits;
    memcpy(&bits, &val, sizeof(bits));
    pxl8_pack_u32_le(buf, offset, bits);
}

void pxl8_pack_f32_be(u8* buf, size_t offset, f32 val) {
    u32 bits;
    memcpy(&bits, &val, sizeof(bits));
    pxl8_pack_u32_be(buf, offset, bits);
}

void pxl8_pack_f64_le(u8* buf, size_t offset, f64 val) {
    u64 bits;
    memcpy(&bits, &val, sizeof(bits));
    pxl8_pack_u64_le(buf, offset, bits);
}

void pxl8_pack_f64_be(u8* buf, size_t offset, f64 val) {
    u64 bits;
    memcpy(&bits, &val, sizeof(bits));
    pxl8_pack_u64_be(buf, offset, bits);
}

u8 pxl8_unpack_u8(const u8* buf, size_t offset) {
    return buf[offset];
}

u16 pxl8_unpack_u16_le(const u8* buf, size_t offset) {
    return (u16)buf[offset] | ((u16)buf[offset + 1] << 8);
}

u16 pxl8_unpack_u16_be(const u8* buf, size_t offset) {
    return ((u16)buf[offset] << 8) | (u16)buf[offset + 1];
}

u32 pxl8_unpack_u32_le(const u8* buf, size_t offset) {
    return (u32)buf[offset] |
           ((u32)buf[offset + 1] << 8) |
           ((u32)buf[offset + 2] << 16) |
           ((u32)buf[offset + 3] << 24);
}

u32 pxl8_unpack_u32_be(const u8* buf, size_t offset) {
    return ((u32)buf[offset] << 24) |
           ((u32)buf[offset + 1] << 16) |
           ((u32)buf[offset + 2] << 8) |
           (u32)buf[offset + 3];
}

u64 pxl8_unpack_u64_le(const u8* buf, size_t offset) {
    return (u64)buf[offset] |
           ((u64)buf[offset + 1] << 8) |
           ((u64)buf[offset + 2] << 16) |
           ((u64)buf[offset + 3] << 24) |
           ((u64)buf[offset + 4] << 32) |
           ((u64)buf[offset + 5] << 40) |
           ((u64)buf[offset + 6] << 48) |
           ((u64)buf[offset + 7] << 56);
}

u64 pxl8_unpack_u64_be(const u8* buf, size_t offset) {
    return ((u64)buf[offset] << 56) |
           ((u64)buf[offset + 1] << 48) |
           ((u64)buf[offset + 2] << 40) |
           ((u64)buf[offset + 3] << 32) |
           ((u64)buf[offset + 4] << 24) |
           ((u64)buf[offset + 5] << 16) |
           ((u64)buf[offset + 6] << 8) |
           (u64)buf[offset + 7];
}

i8 pxl8_unpack_i8(const u8* buf, size_t offset) {
    return (i8)buf[offset];
}

i16 pxl8_unpack_i16_le(const u8* buf, size_t offset) {
    return (i16)pxl8_unpack_u16_le(buf, offset);
}

i16 pxl8_unpack_i16_be(const u8* buf, size_t offset) {
    return (i16)pxl8_unpack_u16_be(buf, offset);
}

i32 pxl8_unpack_i32_le(const u8* buf, size_t offset) {
    return (i32)pxl8_unpack_u32_le(buf, offset);
}

i32 pxl8_unpack_i32_be(const u8* buf, size_t offset) {
    return (i32)pxl8_unpack_u32_be(buf, offset);
}

i64 pxl8_unpack_i64_le(const u8* buf, size_t offset) {
    return (i64)pxl8_unpack_u64_le(buf, offset);
}

i64 pxl8_unpack_i64_be(const u8* buf, size_t offset) {
    return (i64)pxl8_unpack_u64_be(buf, offset);
}

f32 pxl8_unpack_f32_le(const u8* buf, size_t offset) {
    u32 bits = pxl8_unpack_u32_le(buf, offset);
    f32 result;
    memcpy(&result, &bits, sizeof(result));
    return result;
}

f32 pxl8_unpack_f32_be(const u8* buf, size_t offset) {
    u32 bits = pxl8_unpack_u32_be(buf, offset);
    f32 result;
    memcpy(&result, &bits, sizeof(result));
    return result;
}

f64 pxl8_unpack_f64_le(const u8* buf, size_t offset) {
    u64 bits = pxl8_unpack_u64_le(buf, offset);
    f64 result;
    memcpy(&result, &bits, sizeof(result));
    return result;
}

f64 pxl8_unpack_f64_be(const u8* buf, size_t offset) {
    u64 bits = pxl8_unpack_u64_be(buf, offset);
    f64 result;
    memcpy(&result, &bits, sizeof(result));
    return result;
}

void pxl8_bit_set(u32* val, u8 bit) {
    *val |= (1u << bit);
}

void pxl8_bit_clear(u32* val, u8 bit) {
    *val &= ~(1u << bit);
}

bool pxl8_bit_test(u32 val, u8 bit) {
    return (val & (1u << bit)) != 0;
}

u32 pxl8_bit_count(u32 val) {
#if defined(__GNUC__) || defined(__clang__)
    return (u32)__builtin_popcount(val);
#else
    val = val - ((val >> 1) & 0x55555555);
    val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
    return (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
#endif
}

void pxl8_bit_toggle(u32* val, u8 bit) {
    *val ^= (1u << bit);
}
