#include "projectManager.h"

#include "projectData.h"

using namespace std;
using namespace core::project;
//--------------------------------------------------------------
/* Constructor */
ProjectManager::ProjectManager(dBus::Bus &bus)
  : Node(bus)
{
  // Initialize the bus listener thread
  runBusListener();
}
//--------------------------------------------------------------
/* Default destructor */
ProjectManager::~ProjectManager()
{
  // Stop the bus listener thread if it's running
  stopBusListener();
}
//--------------------------------------------------------------
/* Open a project file and load the requirements */
void ProjectManager::openFile(const std::filesystem::path &filePath)
{
}
//--------------------------------------------------------------
/* Save the current requirements to a project file */
void ProjectManager::saveFile(const std::filesystem::path &filePath)
{
}
//--------------------------------------------------------------
/* Run the bus listener thread */
void ProjectManager::runBusListener()
{
  // Subscribe to the bus for messages related to log entries
  subscribe(dBus::makeID("project.file.operation"));

  /* Start the bus listener thread to process incoming messages related to requirements.
   * The thread will continuously wait for messages and process them until a stop is requested. */
  // clang-format off
  m_busListenerThread = std::jthread([this](const std::stop_token &stopToken)
  {
    while (!stopToken.stop_requested())
    {
      // Wait for a message to be received
      syncWaitForMessage();

      // Process all received messages
      while (getMessageCount() > 0)
      {
        auto message = popNextMessage();
        if (message)
          processMessageBus(message);
      }
    } 
  });
  // clang-format on
}
//--------------------------------------------------------------
/* Stop the bus listener thread */
void ProjectManager::stopBusListener()
{
  // Stop event thread loop
  m_busListenerThread.request_stop();

  // Wake up event thread if waiting
  notifyMessageQueue();

  /* No need to join the thread explicitly as std::jthread will
   * automatically join in its destructor.
   * Subscribe to the bus will be automatically cleaned up in the
   * Node destructor. */
}
//--------------------------------------------------------------
/* Process a message received from the bus */
void ProjectManager::processMessageBus(const Message_ptr &message)
{
  const auto messageType = message->getMessageTypeID();
  switch (messageType)
  {
    case dBus::makeID("project.file.operation"):
    {
      // Process project operation request
      on_projectOperationEvent(message->as<dBus::RequestMessage<api::project::ProjectOperationEvent, void>>());
      break;
    }
  }
}
//--------------------------------------------------------------
/* Process a file operation event */
void ProjectManager::on_projectOperationEvent(const ProjectOperationMessage_ptr &message)
{
  // Sanity check
  if (!message)
    return;

  switch (message->value.operationType)
  {
    using enum api::project::OperationType;
    case Create:
      on_projectCreateFileEvent(message);
      break;
    case Open:
      on_projectOpenFileEvent(message);
      break;
    case Save:
    case SaveAs:
      on_projectSaveFileEvent(message);
      break;
    case Close:
      on_projectCloseFileEvent(message);
      break;
  }
}
//--------------------------------------------------------------
/* Process a file creation event */
void ProjectManager::on_projectCreateFileEvent(const ProjectOperationMessage_ptr &message)
{
  m_projectData = make_shared<ProjectData>();

  message->responsePromise.set_value();
}
//--------------------------------------------------------------
/* Process a file open event */
void ProjectManager::on_projectOpenFileEvent(const ProjectOperationMessage_ptr &message)
{
  // message->responsePromise.set_exception(std::make_exception_ptr(std::runtime_error("Open operation not implemented")));
  try
  {
    openFile(message->value.filePath);
  }
  catch (const std::exception &e)
  {
    message->responsePromise.set_exception(std::make_exception_ptr(e));
  }
  message->responsePromise.set_value();
}
//--------------------------------------------------------------
/* Process a file save event */
void ProjectManager::on_projectSaveFileEvent(const ProjectOperationMessage_ptr &message)
{
  try
  {
    if (message->value.operationType == api::project::OperationType::SaveAs)
    {
      if (message->value.filePath.empty())
        throw std::runtime_error("File path is empty for SaveAs operation");
      saveFile(message->value.filePath);
    }
    else if (message->value.operationType == api::project::OperationType::Save)
    {
      if (m_filePath.empty())
        throw std::runtime_error("File path is empty for Save operation");
      saveFile();
    }
  }
  catch (const std::exception &e)
  {
    message->responsePromise.set_exception(std::make_exception_ptr(e));
  }
  message->responsePromise.set_value();
}
//--------------------------------------------------------------
/* Process a file close event */
void ProjectManager::on_projectCloseFileEvent(const ProjectOperationMessage_ptr &message)
{
  m_projectData = {};
  message->responsePromise.set_value();
}
//--------------------------------------------------------------
