// Display images inside a terminal
// Copyright (C) 2023  JustKidding
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include <csignal>

#include <CLI/App.hpp>
#include <CLI/Config.hpp>
#include <CLI/Formatter.hpp>
#include <spdlog/cfg/env.h>
#include <spdlog/spdlog.h>

#include <CLI/Version.hpp>
#if (CLI11_VERSION_MAJOR >= 3) || (CLI11_VERSION_MAJOR == 2 && CLI11_VERSION_MINOR >= 6)
// CLI11 2.6.0 and beyond requires this header file for the CLI::IsMember validator.
#  include <CLI/ExtraValidators.hpp>
#endif

#include "application.hpp"
#include "flags.hpp"
#include "tmux.hpp"
#include "util.hpp"

void signal_handler(const int signal)
{
    Application::stop_flag = true;

    const auto logger = spdlog::get("main");
    if (!logger) {
        return;
    }
    switch (signal) {
        case SIGINT:
            logger->error("SIGINT received, exiting...");
            break;
        case SIGTERM:
            logger->error("SIGTERM received, exiting...");
            break;
        case SIGHUP:
            logger->error("SIGHUP received, exiting...");
            break;
        default:
            logger->error("UNKNOWN({}) signal received, exiting...", signal);
            break;
    }
}

auto main(int argc, char *argv[]) -> int
{
    // handle signals
    struct sigaction sga;
    sga.sa_handler = signal_handler;
    sigemptyset(&sga.sa_mask);
    sga.sa_flags = 0;
    sigaction(SIGINT, &sga, nullptr);
    sigaction(SIGTERM, &sga, nullptr);
    sigaction(SIGHUP, &sga, nullptr);
    sigaction(SIGCHLD, nullptr, nullptr);

    spdlog::cfg::load_env_levels();

    std::shared_ptr<Flags> flags;
    try {
        flags = Flags::instance();
    } catch (const std::exception &e) {
        std::cerr << "Could not parse config file: " << e.what() << std::endl;
        return 1;
    }

    CLI::App program("Display images in the terminal", "ueberzug");
    program.add_flag("-V,--version", flags->print_version, "Print version information.");

    auto *layer_command = program.add_subcommand("layer", "Display images on the terminal.");
    layer_command->add_flag("-s,--silent", flags->silent, "Print stderr to /dev/null.");
    layer_command
        ->add_flag("--use-escape-codes", flags->use_escape_codes, "Use escape codes to get terminal capabilities.")
        ->default_val(false);
    layer_command->add_option("--pid-file", flags->pid_file, "Output file where to write the daemon PID.");
    layer_command->add_flag("--no-stdin", flags->no_stdin, "Do not listen on stdin for commands.")->needs("--pid-file");
    layer_command->add_flag("--no-cache", flags->no_cache, "Disable caching of resized images.");
    layer_command->add_flag("--no-opencv", flags->no_opencv, "Do not use OpenCV, use Libvips instead.");
    layer_command->add_option("-o,--output", flags->output, "Image output method")
        ->check(CLI::IsMember({"x11", "wayland", "sixel", "kitty", "iterm2", "chafa"}));
    layer_command->add_flag("--origin-center", flags->origin_center, "Location of the origin wrt the image");
    layer_command->add_option("-p,--parser", nullptr, "**UNUSED**, only present for backwards compatibility.");
    layer_command->add_option("-l,--loader", nullptr, "**UNUSED**, only present for backwards compatibility.");

    auto *cmd_comand = program.add_subcommand("cmd", "Send a command to a running ueberzugpp instance.");
    cmd_comand->add_option("-s,--socket", flags->cmd_socket, "UNIX socket of running instance");
    cmd_comand->add_option("-i,--identifier", flags->cmd_id, "Preview identifier");
    cmd_comand->add_option("-a,--action", flags->cmd_action, "Action to send");
    cmd_comand->add_option("-f,--file", flags->cmd_file_path, "Path of image file");
    cmd_comand->add_option("-x,--xpos", flags->cmd_x, "X position of preview");
    cmd_comand->add_option("-y,--ypos", flags->cmd_y, "Y position of preview");
    cmd_comand->add_option("--max-width", flags->cmd_max_width, "Max width of preview");
    cmd_comand->add_option("--max-height", flags->cmd_max_height, "Max height of preview");

    auto *tmux_command = program.add_subcommand("tmux", "Handle tmux hooks. Used internaly.");
    tmux_command->allow_extras();

    auto *query_win_command =
        program.add_subcommand("query_windows", "**UNUSED**, only present for backwards compatibility.");
    query_win_command->allow_extras();

    CLI11_PARSE(program, argc, argv);

    if (query_win_command->parsed()) {
        return 0;
    }

    if (flags->print_version) {
        Application::print_version();
        return 0;
    }

    if (!layer_command->parsed() && !tmux_command->parsed() && !cmd_comand->parsed()) {
        program.exit(CLI::CallForHelp());
        return 1;
    }

    if (layer_command->parsed()) {
        Application application(argv[0]);
        application.command_loop();
    }

    if (tmux_command->parsed()) {
        try {
            const auto positionals = tmux_command->remaining();
            tmux::handle_hook(positionals.at(0), std::stoi(positionals.at(1)));
        } catch (const std::out_of_range &oor) {
        }
    }

    if (cmd_comand->parsed()) {
        util::send_command(*flags);
    }

    return 0;
}
