#include <cstdio>
#include <csignal>
#include <cstring>

#include "lr1121_malnus.hpp"

static volatile std::sig_atomic_t g_stop = 0;

static void onSignal(int)
{
    g_stop = 1;
}

static void applyPaPreset(lr1121::Config &cfg, bool hp_mode)
{
    cfg.pa_sel = hp_mode ? lr1121::PA_HP : lr1121::PA_LP;
    cfg.tx_dbm = hp_mode ? 14 : 8;
}

static bool parseArgs(int argc, char **argv, bool &verbose, bool &do_reset, bool &hp_mode)
{
    for (int i = 1; i < argc; ++i) {
        if      (std::strcmp(argv[i], "-v")      == 0) verbose  = true;
        else if (std::strcmp(argv[i], "--reset") == 0) do_reset = true;
        else if (std::strcmp(argv[i], "--hp")    == 0) hp_mode  = true;
        else if (std::strcmp(argv[i], "--lp")    == 0) hp_mode  = false;
        else {
            std::fprintf(stderr, "Unknown option: %s\n"
                                 "Usage: sudo ./lora_rx [-v] [--reset] [--lp|--hp]\n", argv[i]);
            return false;
        }
    }
    return true;
}

int main(int argc, char **argv)
{
    std::signal(SIGINT, onSignal);
    std::signal(SIGTERM, onSignal);

    lr1121::Config cfg;
    constexpr uint32_t rx_timeout_ms = 1000;
    bool verbose  = false;
    bool do_reset = false;
    bool hp_mode  = false;

    if (!parseArgs(argc, argv, verbose, do_reset, hp_mode)) return 2;
    applyPaPreset(cfg, hp_mode);

    std::printf("RX %u Hz, tmo=%ums, gpio(busy=%u reset=%u dio9=%u)%s\n",
                cfg.freq_hz, rx_timeout_ms,
                cfg.busy_gpio, cfg.reset_gpio, cfg.dio9_gpio,
                verbose ? " [v]" : "");

    lr1121::Radio radio;
    if (!radio.begin(cfg)) {
        std::fprintf(stderr, "ERROR: radio init failed: %s\n",
                     lr1121::Radio::errorString(radio.lastError()));
        return 1;
    }
    if (verbose) {
        const auto v = radio.chipVersion();
        std::printf("[lr1121] hw=0x%02X type=0x%02X fw=0x%02X%02X vbat=%.2fV\n",
                    v.hw, v.type, v.fw_hi, v.fw_lo, radio.vbatVolts());
    }
    if (do_reset) {
        std::puts("Applying soft settings reset...");
        if (!radio.softResetSettings()) {
            std::fprintf(stderr, "ERROR: soft settings reset failed: %s\n",
                         lr1121::Radio::errorString(radio.lastError()));
            return 1;
        }
    }
    if (!radio.startListening()) {
        std::fprintf(stderr, "ERROR: start listening failed: %s\n",
                     lr1121::Radio::errorString(radio.lastError()));
        return 1;
    }
    std::puts("Radio OK - listening (continuous RX). Press Ctrl+C to stop.");

    uint8_t buf[256];
    int pkt = 0;
    uint32_t timeout_total = 0;
    uint32_t crc_total = 0;

    while (!g_stop) {
        lr1121::RxInfo info{};
        const int r = radio.receive(buf, uint8_t(sizeof(buf) - 1), rx_timeout_ms, &info);
        if (r > 0) {
            buf[r] = '\0';
            std::printf("[%04d] %3d B rssi=%4d snr=%3d '%s'\n",
                        ++pkt, r, info.rssi_dbm, info.snr_db, buf);
        } else if (r == -2) {
            std::printf("crc error x%u\n", ++crc_total);
        } else if (radio.lastError() == lr1121::Error::RxTimeout) {
            ++timeout_total;
            if ((timeout_total % 10u) == 0u)
                std::printf("timeout x%u\n", timeout_total);
        } else {
            std::printf("rx error: %s\n",
                        lr1121::Radio::errorString(radio.lastError()));
        }
    }

    std::puts("\nStopping listener...");
    radio.stopListening();
    radio.end();
    std::puts("Radio OK - stopped listening...");
    return 0;
}
