// Local QR encoder for the provisioning screen (host).
//
// Verifies the encoder against a reference matrix produced by an independent
// implementation (python-qrcode, byte mode, ECC level M, mask pattern 0) for
// the exact provisioning payload. The matrix is embedded so the test has no
// Python or network dependency.
#include "qr_code.h"
#include "wifi_selection.h"
#include <cassert>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>

using namespace wearable;

static const char *const kExpected[] = {
    "111111100101000000110010001111111",
    "100000101110110111011111001000001",
    "101110100000000110010011101011101",
    "101110100110110001010010101011101",
    "101110101001000000000001001011101",
    "100000100010110001100101101000001",
    "111111101010101010101010101111111",
    "000000000010010011101111100000000",
    "101010100001011101011100000010010",
    "111010001001101000101100000001111",
    "011001100110010001000000011001111",
    "000011011011000000010110001110001",
    "110110100101000000000100111000111",
    "100110010110110101010110111000010",
    "011010101010010001100100011111111",
    "100010001001000101010110001100001",
    "011011100111011001101100011100011",
    "001101011110000001001101100100011",
    "011100111100010010101010011010011",
    "001110010001001011110100000000000",
    "000001100001110010101111011000101",
    "000100010001010110111110101000100",
    "100111100101111110011000001010011",
    "011000001001011011101111110100000",
    "100000110000101110010101111111001",
    "000000001011101110011000100011111",
    "111111100110111011001111101011111",
    "100000100100101000011111100011011",
    "101110101101011000001110111111000",
    "101110100001101101010010000000001",
    "101110101110001000100100101110101",
    "100000100001100101100101110110010",
    "111111101001110001110100100001011",
};
static const int kExpectedSize = 33;

int main() {
    // ---- Payload construction -------------------------------------------
    char payload[160];
    provisioning_qr_payload("AgentPager-A2F3", PROVISIONING_AP_PASSWORD, payload, sizeof(payload));
    assert(std::strcmp(payload, "WIFI:T:WPA;S:AgentPager-A2F3;P:agentpager-setup;;") == 0);

    // ---- Exact matrix match against the reference -----------------------
    QrCode qr;
    assert(qr.encode(payload, std::strlen(payload)));
    assert(qr.size() == kExpectedSize);
    for (int y = 0; y < kExpectedSize; ++y)
        for (int x = 0; x < kExpectedSize; ++x)
            assert(qr.module(x, y) == (kExpected[y][x] == '1'));

    // ---- Structural invariants ------------------------------------------
    // Finder patterns must be present and correct (so a scanner can lock on).
    for (int y = 0; y < 7; ++y) {
        for (int x = 0; x < 7; ++x) {
            const bool dark = x == 0 || x == 6 || y == 0 || y == 6 ||
                              (x >= 2 && x <= 4 && y >= 2 && y <= 4);
            assert(qr.module(x, y) == dark);
            assert(qr.module(kExpectedSize - 1 - x, y) == dark);
            assert(qr.module(x, kExpectedSize - 1 - y) == dark);
        }
    }

    // ---- Deterministic and payload-sensitive ----------------------------
    QrCode again;
    assert(again.encode(payload, std::strlen(payload)));
    for (int y = 0; y < kExpectedSize; ++y)
        for (int x = 0; x < kExpectedSize; ++x)
            assert(again.module(x, y) == qr.module(x, y));
    QrCode other;
    const char *different = "WIFI:T:WPA;S:AgentPager-A2F3;P:different-password;;";
    assert(other.encode(different, std::strlen(different)));
    bool differs = false;
    for (int y = 0; y < other.size() && y < qr.size(); ++y)
        for (int x = 0; x < other.size() && x < qr.size(); ++x)
            if (other.module(x, y) != qr.module(x, y)) differs = true;
    assert(differs);

    // ---- Version growth for longer payloads ------------------------------
    QrCode small;
    assert(small.encode("x", 1));
    assert(small.size() == 21);  // version 1
    QrCode big;
    std::string long_ssid(32, 'A');
    char long_payload[160];
    provisioning_qr_payload(long_ssid.c_str(), "0123456789012345678901234567890123456789", long_payload, sizeof(long_payload));
    assert(big.encode(long_payload, std::strlen(long_payload)));
    assert(big.size() > small.size());
    assert(big.size() <= QR_MAX_MODULES);

    // ---- Rejects impossible input ---------------------------------------
    QrCode none;
    assert(!none.encode(nullptr, 0));
    assert(!none.encode("", 0));
    assert(none.size() == 0);
    std::string too_long(300, 'a');
    assert(!none.encode(too_long.c_str(), too_long.size()));

    // ---- Debug accessors used by diagnostics ----------------------------
    assert(qr.debug_codeword_count() == 100);  // version 4-M: 64 data + 2x18 ecc
    assert(!qr.reserved(-1, -1));

    std::puts("QR encoder byte-mode matrix match, finder invariants, growth, bounds PASS");
}