#include <utils/PluginsLoader.hpp>

#include <dlfcn.h>

#include <rang.hpp>

#include <filesystem>
#include <iostream>
#include <sstream>
#include <unistd.h>
#include <vector>

std::vector<std::string>
split(const std::string& full_string)
{
  std::vector<std::string> split_string;

  std::stringstream is(full_string);

  std::string segment;
  while (std::getline(is, segment, ';')) {
    if (segment.size() > 0) {
      split_string.push_back(segment);
    }
  }
  return split_string;
}

void
PluginsLoader::_open(const std::string& plugin)
{
  auto handle = dlopen(plugin.c_str(), RTLD_NOW);
  if (handle != nullptr) {
    m_dl_handler_stack.push(handle);
    std::cout << " * \"" << rang::fgB::green << plugin << rang::fg::reset << "\"\n";
  } else {
    std::cout << "   " << rang::fgB::red << "cannot load " << rang::fg::reset << '\"' << rang::fgB::yellow << plugin
              << rang::fg::reset << "\"\n";
  }
}

PluginsLoader::PluginsLoader()
{
  std::vector<std::string> plugin_vector;

  {
    char* env = getenv("PUGS_PLUGIN");
    if (env != nullptr) {
      std::string plugins = env;
      plugin_vector       = split(plugins);
    }
  }

  {
    char* env = getenv("PUGS_PLUGIN_DIR");
    if (env != nullptr) {
      std::string paths                    = env;
      std::vector<std::string> path_vector = split(paths);
      for (auto&& path : path_vector) {
        if (access(path.c_str(), R_OK) == -1) {
          std::cout << ' ' << rang::fgB::red << 'X' << rang::fg::reset << " cannot access plugin dir \""
                    << rang::fgB::yellow << path << rang::fg::reset << "\"\n";
        } else {
          for (auto&& entry :
               std::filesystem::directory_iterator(path,
                                                   (std::filesystem::directory_options::follow_directory_symlink |
                                                    std::filesystem::directory_options::skip_permission_denied))) {
            if (entry.path().extension() == ".so") {
              plugin_vector.push_back(entry.path().string());
            }
          }
        }
      }
    }
  }

  // keep unique entries
  std::sort(plugin_vector.begin(), plugin_vector.end());
  plugin_vector.resize(std::distance(plugin_vector.begin(), std::unique(plugin_vector.begin(), plugin_vector.end())));

  if (plugin_vector.size() > 0) {
    std::cout << rang::style::bold << "Loading plugins" << rang::style::reset << '\n';
    for (auto&& plugin : plugin_vector) {
      this->_open(plugin);
    }
    std::cout << "-------------------------------------------------------\n";
  }
}

PluginsLoader::~PluginsLoader()
{
  while (not m_dl_handler_stack.empty()) {
    dlclose(m_dl_handler_stack.top());
    m_dl_handler_stack.pop();
  }
}