#pragma once

#include <cstdint>
#include <utility>

#include "external/il2cpp/il2cpp-api-types.h"
#include "adapters/il2cpp/ManagedRuntime.h"
#include "adapters/unity/bindings/UnityTypes.h"
#include "adapters/il2cpp/Il2CppCollections.h"
#include "foundation/diagnostics/OnceLogger.h"

namespace ta::adapters::eft::common
{
    template <typename Callback>
    void for_each_list_object(ta::adapters::unity::list* list, Callback&& callback)
    {
        if (!list || !list->m_p_list_array)
            return;

        const int capacity = static_cast<int>(list->m_p_list_array->max_length);
        if (capacity <= 0)
            return;

        const int size = list->m_i_size;
        if (size < 0 || size > capacity)
        {
            ta::foundation::diagnostics::OnceLogger::error(
                "IL2CPP", "invalid-list-size", "Managed list size is outside its backing-array capacity.");
            return;
        }
        if (size == 0)
            return;

        auto** data = reinterpret_cast<Il2CppObject**>(&list->m_p_list_array->data);
        for (int i = 0; i < size; ++i)
        {
            callback(data[i], i);
        }
    }

    template <typename T, typename Callback>
    void for_each_hashset_value(ta::adapters::unity::hashset<T>* hashset, Callback&& callback)
    {
        if (!hashset || !hashset->m_p_slots)
            return;

        auto* slots = hashset->get_slots();
        if (!slots)
            return;

        for (int i = 0; i < hashset->m_i_last_index; ++i)
        {
            if (slots[i].m_i_hash_code < 0)
                continue;

            callback(slots[i].m_t_value, i);
        }
    }

    inline Il2CppObject* get_object_method(Il2CppObject* object, const char* method_name)
    {
        if (!object || !object->klass || !method_name)
            return nullptr;

        const auto result = ta::adapters::il2cpp::ManagedRuntime::invoke<Il2CppObject*>(
            object, method_name, ta::adapters::il2cpp::LookupPolicy::IncludeBaseClasses);
        return result ? result.value() : nullptr;
    }

    inline Il2CppObject* get_object_field(Il2CppObject* object, const char* field_name)
    {
        if (!object || !object->klass || !field_name)
            return nullptr;

        const auto result = ta::adapters::il2cpp::ManagedRuntime::read_field<Il2CppObject*>(object, field_name);
        return result ? result.value() : nullptr;
    }

    inline bool get_bool_method(Il2CppObject* object, const char* method_name, bool& value)
    {
        if (!object || !object->klass || !method_name)
            return false;

        const auto result = ta::adapters::il2cpp::ManagedRuntime::invoke<bool>(
            object, method_name, ta::adapters::il2cpp::LookupPolicy::IncludeBaseClasses);
        if (!result)
            return false;
        value = result.value();
        return true;
    }


}
