#pragma once

#include <cstdint>
#include <utility>

#include "adapters/il2cpp/Il2CppObjectInstance.h"
#include "adapters/il2cpp/Il2CppObjectAccess.h"
#include "adapters/il2cpp/Il2CppString.h"
#include "adapters/unity/bindings/UnityTypes.h"
#include "adapters/il2cpp/Il2CppCollections.h"
#include "adapters/unity/bindings/UnityApi.h"
#include "adapters/eft/common/ManagedObjectAccess.h"

namespace ta::adapters::eft::common
{
    inline bool get_player_position(ta::adapters::il2cpp::Il2CppObjectInstance main_player, ta::adapters::unity::vector3& out_pos)
    {
        Il2CppObject* main_player_ptr = main_player.get_instance();
        if (!main_player_ptr) return false;

        Il2CppObject* player_object = ta::adapters::unity::component_get_game_object(main_player_ptr);
        if (!player_object) return false;

        Il2CppObject* player_transform = ta::adapters::unity::gameobject_get_transform(player_object);
        if (!player_transform) return false;

        out_pos = ta::adapters::unity::transform_get_pos(player_transform);
        return true;
    }

    inline ta::adapters::unity::list* get_loot_items(ta::adapters::il2cpp::Il2CppObjectInstance game_world_instance)
    {
        Il2CppObject* game_world = game_world_instance.get_instance();
        if (!game_world || !game_world->klass) return nullptr;

        const auto loot_items_result =
            ta::adapters::il2cpp::ManagedRuntime::read_field<Il2CppObject*>(game_world, "LootItems");
        Il2CppObject* loot_items_ptr = loot_items_result ? loot_items_result.value() : nullptr;
        if (!loot_items_ptr || !loot_items_ptr->klass) return nullptr;

        const auto iteration_result =
            ta::adapters::il2cpp::ManagedRuntime::read_field<ta::adapters::unity::list*>(
                loot_items_ptr, "_iteration");
        auto* items = iteration_result ? iteration_result.value() : nullptr;
        if (!items || !items->m_p_list_array) return nullptr;

        return items;
    }

    inline ta::adapters::unity::list* get_world_loot_list(ta::adapters::il2cpp::Il2CppObjectInstance game_world_instance)
    {
        Il2CppObject* game_world = game_world_instance.get_instance();
        if (!game_world || !game_world->klass) return nullptr;

        const auto loot_list_result =
            ta::adapters::il2cpp::ManagedRuntime::read_field<ta::adapters::unity::list*>(game_world, "LootList");
        auto* loot_list = loot_list_result ? loot_list_result.value() : nullptr;
        if (!loot_list || !loot_list->m_p_list_array) return nullptr;

        return loot_list;
    }

    inline ta::adapters::unity::hashset<Il2CppObject*>* get_quest_items(ta::adapters::il2cpp::Il2CppObjectInstance game_world_instance)
    {
        Il2CppObject* game_world = game_world_instance.get_instance();
        if (!game_world || !game_world->klass) return nullptr;

        const auto quest_items_result =
            ta::adapters::il2cpp::ManagedRuntime::read_field<
                ta::adapters::unity::hashset<Il2CppObject*>*>(game_world, "QuestItemsList");
        auto* quest_items = quest_items_result ? quest_items_result.value() : nullptr;
        if (!quest_items || !quest_items->m_p_slots) return nullptr;

        return quest_items;
    }

    inline Il2CppObject* get_component_transform(Il2CppObject* component)
    {
        if (!component) return nullptr;

        Il2CppObject* object = ta::adapters::unity::component_get_game_object(component);
        if (!object) return nullptr;

        return ta::adapters::unity::gameobject_get_transform(object);
    }

    inline bool get_component_position(Il2CppObject* component, ta::adapters::unity::vector3& out_position)
    {
        Il2CppObject* transform = get_component_transform(component);
        if (!transform) return false;

        out_position = ta::adapters::unity::transform_get_pos(transform);
        return true;
    }

    inline bool set_component_position(Il2CppObject* component, const ta::adapters::unity::vector3& position)
    {
        Il2CppObject* transform = get_component_transform(component);
        if (!transform) return false;

        ta::adapters::unity::transform_set_pos(transform, position);
        return true;
    }

    template <typename Callback>
    void for_each_loot_item(ta::adapters::il2cpp::Il2CppObjectInstance game_world_instance, Callback&& callback)
    {
        ta::adapters::unity::list* items = get_loot_items(game_world_instance);
        if (!items || !items->m_p_list_array)
            return;

        for_each_list_object(items, std::forward<Callback>(callback));
    }

    template <typename Callback>
    void for_each_world_loot_object(ta::adapters::il2cpp::Il2CppObjectInstance game_world_instance, Callback&& callback)
    {
        ta::adapters::unity::list* loot_list = get_world_loot_list(game_world_instance);
        if (!loot_list || !loot_list->m_p_list_array)
            return;

        for_each_list_object(loot_list, std::forward<Callback>(callback));
    }

    template <typename Callback>
    void for_each_quest_loot(ta::adapters::il2cpp::Il2CppObjectInstance game_world_instance, Callback&& callback)
    {
        auto* quest_items = get_quest_items(game_world_instance);
        if (!quest_items || !quest_items->m_p_slots)
            return;

        for_each_hashset_value(quest_items, std::forward<Callback>(callback));
    }
}
