#include "PhysicalDeviceInfo.hpp"

using namespace render::vk;

SwapChainSupportDetails SwapChainSupportDetails::Query(VkPhysicalDevice device, VkSurfaceKHR surface) {
    SwapChainSupportDetails swapDetails;
    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &swapDetails.capabilities);

    uint32_t formatCount;
    vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
    if (formatCount != 0) {
        swapDetails.formats.resize(formatCount);
        vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, swapDetails.formats.data());
    }

    uint32_t presentModeCount;
    vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
    if (presentModeCount != 0) {
        swapDetails.presentModes.resize(presentModeCount);
        vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, swapDetails.presentModes.data());
    }
    return swapDetails;
}

QueueFamilyIndices QueueFamilyIndices::Query(VkPhysicalDevice device, VkSurfaceKHR surface) {
    QueueFamilyIndices queueIndices;
    uint32_t queueFamilyCount = 0;
    vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
    std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
    vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());

    uint32_t i = 0;
    for(const auto& queueFamily: queueFamilies) {
        if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
            queueIndices.graphicsFamily = i;
        } else if (queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT) {
            queueIndices.transferFamily = i;
        }
        VkBool32 presentSupport = false;
        vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
        if (presentSupport) {
            queueIndices.presentFamily = i;
        }

#if LOG_TRACE
        LOG_T("Queue " << i << ' ' << (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT ? "graphics " : "")
                    << (queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT ? "compute " : "")
                    << (presentSupport ? "present " : "")
                    << (queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT ? "transfer " : "")
                    << (queueFamily.queueFlags & VK_QUEUE_PROTECTED_BIT ? "protected " : "")
                    << 'x' << queueFamily.queueCount);
#endif
        i++;
    }

    return queueIndices;
}

VkSurfaceFormatKHR PhysicalDeviceInfo::getSurfaceFormat() const {
    for(const auto& format: swapDetails.formats) {
        if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
            return format;
        }
    }

    LOG_W("Using suboptimal surface format");
    return swapDetails.formats[0];
}
std::optional<VkFormat> PhysicalDeviceInfo::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const {
    for (VkFormat format : candidates) {
        VkFormatProperties props;
        vkGetPhysicalDeviceFormatProperties(device, format, &props);

        if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
            return format;
        } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
            return format;
        }
    }
    return {};
}
VkFormat PhysicalDeviceInfo::findDepthFormat() const {
    if (auto res = findSupportedFormat(
        {VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT},
        VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
        return res.value();

    FATAL("Any compatible depth format");
}
#include <string.h>
bool PhysicalDeviceInfo::hasMemoryBudget() const {
    for (auto extension: optionalExtensions) {
        if (strcmp(extension, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) == 0)
            return true;
    }
    return false;
}