/*
    * main.cpp
    * sdr - software-defined radio receive demo.
    *
    * Exercises the generic SDR Rx API end to end: enumerate receivers, open one,
    * tune it, configure sample rate / gain, stream raw I/Q for a short window,
    * and report basic signal statistics.  The RTL-SDR (RTL2832U/R820T2) driver
    * provides the receiver; with no dongle attached the demo reports that and
    * exits cleanly.
    *
    * Usage:
    *   sdr                       tune 100.100 MHz at 2.048 Msps (defaults)
    *   sdr <freqMHz>             e.g. "sdr 433.92"
    *   sdr <freqMHz> <rateHz>    e.g. "sdr 100.1 2400000"
    * Copyright (c) 2026 Daniel Hammer
*/

#include <montauk/syscall.h>
#include <montauk/string.h>

using namespace montauk;

// ---------------------------------------------------------------------------
// Small integer / formatting helpers (freestanding; no libc printf)
// ---------------------------------------------------------------------------

static void put_u64(uint64_t n) {
    char buf[24];
    int i = 0;
    if (n == 0) { putchar('0'); return; }
    while (n) { buf[i++] = (char)('0' + (n % 10)); n /= 10; }
    while (i) putchar(buf[--i]);
}

static void put_i64(int64_t n) {
    if (n < 0) { putchar('-'); n = -n; }
    put_u64((uint64_t)n);
}

// Render a frequency in Hz as "NNN.NNN MHz".
static void put_mhz(uint64_t hz) {
    uint64_t mhz  = hz / 1000000;
    uint64_t kfrac = (hz % 1000000) / 1000;   // 3-digit kHz remainder
    put_u64(mhz);
    putchar('.');
    if (kfrac < 100) putchar('0');
    if (kfrac < 10)  putchar('0');
    put_u64(kfrac);
    print(" MHz");
}

// Render an integer sample rate as "N.NNN Msps".
static void put_msps(uint64_t hz) {
    uint64_t whole = hz / 1000000;
    uint64_t frac  = (hz % 1000000) / 1000;
    put_u64(whole);
    putchar('.');
    if (frac < 100) putchar('0');
    if (frac < 10)  putchar('0');
    put_u64(frac);
    print(" Msps");
}

// ---------------------------------------------------------------------------
// Argument parsing
// ---------------------------------------------------------------------------

static uint64_t parse_mhz_to_hz(const char* s) {
    uint64_t intpart = 0;
    while (*s >= '0' && *s <= '9') { intpart = intpart * 10 + (uint64_t)(*s - '0'); s++; }
    uint64_t hz = intpart * 1000000;
    if (*s == '.') {
        s++;
        uint64_t scale = 100000;   // first fractional digit == 100 kHz
        while (*s >= '0' && *s <= '9' && scale > 0) {
            hz += (uint64_t)(*s - '0') * scale;
            scale /= 10;
            s++;
        }
    }
    return hz;
}

static uint64_t parse_u64(const char* s) {
    uint64_t v = 0;
    while (*s >= '0' && *s <= '9') { v = v * 10 + (uint64_t)(*s - '0'); s++; }
    return v;
}

// Copy the next whitespace-delimited token into tokbuf; advance *rest past it.
// Returns false when no token remains.
static bool next_token(const char** rest, char* tokbuf, int cap) {
    const char* s = skip_spaces(*rest);
    if (*s == '\0') { *rest = s; return false; }
    int i = 0;
    while (*s && *s != ' ' && i < cap - 1) tokbuf[i++] = *s++;
    tokbuf[i] = '\0';
    *rest = s;
    return true;
}

// ---------------------------------------------------------------------------
// Demo
// ---------------------------------------------------------------------------

static uint8_t g_iq[64 * 1024];   // I/Q read buffer (CU8)

static void print_receiver(int idx, const montauk::abi::SdrDeviceInfo& info) {
    print("  ["); put_u64((uint64_t)idx); print("] ");
    print(info.name);
    print("  tuner="); print(info.tuner);
    if (info.serial[0]) { print("  ("); print(info.serial); print(")"); }
    print("\n        freq ");
    put_mhz(info.freqMin); print(" - "); put_mhz(info.freqMax);
    print("   rate "); put_u64(info.sampleRateMin); print(" - ");
    put_u64(info.sampleRateMax); print(" Hz\n");
    print("        gains ("); put_u64(info.numGains); print(" steps):");
    uint32_t shown = info.numGains < 32 ? info.numGains : 32;
    for (uint32_t g = 0; g < shown; g++) {
        putchar(' ');
        put_i64(info.gains[g] / 10);
        putchar('.');
        put_u64((uint64_t)(info.gains[g] % 10));
    }
    print(" dB\n");
}

