#pragma once
// Minimal, self-contained QR encoder for the provisioning screen.
//
// Local only: no cloud API, no dynamic large allocation, no network dependency
// (docs/wifi-provisioning-plan.md section 33). Byte mode, error-correction
// level M, versions 1..10, which is far more than the ~60-byte Wi-Fi payload
// needs and keeps the tables small.
//
// The encoder writes into a caller-provided fixed buffer; the caller then draws
// modules with graphics primitives and supplies its own quiet zone.
#include <array>
#include <cstdlib>
#include <cstdint>
#include <cstddef>
#include <cstring>

namespace wearable {

// Largest supported symbol: version 10 is 57x57 modules.
constexpr int QR_MAX_MODULES = 57;
// Version 1..10 byte-mode capacity at ECC level M (data codewords).
constexpr int QR_MAX_VERSION = 10;

class QrCode {
public:
    // Encode `text` (UTF-8 bytes) into the module grid. Returns false when the
    // text does not fit or the arguments are invalid; `size()` is 0 on failure.
    bool encode(const char *text, size_t length) {
        size_ = 0;
        if (!text || length == 0 || length > 200) return false;
        int version = 0;
        for (int v = 1; v <= QR_MAX_VERSION; ++v) {
            const size_t capacity = data_codewords(v);
            // Mode(4) + count(8) + payload + terminator fits in data bits.
            const size_t needed = 4 + 8 + length * 8;
            if (needed <= capacity * 8) { version = v; break; }
        }
        if (version == 0) return false;
        const int size = version * 4 + 17;
        size_ = size;

        // --- Bit stream: mode 0100, 8-bit length, data bytes --------------
        uint8_t data[QR_MAX_MODULES * QR_MAX_MODULES / 8 + 16] = {};
        const size_t data_len = data_codewords(version);
        size_t bit = 0;
        auto push = [&](uint32_t value, int count) {
            for (int i = count - 1; i >= 0; --i) {
                if ((value >> i) & 1u) data[bit >> 3] |= uint8_t(0x80 >> (bit & 7));
                ++bit;
            }
        };
        push(0b0100, 4);
        push(uint32_t(length), 8);
        for (size_t i = 0; i < length; ++i) push(uint8_t(text[i]), 8);
        // Terminator up to four zero bits.
        const size_t capacity_bits = data_len * 8;
        for (int i = 0; i < 4 && bit < capacity_bits; ++i) push(0, 1);
        // Pad to a byte boundary, then alternate 0xEC / 0x11.
        while (bit % 8 != 0 && bit < capacity_bits) push(0, 1);
        // How many pad codewords remain. The count is derived up front so the
        // loop below advances its own induction variable and is visible to
        // -Wfor-loop-analysis (Apple Clang), which cannot see the `push`
        // lambda mutate `bit`. The emitted bit stream is identical.
        const size_t pad_codewords = (capacity_bits - bit) / 8;
        for (size_t pad_index = 0; pad_index < pad_codewords; ++pad_index) {
            const uint8_t pad = (pad_index % 2 == 0) ? 0xEC : 0x11;
            push(pad, 8);
        }

        // --- Error correction --------------------------------------------
        uint8_t ecc[QR_MAX_MODULES * 4] = {};
        const int ecc_len = ecc_codewords(version);
        const int blocks = block_count(version);
        if (!reed_solomon(data, data_len, blocks, ecc, ecc_len)) return false;

        // --- Build the final codeword sequence (data blocks + ecc interleave)
        uint8_t codewords[QR_MAX_MODULES * QR_MAX_MODULES] = {};
        interleave(data, data_len, ecc, ecc_len, blocks, codewords);
        // Total codewords = data + per-block ECC across every block.
        const size_t codeword_count = data_len + size_t(ecc_len) * size_t(blocks);
        debug_codewords_ = codeword_count > sizeof(debug_) ? sizeof(debug_) : codeword_count;
        std::memcpy(debug_, codewords, debug_codewords_);

        // --- Place modules -------------------------------------------------
        clear();
        if (!place(version, codewords, codeword_count)) { size_ = 0; return false; }
        premask_valid_ = false;
        mask(version);
        return true;
    }

