#include "Logger.hpp"
#include <spdlog/sinks/dist_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/sinks/wincolor_sink.h>
#include "Vocabulary.hpp"

namespace
{
constexpr auto LOGGER_NAME = "logger";
constexpr auto LOGGER_PATTERN = "[%C-%m-%d %T.%e] [%^%L%$] %v";

spdlog::sink_ptr& getLoggerSink()
{
  static spdlog::sink_ptr Sink;
  return Sink;
}

Ptr<spdlog::logger>& getStaticLogger()
{
  static Ptr<spdlog::logger> Logger;
  return Logger;
}

spdlog::sink_ptr createConsoleSink()
{
#ifdef _WIN32
  return std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
#else
  return std::make_shared<spdlog::sinks::ansicolor_sink>(std::make_shared<
    spdlog::sinks::stdout_sink<spdlog::details::null_mutex>>());
#endif
}

spdlog::sink_ptr createAndConfigureConsoleSink()
{
  auto sink = createConsoleSink();
  sink->set_pattern(LOGGER_PATTERN);
  return sink;
}

spdlog::sink_ptr createFileAndConsoleSink(std::string const& filename)
{
  auto fileSink = std::make_shared<
    spdlog::sinks::rotating_file_sink<spdlog::details::null_mutex>>(
    filename, 1024 * 1024, 3);

  auto distSink = std::make_shared<spdlog::sinks::dist_sink<std::mutex>>();
  distSink->add_sink(fileSink);
  distSink->add_sink(createAndConfigureConsoleSink());
  return distSink;
}

std::shared_ptr<spdlog::logger> createLoggerWith(spdlog::sink_ptr sink)
{
  return std::make_shared<spdlog::logger>(LOGGER_NAME, sink);
}
}  // namespace

void Logger::setupFileLogging(std::string const& prefix)
{
  getLoggerSink() = createFileAndConsoleSink(prefix);
}

std::shared_ptr<spdlog::logger> Logger::get()
{
  auto& logger = getStaticLogger();
  if (!logger)
  {
    auto sink = getLoggerSink();
    if (!sink)
    {
      logger = createLoggerWith(createAndConfigureConsoleSink());
      logger->warn("No logging sink installed, falling back to console.");
    }
    else
    {
      // Default case, install logger from pre-created sink
      logger = createLoggerWith(sink);
    }
  }
  return logger;
}
