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

#include "calibration_master.hpp"
#include "data_preprocessor.hpp"
#include "reference_master.hpp"
#include "run_config.hpp"
#include "shm_ring.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;
}

}  // 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 preprocessed_ring = radar::ipc::ShmRing::open_or_create(
            config.rings.preprocessed.name,
            config.rings.preprocessed.capacity,
            config.rings.preprocessed.slot_size_bytes
        );
        auto preprocessed_tap_ring = radar::ipc::ShmRing::open_or_create(
            config.rings.preprocessed_tap.name,
            config.rings.preprocessed_tap.capacity,
            config.rings.preprocessed_tap.slot_size_bytes
        );

        // Load preprocessing assets once before entering run loop.
        radar::preprocessing::CalibrationMaster calibration_master(radar::preprocessing::make_through_calibrator());
        calibration_master.load_bundle(config.preprocess.calibration_bundle_path);

        radar::preprocessing::ReferenceMaster reference_master;
        reference_master.load_bundle(config.preprocess.reference_bundle_path);
        reference_master.prepare_calibrated(calibration_master);

        radar::preprocessing::DataPreprocessor preprocessor(
            config,
            calibration_master,
            reference_master,
            raw_ring,
            preprocessed_ring,
            &preprocessed_tap_ring
        );
        preprocessor.run(g_stop_requested);
        return 0;
    } catch (const std::exception& exception) {
        std::cerr << "data_preprocessor error: " << exception.what() << '\n';
        return 1;
    }
}
