// core/wire implementation — see wire.h. Any behavioral change here changes
// every ext-state wire seam at once.

#include "core/wire/wire.h"

#include <cstdlib>
#include <limits>

namespace reasampler::wire {

void putField(std::string& out, const std::string& field) {
    out += std::to_string(field.size());
    out += ':';
    out += field;
}

bool parseUnsignedDecimal(const std::string& s, std::int64_t& out) {
    if (s.empty()) return false;
    std::int64_t value = 0;
    constexpr std::int64_t kMax = std::numeric_limits<std::int64_t>::max();
    for (const char c : s) {
        if (c < '0' || c > '9') return false;  // any non-digit -> reject whole
        const int digit = c - '0';
        // Guard value*10 + digit against overflow before performing it.
        if (value > (kMax - digit) / 10) return false;
        value = value * 10 + digit;
    }
    out = value;
    return true;
}

bool Cursor::literal(const char* lit) {
    if (!ok_) return false;
    std::size_t i = 0;
    for (; lit[i] != '\0'; ++i) {
        if (pos_ + i >= s_.size() || s_[pos_ + i] != lit[i]) return fail();
    }
    pos_ += i;
    return true;
}

bool Cursor::field(std::string& out) {
    if (!ok_) return false;
    const std::size_t colon = s_.find(':', pos_);
    if (colon == std::string::npos) return fail();
    if (colon == pos_) return fail();  // empty length token
    // Cap: SIZE_MAX fits in at most 20 decimal digits; a longer run is bogus.
    if (colon - pos_ > 20u) return fail();
    std::size_t len = 0;
    for (std::size_t i = pos_; i < colon; ++i) {
        const char c = s_[i];
        if (c < '0' || c > '9') return fail();
        const std::size_t digit = static_cast<std::size_t>(c - '0');
        // Overflow guard: if len would exceed SIZE_MAX after multiply+add, fail.
        if (len > (std::numeric_limits<std::size_t>::max() - digit) / 10u)
            return fail();
        len = len * 10u + digit;
    }
    const std::size_t start = colon + 1;
    // Subtraction-first form: start + len cannot wrap on a huge len.
    if (start > s_.size() || len > s_.size() - start) return fail();
    out.assign(s_, start, len);
    pos_ = start + len;
    return true;
}

bool Cursor::fieldInt64(std::int64_t& out) {
    std::string f;
    if (!field(f)) return false;
    if (f.empty()) return fail();
    std::size_t i = 0;
    bool neg = false;
    if (f[0] == '-') {
        neg = true;
        i = 1;
        if (f.size() == 1) return fail();  // bare "-"
    }
    // Cap at 19 digits (INT64_MAX = 9223372036854775807 — 19 digits). A 20-digit
    // positive value would overflow INT64_MAX; a 20-digit negative might be valid
    // (INT64_MIN) but is conservatively rejected too — see header.
    if (f.size() - i > 19u) return fail();
    std::int64_t v = 0;
    for (; i < f.size(); ++i) {
        const char c = f[i];
        if (c < '0' || c > '9') return fail();
        const std::int64_t digit = static_cast<std::int64_t>(c - '0');
        // Overflow guard: v * 10 + digit must not exceed INT64_MAX.
        if (v > (std::numeric_limits<std::int64_t>::max() - digit) / 10)
            return fail();
        v = v * 10 + digit;
    }
    out = neg ? -v : v;
    return true;
}

bool Cursor::fieldInt(int& out) {
    std::int64_t v = 0;
    if (!fieldInt64(v)) return false;
    if (v < std::numeric_limits<int>::min() || v > std::numeric_limits<int>::max())
        return fail();
    out = static_cast<int>(v);
    return true;
}

bool Cursor::fieldSizeT(std::size_t& out) {
    std::string f;
    if (!field(f)) return false;
    if (f.empty() || f.size() > 20u) return fail();
    std::size_t v = 0;
    for (const char c : f) {
        if (c < '0' || c > '9') return fail();
        const std::size_t digit = static_cast<std::size_t>(c - '0');
        if (v > (std::numeric_limits<std::size_t>::max() - digit) / 10u)
            return fail();
        v = v * 10u + digit;
    }
    out = v;
    return true;
}

bool Cursor::fieldDouble(double& out) {
    std::string f;
    if (!field(f)) return false;
    const char* b = f.c_str();
    char* end = nullptr;
    double v = std::strtod(b, &end);
    if (end != b + f.size()) return fail();
    out = v;
    return true;
}

}  // namespace reasampler::wire
