#include "logging.hpp"

#ifdef NETGEN_USE_SPDLOG

#include <spdlog/sinks/ansicolor_sink.h>
#include <spdlog/sinks/basic_file_sink.h>

#endif // NETGEN_USE_SPDLOG

namespace ngcore
{
#ifdef NETGEN_USE_SPDLOG
  namespace detail
  {
    std::vector<std::shared_ptr<spdlog::sinks::sink>>& GetDefaultSinks()
    {
      static std::vector<std::shared_ptr<spdlog::sinks::sink>> sinks =
        { std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>() };
      return sinks;
    }
    std::shared_ptr<spdlog::logger> CreateDefaultLogger(const std::string& name)
    {
      auto& default_sinks = GetDefaultSinks();
      auto logger = std::make_shared<spdlog::logger>(name, default_sinks.begin(), default_sinks.end());
      spdlog::details::registry::instance().register_and_init(logger);
      return logger;
    }
  } // namespace detail

  std::shared_ptr<spdlog::logger> GetLogger(const std::string& name)
  {
    auto logger = spdlog::get(name);
    if(!logger)
      logger = detail::CreateDefaultLogger(name);
    return logger;
  }

  void SetLoggingLevel(spdlog::level::level_enum level, const std::string& name)
  {
    if(!name.empty())
      spdlog::get(name)->set_level(level);
    else
      spdlog::set_level(level);
  }

  void AddFileSink(const std::string& filename, spdlog::level::level_enum level, const std::string& logger)
  {
    auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename);
    sink->set_level(level);
    if(!logger.empty())
      GetLogger(logger)->sinks().push_back(sink);
    else
      {
        detail::GetDefaultSinks().push_back(sink);
        spdlog::details::registry::instance().apply_all([sink](auto logger) { logger->sinks().push_back(sink); });
      }
  }

  void AddConsoleSink(spdlog::level::level_enum level, const std::string& logger)
  {
    auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
    sink->set_level(level);
    if(!logger.empty())
      GetLogger(logger)->sinks().push_back(sink);
    else
      {
        detail::GetDefaultSinks().push_back(sink);
        spdlog::details::registry::instance().apply_all([sink](auto logger) { logger->sinks().push_back(sink); });
      }
  }

  void ClearLoggingSinks(const std::string& logger)
  {
    if(!logger.empty())
      GetLogger(logger)->sinks().clear();
    else
      {
        detail::GetDefaultSinks().clear();
        spdlog::details::registry::instance().apply_all([](auto logger) { logger->sinks().clear(); });
      }
  }

  void FlushOnLoggingLevel(spdlog::level::level_enum level, const std::string& logger)
  {
    if(!logger.empty())
      GetLogger(logger)->flush_on(level);
    else
      spdlog::flush_on(level);
  }

#else // NETGEN_USE_SPDLOG

  // Dummy functions if no spdlog is available

  std::shared_ptr<spdlog::logger> GetLogger(const std::string& /*unused*/)
  {
    return std::make_shared<spdlog::logger>();
  }
  void SetLoggingLevel(spdlog::level::level_enum /*unused*/, const std::string& /*unused*/) {}
  void AddFileSink(const std::string& /*unused*/, spdlog::level::level_enum /*unused*/,
                   const std::string& /*unused*/)
  {}
  void AddConsoleSink(spdlog::level::level_enum /*unused*/, const std::string& /*unused*/) {}
  void ClearLoggingSinks(const std::string& /*unused*/) {}
  void FlushOnLoggingLevel(spdlog::level::level_enum /*unused*/, const std::string& /*unused*/) {}

#endif // NETGEN_USE_SPDLOG
} //namespace ngcore