#include "application.hpp"
#include "http_server.hpp"

#include <array>
#include <csignal>
#include <filesystem>
#include <iostream>
#include <sstream>
#include <string>

namespace fs = std::filesystem;

namespace
{
HttpServer *active_server = nullptr;
Application *active_application = nullptr;

void handleSignal(int)
{
    if (active_server)
        active_server->stop();
    if (active_application)
        active_application->shutdown();
}

std::array<std::string, CameraManager::CameraCount> parseCameras(const std::string &value)
{
    std::array<std::string, CameraManager::CameraCount> result{};
    std::stringstream stream(value);
    std::string item;
    std::size_t index = 0;
    while (std::getline(stream, item, ',') && index < result.size())
        result[index++] = item;
    if (index != result.size() || std::getline(stream, item, ','))
        throw std::invalid_argument("--cams kräver exakt tre kommaseparerade källor");
    return result;
}

void printHelp()
{
    std::cout
        << "OpenDartboard x86 " << APP_VERSION << " (Linux x86_64)\n\n"
        << "Användning: opendartboard-x86 [flaggor]\n"
        << "  --host ADDRESS       Bind-adress (standard 0.0.0.0)\n"
        << "  --port PORT          Webbport (standard 13520)\n"
        << "  --cams A,B,C         Tre /dev/video*-enheter eller videofiler\n"
        << "  --mocks              Använd projektets tre testvideor\n"
        << "  --width PIXLAR       Behandlingsbredd (standard 1280)\n"
        << "  --height PIXLAR      Behandlingshöjd (standard 720)\n"
        << "  --fps FPS            Kamerafrekvens (standard 15)\n"
        << "  --web-root KATALOG   Katalog med webbgränssnittet\n"
        << "  --data-dir KATALOG   Sparad konfiguration\n"
        << "  --version            Visa version\n"
        << "  --help               Visa denna hjälp\n";
}
} // namespace

int main(int argc, char **argv)
{
#ifndef OPENDARTBOARD_LINUX_X86_64
#error This program is supported on Linux x86_64 only
#endif

    std::string host = "0.0.0.0";
    int port = 13520;
    int width = 1280;
    int height = 720;
    int fps = 15;
    std::string web_root = OPENDARTBOARD_DEFAULT_WEB_ROOT;
    std::string data_directory = OPENDARTBOARD_DEFAULT_DATA_ROOT;
    std::array<std::string, CameraManager::CameraCount> cameras{};

    try
    {
        for (int index = 1; index < argc; ++index)
        {
            const std::string argument = argv[index];
            const auto next = [&]() -> std::string
            {
                if (index + 1 >= argc)
                    throw std::invalid_argument("Värde saknas efter " + argument);
                return argv[++index];
            };

            if (argument == "--host")
                host = next();
            else if (argument == "--port")
                port = std::stoi(next());
            else if (argument == "--width")
                width = std::stoi(next());
            else if (argument == "--height")
                height = std::stoi(next());
            else if (argument == "--fps")
                fps = std::stoi(next());
            else if (argument == "--web-root")
                web_root = next();
            else if (argument == "--data-dir")
                data_directory = next();
            else if (argument == "--cams")
                cameras = parseCameras(next());
            else if (argument == "--mocks")
            {
                const fs::path root = fs::path(OPENDARTBOARD_DEFAULT_WEB_ROOT).parent_path().parent_path();
                cameras = {
                    (root / "mocks/cam_1.mp4").string(),
                    (root / "mocks/cam_2.mp4").string(),
                    (root / "mocks/cam_3.mp4").string(),
                };
            }
            else if (argument == "--version")
            {
                std::cout << APP_VERSION << '\n';
                return 0;
            }
            else if (argument == "--help" || argument == "-h")
            {
                printHelp();
                return 0;
            }
            else
            {
                throw std::invalid_argument("Okänd flagga: " + argument);
            }
        }

        if (port < 1 || port > 65535 || width < 320 || height < 240 || fps < 1 || fps > 120)
            throw std::invalid_argument("Ogiltig port, upplösning eller FPS");

        Application application(width, height, fps, cameras, data_directory);
        HttpServer server(application, host, port, web_root);
        active_application = &application;
        active_server = &server;
        std::signal(SIGINT, handleSignal);
        std::signal(SIGTERM, handleSignal);

        std::cout << "OpenDartboard x86 " << APP_VERSION << " kör på http://" << host << ':' << port << '\n'
                  << "Öppna http://localhost:" << port << " i webbläsaren." << std::endl;
        const bool success = server.run();
        application.shutdown();
        active_server = nullptr;
        active_application = nullptr;
        return success ? 0 : 1;
    }
    catch (const std::exception &error)
    {
        std::cerr << "Fel: " << error.what() << '\n';
        return 1;
    }
}
