#pragma once

#include <types/base/types.h>
#include <functional>
#include <tuple>
#include <vector>
#include <cstddef>

namespace extra2d::event {

/**
 * @brief 监听器链表节点
 */
struct ListenerEntry {
    ListenerEntry* next = nullptr;
    ListenerEntry* prev = nullptr;
    class ListenerBase* listener = nullptr;
};

/**
 * @brief 监听器基类
 */
class ListenerBase {
protected:
    ListenerEntry* entry_ = nullptr;
    friend class ListenerContainer;
};

/**
 * @brief 监听器容器，管理同一事件的所有监听器
 */
class ListenerContainer {
public:
    ListenerContainer() = default;
    ~ListenerContainer();

    template<typename EHandler, typename... Args>
    bool broadcast(Args&&... args);

protected:
    ListenerEntry* listenerList_ = nullptr;
    ListenerEntry* listenersToAdd_ = nullptr;
    std::vector<ListenerEntry*> listenersToRemove_;
    int broadcasting_ = 0;

    void addListener(ListenerBase* listener);
    void removeListener(ListenerBase* listener);
    void processPendingListeners();
    bool hasPendingListeners() const;

    template<typename EHandler>
    friend class Listener;
};

/**
 * @brief 事件处理器数据库，每个事件类型一个容器
 */
template<typename EHandler>
class ListenerDB {
public:
    static ListenerContainer* container() {
        static ListenerContainer* ctn = new ListenerContainer();
        return ctn;
    }
};

/**
 * @brief 事件特征定义
 */
template<typename BusT, typename... Args>
struct EventTrait {
    using Bus = BusT;
    using ArgTuple = std::tuple<Args...>;
    static constexpr size_t ARG_COUNT = sizeof...(Args);
};

/**
 * @brief 元组提取器
 */
template<typename T>
struct TupleExtractor {
    using FuncType = void();
    using StdFuncType = std::function<void()>;
};

template<typename... Args>
struct TupleExtractor<std::tuple<Args...>> {
    using FuncType = void(Args...);
    using StdFuncType = std::function<void(Args...)>;
};

/**
 * @brief 监听器模板类
 * 
 * 构造时自动注册，析构时自动注销
 */
template<typename EHandler>
class Listener : public ListenerBase {
public:
    using ArgTuple = typename EHandler::ArgTuple;
    using StdFuncType = typename TupleExtractor<ArgTuple>::StdFuncType;

    Listener();
    ~Listener();

    template<typename Fn>
    void bind(Fn&& func) {
        callback_ = std::forward<Fn>(func);
    }

    void enable() { enabled_ = true; }
    void disable() { enabled_ = false; }
    bool isEnabled() const { return enabled_; }
    void reset() { callback_ = nullptr; }

    template<typename... Args>
    void invoke(Args&&... args) {
        if (callback_ && enabled_) {
            callback_(std::forward<Args>(args)...);
        }
    }

    const char* busName() const { return EHandler::BUS_NAME; }
    const char* eventName() const { return EHandler::NAME; }

private:
    bool enabled_ = true;
    StdFuncType callback_;
};

template<typename EHandler>
Listener<EHandler>::Listener() {
    entry_ = new ListenerEntry();
    entry_->listener = this;
    ListenerDB<EHandler>::container()->addListener(this);
}

template<typename EHandler>
Listener<EHandler>::~Listener() {
    ListenerDB<EHandler>::container()->removeListener(this);
}

#define EVENT_LIST_LOOP_BEGIN(curr, list) \
    for (ListenerEntry* curr = list; curr != nullptr; curr = curr->next) {

#define EVENT_LIST_LOOP_END(curr, list) }

template<typename EHandler, typename... Args>
bool ListenerContainer::broadcast(Args&&... args) {
    broadcasting_++;
    
    EVENT_LIST_LOOP_BEGIN(curr, listenerList_)
        if (curr->listener) {
            static_cast<Listener<EHandler>*>(curr->listener)->invoke(std::forward<Args>(args)...);
        }
    EVENT_LIST_LOOP_END(curr, listenerList_)
    
    broadcasting_--;
    
    if (!broadcasting_ && hasPendingListeners()) {
        processPendingListeners();
    }
    
    return true;
}

/**
 * @brief 广播事件
 */
template<typename EHandler, typename... Args>
void broadcast(Args&&... args) {
    static_assert(sizeof...(Args) == EHandler::ARG_COUNT, "Parameter count incorrect");
    auto* container = ListenerDB<EHandler>::container();
    container->template broadcast<EHandler, Args...>(std::forward<Args>(args)...);
}

} // namespace extra2d::event
