//
// Created by lhyqy5 on 2025/12/9.
//

#include "vkloader.h"
// vulkan_loader.cpp


#include <dlfcn.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#define LOG(...) do { std::fprintf(stderr, __VA_ARGS__); std::fprintf(stderr, "\n"); } while(0)

VulkanLoader::VulkanLoader() {}

VulkanLoader::~VulkanLoader() {
    if (instance != VK_NULL_HANDLE) {
        auto vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
                vkGetInstanceProcAddr(instance, "vkDestroyInstance"));

        if (vkDestroyInstance) {
            vkDestroyInstance(instance, nullptr);
        }
        instance = VK_NULL_HANDLE;
    }

    if (lib) {
        dlclose(lib);
        lib = nullptr;
    }
}

bool VulkanLoader::openLib(const char* path) {
    lib = dlopen(path, RTLD_NOW | RTLD_LOCAL);
    if (!lib) {
        LOG("dlopen(%s) failed: %s", path, dlerror());
        return false;
    }

    void* sym = dlsym(lib, "vkGetInstanceProcAddr");
    if (!sym) {
        LOG("dlsym(vkGetInstanceProcAddr) returned NULL");
        dlclose(lib);
        lib = nullptr;
        return false;
    }

    vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(sym);
    LOG("vkGetInstanceProcAddr = %p", sym);

    return true;
}

bool VulkanLoader::loadGlobalFuncs() {
    if (!vkGetInstanceProcAddr) return false;

    vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(
            vkGetInstanceProcAddr(nullptr, "vkCreateInstance"));

    vkEnumerateInstanceExtensionProperties =
            reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
                    vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceExtensionProperties"));

    LOG("vkCreateInstance = %p", reinterpret_cast<void*>(vkCreateInstance));
    LOG("vkEnumerateInstanceExtensionProperties = %p",
        reinterpret_cast<void*>(vkEnumerateInstanceExtensionProperties));

    return vkCreateInstance != nullptr;
}

bool VulkanLoader::createInstance(const VkInstanceCreateInfo* createInfo,
                                  const VkAllocationCallbacks *pAllocator) {
    if (!vkCreateInstance) return false;

//    VkInstanceCreateInfo ci{};
//    ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
//    ci.pApplicationInfo = &appInfo;
//    ci.enabledExtensionCount = (uint32_t)instanceExtensions.size();
//    ci.ppEnabledExtensionNames = instanceExtensions.empty() ? nullptr : instanceExtensions.data();

    VkResult r = vkCreateInstance(createInfo, pAllocator, &instance);

    if (r != VK_SUCCESS) {
        LOG("vkCreateInstance failed: %d", r);
        return false;
    }

    LOG("vkCreateInstance ok, instance=%p", (void*)instance);
    return true;
}

void* VulkanLoader::_getInstanceProc(const char* name) {
    if (!vkGetInstanceProcAddr || instance == VK_NULL_HANDLE)
        return nullptr;

    void* fn = reinterpret_cast<void*>(vkGetInstanceProcAddr(instance, name));
    LOG("instance-level %s = %p", name, fn);
    return fn;
}

void* VulkanLoader::_getDeviceProc(VkDevice device, const char* name) {
    if (!vkGetInstanceProcAddr)
        return nullptr;

    // get vkGetDeviceProcAddr first
    auto vkGetDeviceProcAddr =
            reinterpret_cast<PFN_vkGetDeviceProcAddr>(
                    vkGetInstanceProcAddr(instance, "vkGetDeviceProcAddr"));

    if (vkGetDeviceProcAddr) {
        void* fn = reinterpret_cast<void*>(vkGetDeviceProcAddr(device, name));
        LOG("device-level %s = %p", name, fn);
        return fn;
    }

    // fallback (rare) – some drivers expose device functions at instance level
    void* fn = reinterpret_cast<void*>(vkGetInstanceProcAddr(instance, name));
    LOG("fallback device-level %s = %p", name, fn);
    return fn;
}
