#pragma once

#include "quokka_gfx/core/events/eventBus.h"

#include <iostream>       // For std::cout
#include <syncstream>     // For std::osyncstream (C++20) to ensure thread-safe output
#include <thread>         // For std::thread and std::mutex
#include <utility>        // For std::forward

//--------------------------------------------------------------
class Logger : public quokka_gfx::Listener
{
  public:
  Logger();                                           // Default constructor
  virtual ~Logger()                    = default;     // Default destructor
  Logger(const Logger &obj)            = delete;      // Copy constructor
  Logger(Logger &&obj)                 = delete;      // Move constructor
  Logger &operator=(const Logger &obj) = delete;      // Copy assignment operator
  Logger &operator=(Logger &&obj)      = delete;      // Move assignment operator

  template<typename... Args>
  void log(Args &&...args);     // Variadic template function for logging

  protected:
  std::jthread m_loggerThread;     // Waits for events thread

  private:
  void startThread();     // Start the logger thread
};
//--------------------------------------------------------------
/* Default constructor */
inline Logger::Logger()
{
  subscribe(quokka_gfx::toID("log"));
  startThread();
}
//--------------------------------------------------------------
/* Variadic template function for logging */
template<typename... Args>
void Logger::log(Args &&...args)
{
  std::osyncstream out(std::cout);
  (out << ... << args) << std::endl;
}
//--------------------------------------------------------------
/* Start the logger thread */
inline void Logger::startThread()
{
  m_loggerThread = std::jthread([&](const std::stop_token &st)
                                {
    while (!st.stop_requested())
    {
      while (getEventCount() > 0)
      {
        const auto event = popEvent();
        if (event)
        {
          if (event->getType() == quokka_gfx::toID("log"))
            log(event->getType());
        }
      }

      // Sleep for a short duration to prevent busy waiting
      std::this_thread::sleep_for(std::chrono::milliseconds(100));     
    } });
}
//--------------------------------------------------------------
