/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu 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.

Hikou no mizu 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 Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Network/Server/Server.hpp"
#include "Configuration/ServerConfiguration.hpp"
#include "Tools/ArgumentParser.hpp"
#include "Tools/Log.hpp"
#include "Tools/Format.hpp"
#include "Tools/Filesystem.hpp"
#include "Tools/BuildValues.hpp" ///< Generated at build time
#include <fstream>
#include <iostream>
#include <cstdlib>

/// Print the help message
void help(const std::string &programName)
{
    Log::info(Format::format("Usage: {} [options...]", programName));
    Log::info("Start an Hikou no mizu game server.\n");
    Log::info("Options:");

    constexpr std::size_t labelLength = 28;
    Log::info(Format::column("  -p, --port=[port]",
              "Listen to [port], overrides the port loaded from -c/--conf",
              labelLength));
    Log::info(Format::column("  -c, --conf=[file]",
              "Use the configuration options listed in [file]",
              labelLength));
    Log::info(Format::column("  -e, --export-conf=[file]",
              "Export the loaded configuration to [file] and exit",
              labelLength));
    Log::info(Format::column("  -h, --help",
              "Print a help message and exit", labelLength));
    Log::info(Format::column("  -v, --version",
              "Print the version and exit", labelLength));
}

/// Check if data files can be found
bool hasData()
{
    const std::string &arenaPath = BuildValues::data("cfg/arenas.xml");
    const std::string &charactersPath = BuildValues::data("cfg/characters.xml");

    std::ifstream fileArena, fileCharacters;
    fileArena.open(arenaPath.c_str(), std::ios::in);
    fileCharacters.open(arenaPath.c_str(), std::ios::in);

    return (fileArena.is_open() || fileCharacters.is_open());
}

int main(int argc, char **argv)
{
    //Hikou no mizu server
    const std::string programName = (argc > 0 && argv[0] != nullptr) ?
        Filesystem::getBasename(std::string(argv[0])) : "hikounomizud";

    Log::info(Format::format("{} (Hikou no mizu) {}",
                             programName, BuildValues::VERSION.text()));

    ArgumentParser args;
    args.addOption('p', "port", true);
    args.addOption('c', "conf", true);
    args.addOption('e', "export-conf", true);
    args.addOption('h', "help");
    args.addOption('v', "version");

    ParsedOptions parsed = args.parse(argc, argv);
    if (parsed.hadError)
    {
        help(programName);
        return EXIT_FAILURE;
    }
    else if (parsed.hasOption("-h") || parsed.hasOption("--help"))
    {
        help(programName);
        return EXIT_SUCCESS;
    }
    else if (parsed.hasOption("-v") || parsed.hasOption("--version"))
        return EXIT_SUCCESS; //Version information was already printed

    ServerConfiguration serverConf;

    //Load configuration
    std::string confPath;
    if (parsed.hasOption("-c", confPath) ||
        parsed.hasOption("--conf", confPath))
    {
        if (!serverConf.load(confPath))
        {
            Log::err(Format::format("Could not load configuration file: {}",
                                    confPath));
            return EXIT_FAILURE;
        }

        Log::info(Format::format("Loaded configuration file: {}", confPath));
    }

    //Export configuration
    std::string exportPath;
    if (parsed.hasOption("-e", exportPath) ||
        parsed.hasOption("--export-conf", exportPath))
    {
        if (std::ifstream(exportPath, std::ios::in).is_open())
        {
            std::cout << Format::format("{} already exists. Overwrite? [y/N] ",
                                        exportPath);
            char answer;
            std::cin >> answer;
            if (std::cin.fail() || (answer != 'Y' && answer != 'y'))
                return EXIT_SUCCESS;
        }

        if (!serverConf.save(exportPath))
        {
            Log::err(Format::format("Could not write configuration to: {}",
                                    exportPath));
            return EXIT_FAILURE;
        }

        Log::info(Format::format("Exported configuration at: {}", exportPath));
        return EXIT_SUCCESS;
    }

    //Look for data
    if (!hasData())
    {
        Log::err(Format::format("Game data seems to be missing\n"
                            "Expected data path: {}", BuildValues::DATA_PATH));
        return EXIT_FAILURE;
    }

    //Update server port
    std::uint16_t port;
    if (parsed.hasOption("-p", port) || parsed.hasOption("--port", port))
        serverConf.setPort(port);

    return (Server::run(serverConf) ? EXIT_SUCCESS : EXIT_FAILURE);
}
