#pragma once

#include "forward.hpp"
#include "../../../core/utils/logger.hpp"
#include <vector>
#include <optional>

struct GLFWwindow;
namespace render::vk {
struct SwapChainSupportDetails {
    VkSurfaceCapabilitiesKHR capabilities;
    std::vector<VkSurfaceFormatKHR> formats;
    std::vector<VkPresentModeKHR> presentModes;
    static SwapChainSupportDetails Query(VkPhysicalDevice, VkSurfaceKHR);
    bool isValid() const { return !formats.empty() && !presentModes.empty(); }
};
struct QueueFamilyIndices {
    std::optional<uint32_t> graphicsFamily;
    std::optional<uint32_t> presentFamily;
    std::optional<uint32_t> transferFamily;
    static QueueFamilyIndices Query(VkPhysicalDevice, VkSurfaceKHR);
    bool isComplete() const { return graphicsFamily.has_value() && presentFamily.has_value(); }
    bool isOptimal() const { return isComplete() && transferFamily.has_value(); }
};
struct PhysicalDeviceInfo {
    PhysicalDeviceInfo() {}
    PhysicalDeviceInfo(GLFWwindow *window, VkPhysicalDevice device, VkSurfaceKHR surface, int targetSamples, bool vSync):
        window(window), device(device), surface(surface),
        swapDetails(SwapChainSupportDetails::Query(device, surface)), queueIndices(QueueFamilyIndices::Query(device, surface)),
        vSync(vSync)
    {
        vkGetPhysicalDeviceProperties(device, &properties);
        vkGetPhysicalDeviceFeatures(device, &features);

        samples = [&] {
            VkSampleCountFlags counts = properties.limits.framebufferColorSampleCounts & properties.limits.framebufferDepthSampleCounts;
            //MAYBE: properties.limits.framebufferStencilSampleCounts
            if (targetSamples < 0)
                targetSamples = counts / 4;
            for (auto sample: {VK_SAMPLE_COUNT_64_BIT, VK_SAMPLE_COUNT_32_BIT, VK_SAMPLE_COUNT_16_BIT,
                VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_2_BIT
            }) {
                if (targetSamples >= sample && (counts & sample))
                    return sample;
            }
            return VK_SAMPLE_COUNT_1_BIT;
        }();
    }

    VkSurfaceFormatKHR getSurfaceFormat() const;
    std::optional<VkFormat> findSupportedFormat(const std::vector<VkFormat> &candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const;
    VkFormat findDepthFormat() const;
    bool hasMemoryBudget() const;

    static bool HasStencilComponent(VkFormat format) {
        return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
    }

    GLFWwindow *window;
    VkPhysicalDevice device = VK_NULL_HANDLE;
    VkSurfaceKHR surface;
    VkSampleCountFlagBits samples;
    SwapChainSupportDetails swapDetails;
    QueueFamilyIndices queueIndices;
    VkPhysicalDeviceProperties properties;
    VkPhysicalDeviceFeatures features;
    std::vector<const char *> optionalExtensions;
    bool vSync;
    bool tripleBuffering = true;
};
}