#include <PugsOStream.hpp>
#include <PugsUtils.hpp>

#include <Kokkos_Core.hpp>

#include <BuildInfo.hpp>
#include <RevisionInfo.hpp>

#include <Messenger.hpp>

#include <rang.hpp>

#include <ConsoleManager.hpp>
#include <FPEManager.hpp>
#include <SignalManager.hpp>

#include <CLI/CLI.hpp>

std::string
initialize(int& argc, char* argv[])
{
  parallel::Messenger::create(argc, argv);

  long unsigned number = 10;
  std::string filename;

  pout() << "Pugs version: " << rang::style::bold << RevisionInfo::version()
         << rang::style::reset << '\n';

  pout() << "-------------------- " << rang::fg::green << "git info"
         << rang::fg::reset << " -------------------------" << '\n';
  pout() << "tag:  " << rang::style::bold << RevisionInfo::gitTag()
         << rang::style::reset << '\n';
  pout() << "HEAD: " << rang::style::bold << RevisionInfo::gitHead()
         << rang::style::reset << '\n';
  pout() << "hash: " << rang::style::bold << RevisionInfo::gitHash()
         << rang::style::reset << "  (";

  if (RevisionInfo::gitIsClean()) {
    pout() << rang::fgB::green << "clean" << rang::fg::reset;
  } else {
    pout() << rang::fgB::red << "dirty" << rang::fg::reset;
  }
  pout() << ")\n";
  pout() << "-------------------- " << rang::fg::green << "build info"
         << rang::fg::reset << " -----------------------" << '\n';
  pout() << "type:     " << rang::style::bold << BuildInfo::type()
         << rang::style::reset << '\n';
  pout() << "compiler: " << rang::style::bold << BuildInfo::compiler()
         << rang::style::reset << '\n';
  pout() << "kokkos:   " << rang::style::bold << BuildInfo::kokkosDevices()
         << rang::style::reset << '\n';
  pout() << "mpi:      " << rang::style::bold << BuildInfo::mpiLibrary()
         << rang::style::reset << '\n';
  pout() << "-------------------------------------------------------\n";
  {
    CLI::App app{"Pugs help"};

    app.add_option("-n,--number", number, "Number of cells");   //->required();

    app.add_option("filename,-f,--filename", filename,
                   "gmsh file");   //->required();

    int threads = -1;
    app.add_option("--threads", threads, "Number of Kokkos threads")
      ->check(CLI::Range(1, std::numeric_limits<decltype(threads)>::max()));

    std::string colorize = "auto";
    app.add_set("--colorize", colorize, {"auto", "yes", "no"},
                "Colorize console output", true);

    bool disable_fpe = false;
    app.add_flag("--no-fpe", disable_fpe,
                 "Do not trap floating point exceptions");
    bool disable_signals = false;
    app.add_flag("--no-signal", disable_signals, "Do not catches signals");

    std::string pause_on_error = "auto";
    app.add_set("--pause-on-error", pause_on_error, {"auto", "yes", "no"},
                "Pause for debugging on unexpected error", true);

    std::atexit([]() { pout() << rang::style::reset; });
    try {
      app.parse(argc, argv);
    } catch (const CLI::ParseError& e) {
      parallel::Messenger::destroy();
      std::exit(app.exit(e, pout(), perr()));
    }

    ConsoleManager::init(colorize);
    FPEManager::init(not disable_fpe);
    SignalManager::setPauseForDebug(pause_on_error);
    SignalManager::init(not disable_signals);
  }

  Kokkos::initialize(argc, argv);
  pout() << "-------------------- " << rang::fg::green << "exec info"
         << rang::fg::reset << " ------------------------" << '\n';

  pout() << rang::style::bold;
  Kokkos::DefaultExecutionSpace::print_configuration(pout());
  pout() << rang::style::reset;
  pout() << "-------------------------------------------------------\n";

  return filename;
}

void
finalize()
{
  Kokkos::finalize();
  parallel::Messenger::destroy();
}