#pragma once
#include <drogon/DrObject.h>
#include <json/json.h>
#include <memory>
#include <trantor/utils/Logger.h>
#include <trantor/utils/NonCopyable.h>
namespace drogon
{
enum class PluginStatus
{
    None,
    Initializing,
    Initialized
};
class DROGON_EXPORT PluginBase : public virtual DrObjectBase,
                                 public trantor::NonCopyable
{
  public:
    void initialize()
    {
        if (status_ == PluginStatus::None)
        {
            status_ = PluginStatus::Initializing;
            for (auto dependency : dependencies_)
            {
                dependency->initialize();
            }
            initAndStart(config_);
            status_ = PluginStatus::Initialized;
            if (initializedCallback_)
                initializedCallback_(this);
        }
        else if (status_ == PluginStatus::Initialized)
        {
        }
        else
        {
            LOG_FATAL << "There are a circular dependency within plugins.";
            abort();
        }
    }
    virtual void initAndStart(const Json::Value &config) = 0;
    virtual void shutdown() = 0;
    virtual ~PluginBase()
    {
    }
  protected:
    PluginBase()
    {
    }
  private:
    PluginStatus status_{PluginStatus::None};
    friend class PluginsManager;
    void setConfig(const Json::Value &config)
    {
        config_ = config;
    }
    void addDependency(PluginBase *dp)
    {
        dependencies_.push_back(dp);
    }
    void setInitializedCallback(const std::function<void(PluginBase *)> &cb)
    {
        initializedCallback_ = cb;
    }
    Json::Value config_;
    std::vector<PluginBase *> dependencies_;
    std::function<void(PluginBase *)> initializedCallback_;
};
template <typename T>
struct IsPlugin
{
    using TYPE = std::remove_cv_t<typename std::remove_reference_t<T>>;
    static int test(void *)
    {
        return 0;
    }
    static char test(PluginBase *)
    {
        return 0;
    }
    static constexpr bool value =
        (sizeof(test((TYPE *)nullptr)) == sizeof(char));
};
template <typename T>
class Plugin : public PluginBase, public DrObject<T>
{
  public:
    virtual ~Plugin()
    {
    }
  protected:
    Plugin()
    {
    }
};
}  