#include <cstdint>
#include <filesystem>
#include <iostream>
#include <string>
#include <thread>

#include "CLI11.hpp"

#include "CopyManagerOptions.h"
#include "CopyManager.h"
#include "Logger.h"

namespace sfs = std::filesystem;

const std::string VERSION("v0.1.1");

const std::string BOLD{"\033[1m"};
const std::string ENDC{"\033[0m"};
const std::string RED{"\033[31m"};
const std::string YELLOW{"\033[33m"};
const std::string PURPLE{"\033[35m"};
const std::string LBLUE{"\033[1;34m"};


int main(int argc, char** argv)
{
    std::cout << "copytool -- " << VERSION << "\n" << std::endl;
    CLI::App app{"copytool"};

    // Required fundamental options
    std::string srcPath;
    std::string destPath;

    // Add required positional arguments
    app.add_option("source_path", srcPath,
                   "source path; copy files FROM this path")->required()
            ->check(CLI::ExistingDirectory);
    app.add_option("dest_path", destPath,
                   "destination path; copy files TO this path")->required()
            ->check(CLI::ExistingDirectory);

    // Logging related options
    bool disableLog = DBS::CopyManagerOptions::DEFAULT_DISABLE_LOG;
    bool logToStderr = DBS::CopyManagerOptions::DEFAULT_LOG_TO_STDERR;
    std::string logFilePath{DBS::CopyManagerOptions::DEFAULT_LOG_FILE_PATH};
    int logLevel = DBS::CopyManagerOptions::DEFAULT_LOG_LEVEL;

    // Add logging options
    app.add_flag("--disable-log", disableLog, "disable (do not output) log");
    app.add_flag("--log-to-stderr", logToStderr, "write log messages to stderr");
    app.add_option("-l,--log-file-path", logFilePath, "log file path");
    app.add_option("--log-level", logLevel,
                   "default log message level (error=0, warn=1, info=2, debug=3, trace=4)")
                   ->check(CLI::Range(0, 4));

    // Other options
    uint32_t flowControlThreshold = DBS::CopyManagerOptions::DEFAULT_FC_THRESHOLD;
    uint32_t numCopyThreads = DBS::CopyManagerOptions::DEFAULT_NUM_COPY_THREADS;
    bool copyTopLevel = DBS::CopyManagerOptions::DEFAULT_COPY_TOPLEVEL;

    app.add_option("--flow-control-threshold", flowControlThreshold,
                   "flow control threshold")
                   ->check(CLI::Range(100,65535));
    app.add_option("-n,--num-copy-threads", numCopyThreads,
                   "number of copy threads to spawn (default: 4)")
                   ->check(CLI::Range(1, 512));
    app.add_flag("--copy-top-level", copyTopLevel,
                 "copy top level source directory to destination rather then "
                 "the contents of the top level source directory (which is "
                 "the default behavior)");

    // Parse options
    CLI11_PARSE(app, argc, argv);

    // Build options object
    DBS::CopyManagerOptions options(srcPath,
                                    destPath,
                                    disableLog,
                                    logToStderr,
                                    logFilePath,
                                    logLevel,
                                    flowControlThreshold,
                                    numCopyThreads,
                                    copyTopLevel);

    // Prime the logger
    DBS::Logger::getInstance(DBS::off, &options);
    LOG(DBS::info) << "copytool -- DBS";

    // Display a warning if the specified number of copy threads exceeds the
    // number of cores in the system
    uint32_t numCores = std::thread::hardware_concurrency();
    if (numCopyThreads > numCores)
    {
        std::cout << BOLD << YELLOW << "WARNING: " << ENDC
                  << "The number of copy threads specified ("
                  << numCopyThreads << ") exceeds the number of cores in the "
                  << "system (" << numCores << "); proceed with caution.\n"
                  << std::endl;
    }

    // Create the copy manager and run it
    DBS::CopyManager manager(options);
    int rc = manager.run();

    return rc;
}
