package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen;

import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.PhysicalDevice;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.VulkanInstance;
import org.lwjgl.glfw.GLFWVulkan;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.KHRSurface;
import org.lwjgl.vulkan.VkSurfaceCapabilitiesKHR;
import org.lwjgl.vulkan.VkSurfaceFormatKHR;
import org.tinylog.Logger;

import java.nio.IntBuffer;
import java.nio.LongBuffer;

import static net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils.vkCheck;
import static org.lwjgl.vulkan.VK13.VK_FORMAT_B8G8R8A8_SRGB;

public class Surface {
    private final VkSurfaceCapabilitiesKHR SurfaceCapabilities;
    private final SurfaceFormat SurfaceFormatVar;
    private final long VulkanSurface;

    public record SurfaceFormat(int ImageFormat, int ColourSpace){}

    public Surface(VulkanInstance VkInstance, PhysicalDevice PhysDevice, Window window){
        Logger.debug("Creating Vulkan Surface");
        try(var MemStack = MemoryStack.stackPush()){
            LongBuffer SurfacePtr = MemStack.mallocLong(1);
            GLFWVulkan.glfwCreateWindowSurface(VkInstance.getVkInstance(), window.getHandle(), null, SurfacePtr);
            VulkanSurface = SurfacePtr.get(0);

            SurfaceCapabilities = VkSurfaceCapabilitiesKHR.calloc();
            vkCheck(KHRSurface.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(PhysDevice.GetPhysicalDevice(), VulkanSurface,SurfaceCapabilities),"Failed to get surface capabilities");

            SurfaceFormatVar = CalculateSurfaceFormat(PhysDevice, VulkanSurface);
        }
    }

    public static SurfaceFormat CalculateSurfaceFormat(PhysicalDevice PhysDevice, long VulkanSurface){
        int ImageFormat;
        int ColourSpace ;
        try(var MemStack = MemoryStack.stackPush()){
            IntBuffer IntPointer = MemStack.mallocInt(1);
            vkCheck(KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR(PhysDevice.GetPhysicalDevice(), VulkanSurface, IntPointer, null),"Failed to get number of surface formats");
            int SurfaceFormatCount = IntPointer.get(0);
            if (SurfaceFormatCount <= 0){
                throw new RuntimeException("Vulkan Error: Unable to locate and surface formats");
            }

            var SurfaceFormats = VkSurfaceFormatKHR.calloc(SurfaceFormatCount, MemStack);
            vkCheck(KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR(PhysDevice.GetPhysicalDevice(),VulkanSurface, IntPointer, SurfaceFormats), "Failed to get surface formats");

            ImageFormat = VK_FORMAT_B8G8R8A8_SRGB;
            ColourSpace = SurfaceFormats.get(0).colorSpace();
            for(int i = 0; i < SurfaceFormatCount; i++){
                VkSurfaceFormatKHR SurfaceFormatKHR = SurfaceFormats.get(i);
                if (SurfaceFormatKHR.format() == VK_FORMAT_B8G8R8A8_SRGB && SurfaceFormatKHR.colorSpace() == KHRSurface.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR){
                    ImageFormat = SurfaceFormatKHR.format();
                    ColourSpace = SurfaceFormatKHR.colorSpace();
                    break;
                }
            }
        }
        return new SurfaceFormat(ImageFormat, ColourSpace);
    }
    public void cleanup(VulkanInstance instance){
        Logger.debug("Cleaning Up Surfaces");
        SurfaceCapabilities.free();
        KHRSurface.vkDestroySurfaceKHR(instance.getVkInstance(), VulkanSurface, null);
        VulkanContext.VulkanLayersOpen--;
    }

    public VkSurfaceCapabilitiesKHR GetSurfaceCapabilities(){
        return SurfaceCapabilities;
    }
    public SurfaceFormat GetSurfaceFormat(){
        return SurfaceFormatVar;
    }
    public long GetVkSurface(){
        return VulkanSurface;
    }

}
