#include "adapters/hooks/items/ItemHookState.h"

#include <atomic>

#include "application/lifecycle/RuntimeState.h"

namespace ta::adapters::hooks::items
{
    namespace
    {
        std::atomic<Il2CppObject*> factory{nullptr};
        std::atomic_bool no_slot_limits{false};
        std::atomic_bool increased_transfer_size{false};
        std::atomic<EnsureFactoryHookFn> ensure_factory_hook{nullptr};
    }

    void set_no_slot_limits(const bool enabled) noexcept
    {
        no_slot_limits.store(enabled, std::memory_order_release);
    }

    void set_increased_transfer_size(const bool enabled) noexcept
    {
        increased_transfer_size.store(enabled, std::memory_order_release);
    }

    bool no_slot_limits_enabled() noexcept
    {
        return ta::application::runtime::should_accept_callbacks() &&
            no_slot_limits.load(std::memory_order_acquire);
    }

    bool increased_transfer_size_enabled() noexcept
    {
        return ta::application::runtime::should_accept_callbacks() &&
            increased_transfer_size.load(std::memory_order_acquire);
    }

    Il2CppObject* item_factory() noexcept
    {
        return factory.load(std::memory_order_acquire);
    }

    void set_item_factory(Il2CppObject* value) noexcept
    {
        factory.store(value, std::memory_order_release);
    }

    void set_ensure_factory_hook(const EnsureFactoryHookFn function) noexcept
    {
        ensure_factory_hook.store(function, std::memory_order_release);
    }

    bool ensure_item_factory_available()
    {
        if (item_factory())
            return true;
        const auto function = ensure_factory_hook.load(std::memory_order_acquire);
        return function ? function() : false;
    }

    void reset_item_factory() noexcept
    {
        factory.store(nullptr, std::memory_order_release);
    }

    void reset_state() noexcept
    {
        reset_item_factory();
        set_ensure_factory_hook(nullptr);
        no_slot_limits.store(false, std::memory_order_release);
        increased_transfer_size.store(false, std::memory_order_release);
    }
}
