/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main
   SPDX-License-Identifier: GPL-3.0-only
   Authors: Felix Weiglhofer [committer] */
#include "Options.h"

#include <boost/program_options.hpp>

#include <iostream>
#include <unordered_map>

using namespace cbm::algo;

void validate(boost::any& v, const std::vector<std::string>& values, severity_level*, int)
{

  static const std::unordered_map<std::string, severity_level> levels {
    {"trace", severity_level::trace}, {"debug", severity_level::debug},     {"status", severity_level::status},
    {"info", severity_level::info},   {"warning", severity_level::warning}, {"error", severity_level::error},
    {"fatal", severity_level::fatal}};

  namespace po = boost::program_options;

  po::validators::check_first_occurrence(v);

  const std::string& s = po::validators::get_single_string(values);

  auto it = levels.find(s);

  if (it == levels.end()) { throw po::validation_error(po::validation_error::invalid_option_value); }

  v = it->second;
}

Options::Options(int argc, char** argv)
{
  namespace po = boost::program_options;

  po::options_description required("Required options");
  // clang-format off
  required.add_options()
    ("param-dir,p", po::value(&fParamsDir)->value_name("<folder>")->required(),
      "read program options from this folder")
    ("input-locator,i", po::value(&fInputLocator)->value_name("<locator>")->required(),
      "URI specifying input timeslice source")
  ;
  // clang-format on

  po::options_description generic("Other options");
  // clang-format off
  generic.add_options()
    ("device,d", po::value(&fDevice)->default_value("cpu")->value_name("<device>"),
      "select device (cpu, cuda0, cuda1, hip0, ...)")
    ("log-level,l", po::value(&fLogLevel)->default_value(info)->value_name("<level>"),
      "set log level (debug, info, warning, error, fatal)")
    ("monitor,m", po::value(&fMonitorUri)->value_name("<uri>")->implicit_value("file:cout"),
      "URI specifying monitor output (e.g. file:/tmp/monitor.txt, influx1:login:8086:cbmreco_status). Prints to cout when no argument is given. Monitor is disabled when flag is not set.")
    ("num-ts,n", po::value(&fNumTimeslices)->default_value(-1)->value_name("<num>"),
      "Stop after <num> timeslices (-1 = all)")
    ("skip-ts,s", po::value(&fSkipTimeslices)->default_value(0)->value_name("<num>"),
      "Skip first <num> timeslices")
    ("times,t", po::bool_switch(&fCollectKernelTimes)->default_value(false),
      "print kernel times")
    ("help,h",
      "produce help message")
  ;
  // clang-format on

  po::options_description cmdline_options;
  cmdline_options.add(required).add(generic);

  po::variables_map vm;
  po::command_line_parser parser {argc, argv};
  parser.options(cmdline_options);
  po::store(parser.run(), vm);

  if (vm.count("help")) {
    std::cerr << cmdline_options << std::endl;
    std::exit(0);
  }

  try {
    po::notify(vm);
  }
  catch (const po::required_option& e) {
    std::cerr << "Error: " << e.what() << std::endl;
    std::cerr << cmdline_options << std::endl;
    std::exit(1);
  }
}