#include "mitt.h"
#include <future>
#include <stdexcept>
#include <typeindex>
#include <typeinfo>

namespace zsy::Mitt
{
    EventKey::EventKey(const void *p, const std::type_info *ti) noexcept
        : ptr(p), type(ti), mtx(std::make_shared<std::shared_mutex>())
    {
    }

    bool EventKey::operator==(const EventKey &other) const noexcept
    {
        return (ptr == other.ptr) && (*type == *other.type);
    }

    std::size_t EventKeyHash::operator()(const EventKey &key) const noexcept
    {
        const std::size_t h1 = std::hash<const void *>()(key.ptr);
        const std::size_t h2 = key.type->hash_code();
        return h1 ^ (h2 << 1);
    }

    MittVoidEvent::MittVoidEvent()
    {
        const auto event_ptr = static_cast<const void *>(this);
        const std::type_info *event_type = &typeid(MittVoidEvent);
        key = std::make_shared < EventKey > (event_ptr, event_type);
    }

    std::shared_ptr<MittVoidEvent> MittVoidEvent::make_event()
    {
        return std::make_shared<MittVoidEvent>();
    }

    void MittVoidEvent::$emit() const
    {
        std::shared_lock lock(*key->mtx);

        const auto it = EventPool.find(*key);
        if (it == EventPool.end()) return;
        tf::Taskflow tf;
        auto count = 0;
        const auto &callback_list = it->second;
        for (const auto &void_ptr: callback_list)
        {
            if (auto callback_ptr = std::static_pointer_cast<EventCallbackWithoutArg>(void_ptr))
            {
                count++;
                tf.emplace([callback_ptr]
                {
                    try
                    {
                        (*callback_ptr)();
                    } catch (const std::exception &e)
                    {
                        LOGGER_ERROR("任务执行失败：{}", e.what());
                    } catch (...)
                    {
                        LOGGER_ERROR("任务执行失败：未知错误");
                    }
                });
            }
        }
        if (count > 0) Exec.run(std::move(tf));
    }

    MittVoidEvent::~MittVoidEvent()
    {
        std::unique_lock lock(*key->mtx);
        EventPool.unsafe_erase(*key);
    }
}