#pragma once

#include <cstring>
#include <string>

#include "adapters/il2cpp/Il2CppApi.h"
#include "foundation/diagnostics/Logger.h"

namespace ta::adapters::il2cpp
{
    inline Il2CppImage* resolve_image(const char* name)
    {
        if (!name) return nullptr;

        Il2CppDomain* domain = ta::adapters::il2cpp::il2cpp_domain_get();
        if (!domain)
        {
            ta::foundation::logger::warn(std::string("resolve_image FAILED (null domain): ") + name);
            return nullptr;
        }

        size_t assemblies_size = 0;
        const Il2CppAssembly** assemblies = ta::adapters::il2cpp::il2cpp_domain_get_assemblies(domain, &assemblies_size);
        if (!assemblies)
        {
            ta::foundation::logger::warn(std::string("resolve_image FAILED (null assemblies): ") + name);
            return nullptr;
        }

        for (size_t i = 0; i < assemblies_size; ++i)
        {
            Il2CppImage* image = assemblies[i] ? assemblies[i]->image : nullptr;
            if (image && image->name && std::string(image->name) == name)
                return image;
        }

        ta::foundation::logger::warn(std::string("resolve_image FAILED: ") + name);
        return nullptr;
    }

    inline const Il2CppClass* resolve_class_nested(const Il2CppClass* parent, const char* name)
    {
        if (!parent || !name)
        {
            ta::foundation::logger::warn(std::string("resolve_class_nested FAILED (null input): ") + (name ? name : "?"));
            return nullptr;
        }

        void* iter = nullptr;
        Il2CppClass* nested = nullptr;
        while ((nested = ta::adapters::il2cpp::il2cpp_class_get_nested_types(const_cast<Il2CppClass*>(parent), &iter)) != nullptr)
        {
            if (nested->name && strcmp(nested->name, name) == 0)
                return nested;
        }

        ta::foundation::logger::warn(std::string("resolve_class_nested FAILED: ") + (parent->name ? parent->name : "?") + "::" + name);
        return nullptr;
    }

    inline const Il2CppClass* resolve_class(const Il2CppImage* image, const char* namespaze, const char* name)
    {
        if (!image || !namespaze || !name)
        {
            ta::foundation::logger::warn(std::string("resolve_class FAILED (null input): ") + (namespaze ? namespaze : "?") + "." + (name ? name : "?"));
            return nullptr;
        }

        return ta::adapters::il2cpp::il2cpp_class_from_name(image, namespaze, name);
    }

    inline Il2CppObject* get_system_type(const Il2CppImage* image, const char* namespaze, const char* name)
    {
        const Il2CppClass* klass = resolve_class(image, namespaze, name);
        if (!klass) return nullptr;

        const Il2CppType* type = ta::adapters::il2cpp::il2cpp_class_get_type(const_cast<Il2CppClass*>(klass));
        if (!type) return nullptr;

        return ta::adapters::il2cpp::il2cpp_type_get_object(type);
    }
}
