#pragma once

#include "exception.hpp"
#include "modules/base.hpp"

DefineBaseException(RegistryError);
DefineChildException(ModuleNotFound, RegistryError);

struct RegistryModuleEntry
{
  concurrency::Atomic<bool> warmedup;
  std::unique_ptr<modules::ModuleInterface> module;

  RegistryModuleEntry(std::unique_ptr<modules::ModuleInterface> &&module) : warmedup(false)
  {
    this->module.swap(module);
  }
};

class Registry
{
  const int STAGE_1 = 1; // Stopped and no loaded modules
  const int STAGE_2 = 2; // Modules loaded but waiting for initial broadcast
  const int STAGE_3 = 3; // Running
  const int STAGE_4 = 4; // Unloading modules

  std::shared_ptr<Logger> logger;

  concurrency::Atomic<int> stage;

  std::vector<std::unique_ptr<RegistryModuleEntry>> modules;

  std::mutex wait_lock;
  std::condition_variable wait_handler;

  public:
    Registry();

    bool ready();
    void insert(std::unique_ptr<modules::ModuleInterface> &&module);
    void load(std::function<void(std::string)> add_stdin_subscriber);
    void unload();
    bool wait();
    void notify(std::string module_name);
    std::string get(std::string module_name);
    std::unique_ptr<RegistryModuleEntry>& find(std::string module_name);
};