#pragma once
typedef struct {
  double tdbscan_eps_time;
  double tdbscan_k;
  int    tdbscan_min_pts;

  double douglas_eps;
  double douglas_base;
  double douglas_k;

  std::string osrm_path;
} Config;

Config config_global;

void inline fatal(std::string message) {
  fprintf(stderr, "Fatal: %s\n", message.c_str());
  exit(1);
}

#define TOML_TABLE(var, name)             \
auto var = result.table->getTable(name);  \
if (!var) fatal("Missing [" name "]");    \

#define TOML_GET_DOUBLE(var, name, out)        \
  auto [ok_##out, out] = var->getDouble(name); \
  if (!ok_##out) fatal("Missing " name)        \

#define TOML_GET_INT(var, name, out)           \
  auto [ok_##out, out] = var->getInt(name);    \
  if (!ok_##out) fatal("Missing " name)        \

#define TOML_GET_STRING(var, name, out)        \
  auto [ok_##out, out] = var->getString(name); \
  if (!ok_##out) fatal("Missing " name)        \

#include "../tomlcpp/tomlcpp.hpp"


#if defined(_WIN32)

#define NOMINMAX
#include <windows.h>

#elif defined(__linux__)

#include <unistd.h>

#elif defined(__APPLE__)

#include <mach-o/dyld.h>

#endif

std::filesystem::path executable_dir() {
#if defined(_WIN32)
    char buffer[MAX_PATH];
    GetModuleFileNameA(NULL, buffer, MAX_PATH);
    return std::filesystem::path(buffer).parent_path();

#elif defined(__linux__)
    char buffer[1024];
    ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer)-1);
    buffer[len] = '\0';
    return std::filesystem::path(buffer).parent_path();

#elif defined(__APPLE__)
    char buffer[1024];
    uint32_t size = sizeof(buffer);
    _NSGetExecutablePath(buffer, &size);
    return std::filesystem::path(buffer).parent_path();
#endif
}

void Config_Init(Config *config) {
  auto config_path = executable_dir() / "config.toml";
  auto result = toml:: parseFile(config_path.u8string());
  if (!result.table) fatal("Cannot parse file: " + result.errmsg);

  TOML_TABLE(tdbscan, "T-DBSCAN");
  TOML_GET_DOUBLE(tdbscan, "eps_time", tdbscan_eps_time);
  TOML_GET_DOUBLE(tdbscan, "k"       , tdbscan_k);
  TOML_GET_INT   (tdbscan, "min_pts" , tdbscan_min_pts);

  TOML_TABLE(douglas, "Douglas");
  TOML_GET_DOUBLE(douglas, "eps" , douglas_eps);
  TOML_GET_DOUBLE(douglas, "base", douglas_base);
  TOML_GET_DOUBLE(douglas, "k"   , douglas_k);

  TOML_TABLE(osrm, "OSRM");
  TOML_GET_STRING(osrm, "path", osrm_path);

  config->tdbscan_eps_time = tdbscan_eps_time;
  config->tdbscan_k        = tdbscan_k;
  config->tdbscan_min_pts  = tdbscan_min_pts;

  config->douglas_eps  = douglas_eps;
  config->douglas_base = douglas_base;
  config->douglas_k    = douglas_k;

  config->osrm_path = osrm_path;
}

void Config_Print(Config config) {
  fprintf(stderr, "Config { tdbscan_eps_time = %f, tdbscan_k = %f, tdbscan_min_pts = %d, douglas_eps = %f, douglas_base = %f, douglas_k = %f, osrm_path = %s }\n", config.tdbscan_eps_time, config.tdbscan_k, config.tdbscan_min_pts, config.douglas_eps, config.douglas_base, config.douglas_k, config.osrm_path.c_str());
}