    int size() const { return size_; }
    bool module(int x, int y) const {
        if (x < 0 || y < 0 || x >= size_ || y >= size_) return false;
        return (grid_[y * QR_MAX_MODULES + x] & 1u) != 0;
    }
    // Test/debug only: whether a module belongs to a function pattern or format
    // area (and therefore is never masked or used for data).
    bool reserved(int x, int y) const {
        if (x < 0 || y < 0 || x >= size_ || y >= size_) return false;
        return reserved_[y * QR_MAX_MODULES + x] != 0;
    }
    // Test/debug only: the module value before masking was applied.
    bool premask_module(int x, int y) const {
        if (!premask_valid_ || x < 0 || y < 0 || x >= size_ || y >= size_) return false;
        return (premask_[y * QR_MAX_MODULES + x] & 1u) != 0;
    }
    // Test/debug only: the final interleaved codeword sequence.
    size_t debug_codeword_count() const { return debug_codewords_; }
    uint8_t debug_codeword(size_t index) const { return index < debug_codewords_ ? debug_[index] : 0; }

private:
    static size_t data_codewords(int version) {
        // Total codewords minus ECC codewords, version 1..10, ECC level M.
        static const uint8_t table[QR_MAX_VERSION + 1] = {0, 16, 28, 44, 64, 86, 108, 124, 154, 182, 216};
        return table[version];
    }
    static int ecc_codewords(int version) {
        static const uint8_t table[QR_MAX_VERSION + 1] = {0, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26};
        return table[version];
    }
    static int block_count(int version) {
        static const uint8_t table[QR_MAX_VERSION + 1] = {0, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5};
        return table[version];
    }

    void clear() {
        for (size_t i = 0; i < sizeof(grid_); ++i) grid_[i] = 0;
        reserved_.fill(0);
    }
    void set_module(int x, int y, bool dark) {
        grid_[y * QR_MAX_MODULES + x] = dark ? 1 : 0;
        reserved_[y * QR_MAX_MODULES + x] = 1;
    }
    void set_function(int x, int y, bool dark) {
        grid_[y * QR_MAX_MODULES + x] = dark ? 1 : 0;
        reserved_[y * QR_MAX_MODULES + x] = 1;
    }

    // Reed-Solomon over GF(256) with primitive polynomial 0x11d. Splits the data
    // codewords into `blocks` groups and appends ECC codewords to `ecc`.
    // `generator()` builds the degree-`ecc_len` generator in ascending exponent
    // order with the leading coefficient last (g[ecc_len] == 1).
    bool reed_solomon(const uint8_t *data, size_t data_len, int blocks,
                      uint8_t *ecc, int ecc_len) {
        const int per_block = int(data_len) / blocks;
        const int extra = int(data_len) % blocks;
        generator(ecc_len);
        size_t offset = 0;
        for (int b = 0; b < blocks; ++b) {
            const int count = per_block + (b >= blocks - extra ? 1 : 0);
            uint8_t block[128] = {};
            uint8_t remainder[128] = {};
            for (int i = 0; i < count; ++i) block[i] = data[offset++];
            // Polynomial long division in place over block || 0^ecc_len.
            uint8_t work[256] = {};
            std::memcpy(work, block, size_t(count));
            for (int i = 0; i < count; ++i) {
                const uint8_t coefficient = work[i];
                if (coefficient == 0) continue;
                for (int j = 0; j <= ecc_len; ++j)
                    work[i + j] ^= gf_mul(gen_[j], coefficient);
            }
            for (int i = 0; i < ecc_len; ++i) remainder[i] = work[count + i];
            for (int i = 0; i < ecc_len; ++i) ecc[b * ecc_len + i] = remainder[i];
        }
        return true;
    }

    void generator(int degree) {
        // Build the monic generator polynomial product (x - alpha^i) in
        // ascending coefficient order, then reverse to descending so gen_[0]
        // is the leading 1 that the division loop expects.
        uint8_t ascending[128] = {};
        ascending[0] = 1;
        int length = 1;
        for (int d = 0; d < degree; ++d) {
            for (int k = length; k > 0; --k) ascending[k] = ascending[k - 1];
            ascending[0] = 0;
            ++length;
            for (int j = 0; j < length - 1; ++j)
                ascending[j] ^= gf_mul(ascending[j + 1], gf_exp(d));
        }
        for (int i = 0; i <= degree; ++i) gen_[i] = ascending[degree - i];
    }

    static uint8_t gf_mul(uint8_t a, uint8_t b) {
        uint16_t result = 0;
        for (int i = 0; i < 8; ++i) {
            if (b & 1) result ^= a;
            const bool high = (a & 0x80) != 0;
            a = uint8_t(a << 1);
            if (high) a ^= 0x1d;
            b = uint8_t(b >> 1);
        }
        return uint8_t(result);
    }
    static uint8_t gf_exp(int power) {
        static uint8_t table[256];
        static bool ready = false;
        if (!ready) {
            int value = 1;
            for (int i = 0; i < 255; ++i) {
                table[i] = uint8_t(value);
                value <<= 1;
                if (value & 0x100) value ^= 0x11d;
            }
            table[255] = table[0];
            ready = true;
        }
        return table[power % 255];
    }

    void interleave(const uint8_t *data, size_t data_len, const uint8_t *ecc,
                    int ecc_len, int blocks, uint8_t *out) {
        const int per_block = int(data_len) / blocks;
        const int extra = int(data_len) % blocks;
        size_t out_index = 0;
        for (int i = 0; i < per_block + (extra ? 1 : 0); ++i) {
            for (int b = 0; b < blocks; ++b) {
                const int count = per_block + (b >= blocks - extra && extra ? 1 : 0);
                if (i < count) out[out_index++] = data[b * per_block + std::min(b, extra) + i];
            }
        }
        for (int i = 0; i < ecc_len; ++i)
            for (int b = 0; b < blocks; ++b)
                out[out_index++] = ecc[b * ecc_len + i];
    }

