#include <boost/test/unit_test.hpp>

#include "../../lua/optionsfunction.h"
#include "../../lua/luathreadgroup.h"

#include <optional>

BOOST_AUTO_TEST_SUITE(optionsfunction, *boost::unit_test::label("lua"))

BOOST_AUTO_TEST_CASE(no_function) {
  LuaThreadGroup lua(1);
  lua.LoadText("-- nothing here\n");
  std::map<std::string, Options> options =
      OptionsFunction::GetOptions(lua.GetThread(0).State(), Options());
  BOOST_CHECK(options.empty());
}

BOOST_AUTO_TEST_CASE(empty) {
  LuaThreadGroup lua(1);
  lua.LoadText(
      "function options()\n"
      "  return { }\n"
      "end\n");
  std::map<std::string, Options> options =
      OptionsFunction::GetOptions(lua.GetThread(0).State(), Options());
  BOOST_CHECK(options.empty());
}

BOOST_AUTO_TEST_CASE(single_default_run) {
  LuaThreadGroup lua(1);
  lua.LoadText(
      "function options()\n"
      "  mainopts = { }\n"
      "  runs = { }\n"
      "  runs[\"main\"] = mainopts\n"
      "  return runs\n"
      "end\n");
  std::map<std::string, Options> options =
      OptionsFunction::GetOptions(lua.GetThread(0).State(), Options());
  BOOST_REQUIRE_EQUAL(options.size(), 1);
  BOOST_CHECK_EQUAL(options.begin()->first, "main");
  BOOST_CHECK(options.begin()->second == Options());
}

BOOST_AUTO_TEST_CASE(single_dual_run) {
  LuaThreadGroup lua(1);
  lua.LoadText(
      "function options()\n"
      "  first_opts = { }\n"
      "  second_opts = { }\n"
      "  second_opts.bands = { 11, 13 }\n"
      "  second_opts.baselines = \"auto\"\n"
      "  second_opts[\"chunk-size\"] = 3\n"
      "  second_opts[\"column-name\"] = \"MY_DATA\"\n"
      "  second_opts[\"combine-spws\"] = true\n"
      "  second_opts[\"execute-file\"] = \"somefile.lua\"\n"
      "  second_opts[\"execute-function\"] = \"my_execute\"\n"
      "  second_opts.fields = { 3, 4, 5 }\n"
      "  second_opts.files = { \"first_file.txt\", \"second_file.txt\" }\n"
      "  second_opts[\"min‑aoflagger-version\"] = \"2.99\"\n"
      "  second_opts.quiet = true\n"
      "  second_opts[\"read-mode\"] = \"direct\"\n"
      "  second_opts[\"read-uvws\"] = false\n"
      "  second_opts[\"script-version\"] = \"1.0\"\n"
      "  second_opts[\"start-timestep\"] = 1982\n"
      "  second_opts[\"end-timestep\"] = 2020\n"
      "  second_opts.threads = 24\n"
      "  second_opts.verbose = true\n"
      "  runs = { }\n"
      "  runs[\"first\"] = first_opts\n"
      "  runs[\"second\"] = second_opts\n"
      "  return runs\n"
      "end\n");
  std::map<std::string, Options> options =
      OptionsFunction::GetOptions(lua.GetThread(0).State(), Options());
  BOOST_REQUIRE_EQUAL(options.size(), 2);

  Options secOptions;
  secOptions.bands = std::set<size_t>{11, 13};
  secOptions.baselineSelection = BaselineSelection::AutoCorrelations;
  secOptions.chunkSize = 3;
  secOptions.dataColumn = "MY_DATA";
  secOptions.combineSPWs.emplace(true);
  secOptions.executeFilename = "somefile.lua";
  secOptions.executeFunctionName = "my_execute";
  secOptions.fields = std::set<size_t>{3, 4, 5};
  secOptions.filenames =
      std::vector<std::string>{"first_file.txt", "second_file.txt"};
  secOptions.logVerbosity =
      Logger::QuietVerbosity;  // be aware that verbose = true is overruled by
                               // quiet = true
  secOptions.readMode = BaselineIOMode::DirectReadMode;
  secOptions.readUVW = false;
  secOptions.scriptVersion = "1.0";
  secOptions.startTimestep = 1982;
  secOptions.endTimestep = 2020;
  secOptions.threadCount = 24;

  BOOST_CHECK_EQUAL(options.begin()->first, "first");
  BOOST_CHECK(options.begin()->second == Options());
  BOOST_CHECK(options.begin()->second != secOptions);

  BOOST_CHECK_EQUAL(options.rbegin()->first, "second");
  BOOST_CHECK_EQUAL(options.rbegin()->second.chunkSize, secOptions.chunkSize);
  BOOST_CHECK_EQUAL(options.rbegin()->second.dataColumn, secOptions.dataColumn);
  BOOST_CHECK(options.rbegin()->second.combineSPWs == secOptions.combineSPWs);
  BOOST_CHECK(options.rbegin()->second.readMode == secOptions.readMode);
  BOOST_CHECK(options.rbegin()->second.readUVW == secOptions.readUVW);
  BOOST_CHECK_EQUAL(options.rbegin()->second.scriptVersion,
                    secOptions.scriptVersion);
  BOOST_CHECK(options.rbegin()->second.startTimestep ==
              secOptions.startTimestep);
  BOOST_CHECK(options.rbegin()->second.endTimestep == secOptions.endTimestep);
  BOOST_CHECK(options.rbegin()->second == secOptions);
  BOOST_CHECK(options.rbegin()->second != Options());
}

BOOST_AUTO_TEST_CASE(version_check) {
  LuaThreadGroup lua(1);
  lua.LoadText(
      "function options()\n"
      "  mainopts = { }\n"
      "  mainopts[\"min‑aoflagger-version\"] = \"999.999\"\n"
      "  runs = { }\n"
      "  runs[\"main\"] = mainopts\n"
      "  return runs\n"
      "end\n");
  BOOST_CHECK_THROW(
      OptionsFunction::GetOptions(lua.GetThread(0).State(), Options()),
      std::exception);
}

BOOST_AUTO_TEST_CASE(cmdline_override) {
  LuaThreadGroup lua(1);
  lua.LoadText(
      "function options()\n"
      "  opt_a = { }\n"
      "  opt_a[\"column-name\"] = \"option\"\n"
      "  opt_b = { }\n"
      "  opt_b[\"baselines\"] = \"auto\"\n"
      "  opt_c = { }\n"
      "  opt_c[\"threads\"] = 16533\n"
      "  opt_d = { }\n"
      "  opt_d[\"verbose\"] = false\n"
      "  return { opt_a, opt_b, opt_c, opt_d }\n"
      "end\n");
  Options cmdLineOptions;
  cmdLineOptions.dataColumn = "override_column";
  cmdLineOptions.baselineSelection = BaselineSelection::All;
  cmdLineOptions.threadCount = 2;
  cmdLineOptions.logVerbosity = Logger::VerboseVerbosity;

  std::map<std::string, Options> options =
      OptionsFunction::GetOptions(lua.GetThread(0).State(), cmdLineOptions);

  BOOST_REQUIRE_EQUAL(options.size(), 4);
  for (const auto& x : options) {
    BOOST_CHECK(x.second == cmdLineOptions);
  }
}

BOOST_AUTO_TEST_SUITE_END()
