#include <iostream>
#include <Kokkos_Core.hpp>
#include <RevisionInfo.hpp>
#include <rang.hpp>
#include <FPEManager.hpp>
#include <SignalManager.hpp>
#include <ConsoleManager.hpp>

#include <RawKokkosAcousticSolver.hpp>
#include <MeshLessAcousticSolver.hpp>
#include <AcousticSolver.hpp>
#include <AcousticSolverTest.hpp>

#include <TinyVector.hpp>

#include <CLI/CLI.hpp>
#include <cassert>
#include <limits>
#include <map>

int main(int argc, char *argv[])
{
  long unsigned number = 10;

  {
    CLI::App app{"Pastis help"};

    app.add_option("number,-n,--number", number, "Number of cells");//->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([](){std::cout << rang::style::reset;});
    try {
      app.parse(argc, argv);
    } catch (const CLI::ParseError &e) {
      return app.exit(e);
    }

    ConsoleManager::init(colorize);
    FPEManager::init(not disable_fpe);
    SignalManager::setPauseForDebug(pause_on_error);
    SignalManager::init(not disable_signals);
  }
  
  std::cout << "Code version: "
	    << rang::style::bold << RevisionInfo::version() << rang::style::reset << '\n';

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

  if (RevisionInfo::gitIsClean()) {
    std::cout << rang::fgB::green << "clean" << rang::fg::reset;
  } else {
    std::cout << rang::fgB::red << "dirty" << rang::fg::reset; 
  }
  std::cout << ")\n";
  std::cout << "-------------------------------------------------------\n";

  Kokkos::initialize(argc, argv);
  Kokkos::DefaultExecutionSpace::print_configuration(std::cout);

  std::map<std::string, double> method_cost_map;

  { // Basic function based acoustic solver
    Kokkos::Timer timer;
    timer.reset();
    RawKokkos::AcousticSolver(number);
    method_cost_map["RawKokkos"] = timer.seconds();
  }

  { // class for acoustic solver (mesh less)
    Kokkos::Timer timer;
    timer.reset();
    MeshLessAcousticSolver acoustic_solver(number);
    method_cost_map["MeshLessAcousticSolver"] = timer.seconds();
  }

  { // class for acoustic solver test
    Kokkos::Timer timer;
    timer.reset();
    AcousticSolverTest acoustic_solver(number);
    method_cost_map["AcousticSolverTest"] = timer.seconds();
  }

  { // class for acoustic solver
    Kokkos::Timer timer;
    timer.reset();
    AcousticSolver acoustic_solver(number);
    method_cost_map["AcousticSolver"] = timer.seconds();
  }

  Kokkos::View<TinyVector<2,double>*[2]> test("test", 10);
  constexpr size_t N = 3;
  std::cout << "sizeof(TinyVector<" << N << ",double>)=" << sizeof(TinyVector<N,double>) << std::endl;
  std::cout << "sizeof(double)=" << sizeof(double) << std::endl;


  Kokkos::finalize();

  std::cout << "----------------------\n";

  std::string::size_type size=0;
  for (const auto& method_cost : method_cost_map) {
    size = std::max(size, method_cost.first.size());
  }

  for (const auto& method_cost : method_cost_map) {
    std::cout << "* [" << std::setw(size) << std::left
	      << method_cost.first
	      << " ] Execution time: "
	      << rang::style::bold
	      << method_cost.second
	      << rang::style::reset << '\n';
  }

  TinyVector<2> x=1;
  TinyVector<2> y=2;

  std::cout << x << "-" << y << "=" << x-y << std::endl;
  std::cout << x << "+" << y << "=" << x+y << std::endl;
  std::cout << "3*" << x << '=' << 3*x << std::endl;
  x[1]=3; y[1]=0.1;
  std::cout << "< " << x << " | " << y << " > = " << (x,y) << std::endl;
  return 0;
}