extern "C" void _start() {
    char args[128];
    int alen = montauk::getargs(args, sizeof(args));
    const char* rest = (alen > 0) ? args : "";

    uint64_t freqHz = 100100000ull;   // 100.100 MHz default (FM broadcast band)
    uint32_t rateHz = 2048000;        // 2.048 Msps default

    char tok[64];
    if (next_token(&rest, tok, sizeof(tok))) freqHz = parse_mhz_to_hz(tok);
    if (next_token(&rest, tok, sizeof(tok))) rateHz = (uint32_t)parse_u64(tok);

    print("=== MontaukOS SDR receive demo ===\n\n");

    // --- Enumerate -------------------------------------------------------
    int count = montauk::sdr_count();
    print("SDR receivers detected: "); put_u64((uint64_t)count); print("\n");
    if (count <= 0) {
        print("\nNo SDR receivers are connected.\n");
        print("Plug in an RTL-SDR dongle (Realtek RTL2832U + R820T2) and retry.\n");
        montauk::exit(0);
    }

    montauk::abi::SdrDeviceInfo info;
    for (int i = 0; i < count; i++) {
        if (montauk::sdr_info(i, &info) == 0) print_receiver(i, info);
    }

    // --- Open + configure receiver 0 ------------------------------------
    int idx = 0;
    if (montauk::sdr_info(idx, &info) != 0) {
        print("\nsdr: failed to query receiver info\n");
        montauk::exit(1);
    }

    int h = montauk::sdr_open(idx);
    if (h < 0) {
        print("\nsdr: failed to open receiver 0\n");
        montauk::exit(1);
    }
    print("\nOpened receiver 0 (handle "); put_u64((uint64_t)h); print(")\n");

    // Clamp the requested tuning to the receiver's advertised limits.
    if (freqHz < info.freqMin) freqHz = info.freqMin;
    if (freqHz > info.freqMax) freqHz = info.freqMax;
    if (rateHz < info.sampleRateMin) rateHz = info.sampleRateMin;
    if (rateHz > info.sampleRateMax) rateHz = info.sampleRateMax;

    print("Configuring: ");
    if (montauk::sdr_set_sample_rate(h, rateHz) != 0)
        print("\n  warning: sample rate rejected");
    if (montauk::sdr_set_freq_correction(h, 0) != 0) { /* optional */ }
    if (montauk::sdr_set_gain_mode(h, 0) != 0)        // 0 = auto/AGC
        print("\n  warning: gain mode rejected");
    if (montauk::sdr_set_freq(h, freqHz) != 0)
        print("\n  warning: tune rejected (PLL may be unlocked)");

    print("\n  center : "); put_mhz(montauk::sdr_get_freq(h)); print("\n");
    print("  rate   : "); put_msps(montauk::sdr_get_sample_rate(h));
    print(" ("); put_u64(montauk::sdr_get_sample_rate(h)); print(" Hz)\n");
    print("  gain   : auto (AGC)\n");

    // --- Stream ----------------------------------------------------------
    if (montauk::sdr_start(h) != 0) {
        print("\nsdr: failed to start streaming\n");
        montauk::sdr_close(h);
        montauk::exit(1);
    }
    print("\nStreaming I/Q for ~2s ...\n");

    uint64_t totalBytes = 0;
    int64_t  sumI = 0, sumQ = 0;
    uint64_t sumPower = 0;
    uint32_t peak = 0;

    uint64_t start = montauk::get_milliseconds();
    uint64_t lastReport = start;
    while (montauk::get_milliseconds() - start < 2000) {
        int n = montauk::sdr_read(h, g_iq, sizeof(g_iq));
        if (n <= 0) { montauk::sleep_ms(20); continue; }

        // CU8: interleaved 8-bit unsigned I/Q, 127.5 == zero.
        for (int k = 0; k + 1 < n; k += 2) {
            int i = (int)g_iq[k]     - 127;
            int q = (int)g_iq[k + 1] - 127;
            sumI += i;
            sumQ += q;
            sumPower += (uint64_t)(i * i + q * q);
            uint32_t mi = (uint32_t)(i < 0 ? -i : i);
            uint32_t mq = (uint32_t)(q < 0 ? -q : q);
            if (mi > peak) peak = mi;
            if (mq > peak) peak = mq;
        }
        totalBytes += (uint64_t)n;

        uint64_t now = montauk::get_milliseconds();
        if (now - lastReport >= 500) {
            print("  ... "); put_u64(totalBytes / 1024); print(" KiB\n");
            lastReport = now;
        }
    }

    montauk::sdr_stop(h);

    // --- Report ----------------------------------------------------------
    uint64_t elapsed = montauk::get_milliseconds() - start;
    if (elapsed == 0) elapsed = 1;
    uint64_t pairs = totalBytes / 2;

    print("\n--- capture summary ---\n");
    print("  bytes read   : "); put_u64(totalBytes);
    print(" ("); put_u64(pairs); print(" I/Q samples)\n");
    print("  throughput   : "); put_u64((totalBytes * 1000) / elapsed / 1024);
    print(" KiB/s\n");

    if (pairs == 0) {
        print("  no samples were delivered.\n");
        print("  (expected under emulation / with no dongle actively streaming.)\n");
    } else {
        int64_t meanI = sumI / (int64_t)pairs;
        int64_t meanQ = sumQ / (int64_t)pairs;
        uint64_t avgPower = sumPower / pairs;
        print("  DC offset    : I="); put_i64(meanI);
        print(" Q="); put_i64(meanQ); print("\n");
        print("  avg power    : "); put_u64(avgPower);
        print(" (per-sample I^2+Q^2)\n");
        print("  peak |sample|: "); put_u64((uint64_t)peak); print(" / 128\n");
    }

    montauk::sdr_close(h);
    print("\nClosed receiver. Done.\n");
    montauk::exit(0);
}
