#include <atomic>
#include <csignal>
#include <exception>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>

#include "h7992_minimal_driver.hpp"
#include "hmc349a_minimal_driver.hpp"
#include "librevna_minimal_driver.hpp"
#include "run_config.hpp"
#include "shm_ring.hpp"
#include "sweep_orchestrator.hpp"

namespace {

constexpr const char* kDefaultConfigPath = "run_config.json";
std::atomic<bool> g_stop_requested{false};

void signal_handler(int /*signal*/) {
    g_stop_requested.store(true, std::memory_order_relaxed);
}

void install_signal_handlers() {
    std::signal(SIGINT, signal_handler);
    std::signal(SIGTERM, signal_handler);
}

[[nodiscard]] auto read_config_path(int argc, char** argv) -> std::string {
    std::string config_path = kDefaultConfigPath;

    for (int index = 1; index < argc; ++index) {
        const std::string arg = argv[index];
        if (arg == "--config" && (index + 1) < argc) {
            config_path = argv[++index];
        }
    }

    return config_path;
}

[[nodiscard]] auto make_radar_driver(const radar::config::RadarConfig& config) -> radar::drivers::LibreVnaMinimalDriver {
    return radar::drivers::LibreVnaMinimalDriver({
        .mode = config.driver_mode,
        .serial = config.serial,
        .sweep = config.sweep,
        .mock_signal_hz = config.mock_signal_hz,
    });
}

[[nodiscard]] auto make_h7992_driver(const radar::config::SwitchConfig& config)
    -> std::unique_ptr<radar::drivers::SwitchDriver> {
    return std::make_unique<radar::drivers::H7992MinimalDriver>(
        radar::drivers::H7992MinimalDriverSettings{
            .name = config.name,
            .mode = config.driver_mode,
            .positions = config.positions,
            .default_position = config.default_position,
            .gpio_chip = config.gpio_chip,
            .pin_a = config.pin_a,
            .pin_b = config.pin_b,
        }
    );
}

[[nodiscard]] auto make_hmc349a_driver(const radar::config::SwitchConfig& config)
    -> std::unique_ptr<radar::drivers::SwitchDriver> {
    return std::make_unique<radar::drivers::HMC349AMinimalDriver>(
        radar::drivers::HMC349AMinimalDriverSettings{
            .name = config.name,
            .mode = config.driver_mode,
            .positions = config.positions,
            .default_position = config.default_position,
            .gpio_chip = config.gpio_chip,
            .pin_a = config.pin_a,
            .invert_logic = config.invert_logic,
        }
    );
}

[[nodiscard]] auto make_switch_driver(const radar::config::SwitchConfig& config)
    -> std::unique_ptr<radar::drivers::SwitchDriver> {
    using radar::config::SwitchDriverKind;

    switch (config.driver_kind) {
        case SwitchDriverKind::H7992:
            return make_h7992_driver(config);
        case SwitchDriverKind::HMC349A:
            return make_hmc349a_driver(config);
        default:
            throw std::runtime_error("Unsupported switch driver kind");
    }
}

}  // namespace

int main(int argc, char** argv) {
    install_signal_handlers();

    try {
        const auto config_path = read_config_path(argc, argv);
        const auto config = radar::config::load_run_config(config_path);

        auto raw_ring = radar::ipc::ShmRing::open_or_create(
            config.rings.raw.name,
            config.rings.raw.capacity,
            config.rings.raw.slot_size_bytes
        );
        auto raw_tap_ring = radar::ipc::ShmRing::open_or_create(
            config.rings.raw_tap.name,
            config.rings.raw_tap.capacity,
            config.rings.raw_tap.slot_size_bytes
        );

        auto radar_driver = make_radar_driver(config.radar);

        auto input_switch = make_switch_driver(config.input_switch);
        auto output_switch = make_switch_driver(config.output_switch);

        radar::acq::SweepOrchestrator orchestrator(
            config,
            radar_driver,
            *input_switch,
            *output_switch,
            raw_ring,
            &raw_tap_ring
        );
        orchestrator.run(g_stop_requested);
        return 0;
    } catch (const std::exception& exception) {
        std::cerr << "sweep_orchestrator error: " << exception.what() << '\n';
        return 1;
    }
}
