#include "config.h"
#include "renderer.h"
#include <csignal>
#include <cstdio>
#include <cstring>

static volatile bool g_running = true;

static void signal_handler(int) { g_running = false; }

static void print_usage(const char* prog) {
    std::fprintf(stderr,
        "Usage: %s [options] [config.ini] [label@host:port[/group:dataport] ...]\n"
        "\n"
        "Options:\n"
        "  --help      Show this message\n"
        "\n"
        "Sources can be specified:\n"
        "  - In [source.N] sections of the INI file\n"
        "  - As label@host:port arguments on the command line\n"
        "    (e.g.  MySource@192.168.1.100:44500)\n"
        "  - With optional multicast suffix /group:dataport\n"
        "    (e.g.  MySource@192.168.1.100:44500/239.0.0.1:44501)\n"
        "\n"
        "If no INI file is given, defaults are used.\n",
        prog);
}

/**
 * Parse  label@host:port[/group:dataport]  into a SourceConfig.
 * Returns false on failure.
 */
static bool parse_source_arg(const char* arg, SourceConfig& sc) {
    std::string s(arg);

    // Split off optional multicast suffix after '/'
    auto slash = s.find('/');
    std::string mcast_part;
    if (slash != std::string::npos) {
        mcast_part = s.substr(slash + 1);
        s          = s.substr(0, slash);
    }

    auto at  = s.find('@');
    auto col = s.rfind(':');
    if (at == std::string::npos || col == std::string::npos || col < at) {
        // Try host:port without label
        if (col != std::string::npos) {
            sc.label = "Source";
            sc.host  = s.substr(0, col);
            try { sc.port = static_cast<uint16_t>(std::stoi(s.substr(col + 1))); }
            catch (...) { return false; }
        } else {
            return false;
        }
    } else {
        sc.label = s.substr(0, at);
        sc.host  = s.substr(at + 1, col - at - 1);
        try { sc.port = static_cast<uint16_t>(std::stoi(s.substr(col + 1))); }
        catch (...) { return false; }
    }

    // Parse optional multicast suffix: group:dataport
    if (!mcast_part.empty()) {
        auto mc = mcast_part.rfind(':');
        if (mc != std::string::npos) {
            sc.multicast_group = mcast_part.substr(0, mc);
            try { sc.data_port = static_cast<uint16_t>(std::stoi(mcast_part.substr(mc + 1))); }
            catch (...) { sc.data_port = 0; }
        } else {
            sc.multicast_group = mcast_part;
            sc.data_port = 0;
        }
    }

    return true;
}

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

    const char*             config_path = nullptr;
    std::vector<SourceConfig> extra_sources;

    for (int i = 1; i < argc; ++i) {
        if (std::strcmp(argv[i], "--help") == 0) {
            print_usage(argv[0]);
            return 0;
        } else if (argv[i][0] != '-') {
            // Either a config file or a label@host:port
            SourceConfig sc;
            if (parse_source_arg(argv[i], sc)) {
                extra_sources.push_back(sc);
            } else {
                config_path = argv[i];
            }
        }
    }

    AppConfig cfg;
    if (config_path) {
        try {
            cfg = parse_ini(config_path);
        } catch (const std::exception& e) {
            std::fprintf(stderr, "Config error: %s\n", e.what());
            return 1;
        }
    }

    // Merge CLI sources
    for (auto& sc : extra_sources)
        cfg.sources.push_back(sc);

    if (cfg.sources.empty()) {
        std::fprintf(stderr,
            "Warning: no sources configured.  Use --help for usage.\n"
            "Starting without sources; add them via Sources → Add Source.\n");
    }

    Renderer renderer(cfg);
    if (!renderer.init()) {
        std::fprintf(stderr, "Failed to initialise renderer.\n");
        return 1;
    }

    renderer.run();
    return 0;
}
