#include "cmd_line_options.h"

#include "exit_codes.h"

#include <maps/libs/common/include/exception.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/libs/stringutils/include/join.h>
#include <maps/libs/stringutils/include/to_string.h>

#include <boost/program_options.hpp>

#include <iostream>
#include <stdexcept>
#include <thread>

namespace maps {
namespace wiki {
namespace ymapsdf_splitter {
namespace {

const std::string PROGRAM_DESCRIPTION =
R"(Writes into a ymapsdf schema data from a selected region. Where the region is
defined by a number of ISO codes.

Usage:
  ymapsdf-splitter --conn <connection string> --in-schema <schema>
     --out-schema <schema> --region-name <region name> --iso-codes <ISO code>...
     [--threads-number <number>] [--log-level <log level>] [--dry-run]

  ymapsdf-splitter --help

Options)";

const std::vector<log8::Level> LOG_LEVELS = {
    log8::Level::FATAL, log8::Level::ERROR, log8::Level::WARNING,
    log8::Level::INFO, log8::Level::DEBUG
};

const std::string LOG_LEVELS_STR =
    "(" + stringutils::join(stringutils::toStrings(LOG_LEVELS), ", ") + ")";


size_t defaultThreadsNumber()
{
    auto threads = std::thread::hardware_concurrency();
    return std::max<size_t>(1, (2 * threads) / 3);
}

} // namespace

namespace po = boost::program_options;

CmdLineOptions parseCommandLine(int argc, char* argv[])
{
    CmdLineOptions result;

    po::options_description desc(PROGRAM_DESCRIPTION);
    desc.add_options()
        ("conn,c",
         po::value<std::string>(&result.conn)->required(),
         "A connection string.")
        ("dry-run,d",
         "Do not execute queries.")
        ("help,h",
         "Show this help message.")
        ("in-schema",
         po::value<std::string>(&result.inSchema)->required(),
         "A schema to be split by regions.")
        ("iso-codes,i",
         po::value<std::vector<std::string>>(&result.region.isoCodes)->multitoken()->required(),
         "ISO codes to split the schema by.")
        ("log-level,l",
         po::value<log8::Level>(&result.logLevel)->default_value(log8::Level::INFO),
         ("A log level " + LOG_LEVELS_STR + ".").c_str())
        ("out-schema",
         po::value<std::string>(&result.outSchema)->required(),
         "A schema, the result to be written to.")
        ("region-name,r",
         po::value<std::string>(&result.region.name)->required(),
         "The name of the region.")
        ("threads-number,t",
         po::value<size_t>(&result.threadsNumber)->default_value(defaultThreadsNumber()),
         "A number of threads to run the split process by.");

    po::variables_map vm;
    try {
        po::store(parse_command_line(argc, argv, desc), vm);
    } catch (const std::runtime_error& e) { // wrong log level
        FATAL() << e.what();
        std::cout << desc;
        exit(EXITCODE_WRONG_CMD_LINE_OPTION_ERROR);
    }

    try {
        po::notify(vm);
    } catch (const po::required_option& e) {
        FATAL() << e.what();
        std::cout << desc;
        exit(EXITCODE_WRONG_CMD_LINE_OPTION_ERROR);
    }

    if (vm.count("help")) {
        std::cout << desc << "\n";
        exit(EXITCODE_OK);
    }

    result.dryRun = vm.count("dry-run");

    return result;
 }


} // namespace ymapsdf_splitter
} // namespace wiki
} // namespace maps
