// core/wire/bytes.h — the ONE little-endian byte codec. Pure, header-only:
// standard library only — no REAPER, no SWELL, no VST3.
//
// component_state_io is the biggest consumer. Wire format is FROZEN: putLE
// emits fixed-width LSB-first bytes exactly as the hand-rolled copies it
// replaced did, and ByteReader preserves the latch-on-truncation contract —
// once a read runs past the end, ok latches false and every subsequent read
// yields zeros/empties, so a truncated blob degrades to a partial parse,
// never an out-of-bounds read.

#pragma once

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <type_traits>
#include <vector>

namespace reasampler::wire {

// Append `v` little-endian (LSB first, sizeof(T) bytes). Unsigned integral types
// only — signed values go on the wire as their two's-complement unsigned image
// (cast at the call site, the established idiom: u32 for int, u64 for int64).
template <class T>
inline void putLE(std::vector<std::uint8_t>& out, T v) {
    static_assert(std::is_unsigned_v<T> && !std::is_same_v<T, bool>, "putLE takes the unsigned wire image");
    for (std::size_t b = 0; b < sizeof(T); ++b) {
        out.push_back(static_cast<std::uint8_t>((v >> (b * 8)) & 0xFF));
    }
}

// Signed 64-bit values ride the wire as their two's-complement unsigned image (the putLE
// call site's u64 cast). The one home for that cast — component_state_io and params_payload
// both need it and must not carry their own copies.
inline std::uint64_t asU64(std::int64_t v) { return static_cast<std::uint64_t>(v); }

// IEEE-754 double <-> u64 bit-cast for the wire (memcpy is the only defined
// type-pun in C++17). Doubles ride the wire as their u64 bit image via putLE.
inline std::uint64_t doubleToBits(double d) {
    std::uint64_t bits;
    std::memcpy(&bits, &d, sizeof(bits));
    return bits;
}
inline double bitsToDouble(std::uint64_t bits) {
    double d;
    std::memcpy(&d, &bits, sizeof(d));
    return d;
}

// A bounded little-endian reader over a byte blob (see the file header for the
// truncation-latch contract).
struct ByteReader {
    const std::vector<std::uint8_t>& bytes;
    std::size_t pos = 0;
    bool ok = true;

    explicit ByteReader(const std::vector<std::uint8_t>& b) : bytes(b) {}

    // Read one unsigned integral little-endian (fixed sizeof(T) width).
    template <class T>
    T readLE() {
        static_assert(std::is_unsigned_v<T>, "readLE yields the unsigned wire image");
        if (!ok || pos + sizeof(T) > bytes.size()) {
            ok = false;
            return 0;
        }
        T v = 0;
        for (std::size_t b = 0; b < sizeof(T); ++b) {
            v |= static_cast<T>(bytes[pos + b]) << (b * 8);
        }
        pos += sizeof(T);
        return v;
    }

    std::uint8_t u8() { return readLE<std::uint8_t>(); }
    std::uint32_t u32() { return readLE<std::uint32_t>(); }
    std::uint64_t u64() { return readLE<std::uint64_t>(); }
    // Signed ints ride the wire as fixed-width two's-complement unsigned images.
    int i32() { return static_cast<int>(static_cast<std::int32_t>(u32())); }
    std::int64_t i64() { return static_cast<std::int64_t>(u64()); }

    std::string str(std::uint32_t len) {
        if (!ok || pos + len > bytes.size()) {
            ok = false;
            return {};
        }
        std::string s(reinterpret_cast<const char*>(bytes.data() + pos), len);
        pos += len;
        return s;
    }

    // Non-consuming peek of the next u32 (format-marker probes). Yields 0 and
    // latches nothing when fewer than 4 bytes remain — the caller treats a short
    // blob as "no marker" and falls through to its (also-guarded) fallback read.
    std::uint32_t peekU32() const {
        if (!ok || pos + 4 > bytes.size()) return 0;
        return static_cast<std::uint32_t>(bytes[pos]) |
               (static_cast<std::uint32_t>(bytes[pos + 1]) << 8) |
               (static_cast<std::uint32_t>(bytes[pos + 2]) << 16) |
               (static_cast<std::uint32_t>(bytes[pos + 3]) << 24);
    }
};

}  // namespace reasampler::wire