    bool place(int version, const uint8_t *codewords, size_t codeword_count) {
        const int size = version * 4 + 17;
        // Finder patterns + separators.
        for (int y = -1; y <= 7; ++y) {
            for (int x = -1; x <= 7; ++x) {
                const bool in = x >= 0 && x <= 6 && y >= 0 && y <= 6;
                const bool dark = in && (x == 0 || x == 6 || y == 0 || y == 6 ||
                                         (x >= 2 && x <= 4 && y >= 2 && y <= 4));
                if (y >= 0 && y < size && x >= 0 && x < size) set_function(x, y, dark);
                if (y >= 0 && y < size && x + size - 7 >= 0 && x + size - 7 < size)
                    set_function(x + size - 7, y, dark);
                if (y + size - 7 >= 0 && y + size - 7 < size && x >= 0 && x < size)
                    set_function(x, y + size - 7, dark);
            }
        }
        // Timing patterns.
        for (int i = 8; i < size - 8; ++i) {
            set_function(i, 6, i % 2 == 0);
            set_function(6, i, i % 2 == 0);
        }
        // Alignment pattern (version >= 2).
        if (version >= 2) {
            static const uint8_t centers[QR_MAX_VERSION + 1] = {0, 0, 18, 22, 26, 30, 34, 22, 24, 26, 28};
            const int c = centers[version];
            for (int dy = -2; dy <= 2; ++dy)
                for (int dx = -2; dx <= 2; ++dx) {
                    const bool dark = std::abs(dx) == 2 || std::abs(dy) == 2 || (dx == 0 && dy == 0);
                    set_function(c + dx, c + dy, dark);
                }
        }
        // Reserve format areas (filled by mask()).
        for (int i = 0; i < 9; ++i) {
            if (i != 6) { set_function(8, i, false); set_function(i, 8, false); }
            if (i < 8) { set_function(8, size - 1 - i, false); set_function(size - 1 - i, 8, false); }
        }
        set_function(8, size - 8, false);
        // Dark module.
        set_function(8, size - 8, true);

        // Data placement: two-column zigzag from bottom-right.
        size_t index = 0;
        int bit = 0;
        bool upward = true;
        for (int right = size - 1; right > 0; right -= 2) {
            if (right == 6) --right;
            for (int i = 0; i < size; ++i) {
                const int y = upward ? size - 1 - i : i;
                for (int column = 0; column < 2; ++column) {
                    const int x = right - column;
                    if (reserved_[y * QR_MAX_MODULES + x]) continue;
                    bool value = false;
                    if (index < codeword_count) value = ((codewords[index] >> (7 - bit)) & 1u) != 0;
                    grid_[y * QR_MAX_MODULES + x] = value ? 1 : 0;
                    if (++bit == 8) { bit = 0; ++index; }
                }
            }
            upward = !upward;
        }
        return true;
    }

    void mask(int /*version*/) {
        const int size = size_;
        // Snapshot the placed (pre-mask) grid for tests and diagnostics.
        std::memcpy(premask_, grid_, sizeof(grid_));
        premask_valid_ = true;
        // Apply mask pattern 0 ((x+y) % 2) and write format information.
        for (int y = 0; y < size; ++y)
            for (int x = 0; x < size; ++x)
                if (!reserved_[y * QR_MAX_MODULES + x] && ((x + y) % 2 == 0))
                    grid_[y * QR_MAX_MODULES + x] ^= 1;
        // Format info for ECC level M (0b00) and mask 0, BCH protected.
        const uint16_t format = 0b101010000010010;
        for (int i = 0; i < 15; ++i) {
            const bool bit = (format >> i) & 1u;
            // Vertical strip near top-left and horizontal strip; standard fixed
            // positions for the two copies of the format information.
            if (i < 6) set_function(8, i, bit);
            else if (i < 8) set_function(8, i + 1, bit);
            else if (i == 8) set_function(7, 8, bit);
            else set_function(14 - i, 8, bit);
            if (i < 8) set_function(size - 1 - i, 8, bit);
            else set_function(8, size - 15 + i, bit);
        }
        set_function(8, size - 8, true);
    }

    uint8_t grid_[QR_MAX_MODULES * QR_MAX_MODULES] = {};
    uint8_t premask_[QR_MAX_MODULES * QR_MAX_MODULES] = {};
    uint8_t debug_[QR_MAX_MODULES * QR_MAX_MODULES] = {};
    size_t debug_codewords_ = 0;
    bool premask_valid_ = false;
    std::array<uint8_t, QR_MAX_MODULES * QR_MAX_MODULES> reserved_{};
    uint8_t gen_[64] = {};
    int size_ = 0;
};

}  // namespace wearable