package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images;

import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkImageCreateInfo;
import org.lwjgl.vulkan.VkMemoryAllocateInfo;
import org.lwjgl.vulkan.VkMemoryRequirements;

import java.nio.LongBuffer;

import static org.lwjgl.vulkan.VK10.*;
import static org.lwjgl.vulkan.VK13.VK_FORMAT_R8G8B8A8_SRGB;

public class Image {

    private final int Format;
    private final int MipLevels;
    private final long VulkanImage;
    private final long VulkanMemory;

    public Image(VulkanContext VkCtx, ImageData imageData){
        try(var MemStack = MemoryStack.stackPush()){
            this.Format = imageData.Format;
            this.MipLevels = imageData.MipMapLevels;

            VkImageCreateInfo vkImageCreateInfo = VkImageCreateInfo.calloc(MemStack)
                    .sType$Default()
                    .imageType(VK_IMAGE_TYPE_2D)
                    .format(Format)
                    .extent(it -> it
                            .width(imageData.Width)
                            .height(imageData.Height)
                            .depth(1))
                    .mipLevels(MipLevels)
                    .arrayLayers(imageData.ArrayLayers)
                    .samples(imageData.SampleCount)
                    .initialLayout(VK_IMAGE_LAYOUT_UNDEFINED)
                    .sharingMode(VK_SHARING_MODE_EXCLUSIVE)
                    .tiling(VK_IMAGE_TILING_OPTIMAL)
                    .usage(imageData.Usage);
            Device device = VkCtx.GetDevice();
            LongBuffer LongPtr = MemStack.mallocLong(1);
            VulkanUtils.vkCheck(vkCreateImage(device.FetchVulkanDevice(),vkImageCreateInfo,null,LongPtr),
                    "Failed to create Vulkan Image");
            VulkanImage = LongPtr.get(0);
            VkMemoryRequirements memoryRequirements = VkMemoryRequirements.calloc(MemStack);
            vkGetImageMemoryRequirements(device.FetchVulkanDevice(), VulkanImage, memoryRequirements);

            var MemoryAllocation = VkMemoryAllocateInfo.calloc(MemStack)
                    .sType$Default()
                    .allocationSize(memoryRequirements.size())
                    .memoryTypeIndex(VulkanUtils.MemoryTypeFromProperties(VkCtx, memoryRequirements.memoryTypeBits(),0));
            //allocate
            VulkanUtils.vkCheck(vkAllocateMemory(device.FetchVulkanDevice(),MemoryAllocation,null,LongPtr)
            ,"Failed to allocate Memory for Vulkan Image");
            VulkanMemory = LongPtr.get(0);
            //bind
            VulkanUtils.vkCheck(vkBindImageMemory(device.FetchVulkanDevice(),VulkanImage,VulkanMemory,0)
            ,"Failed to bind memory for Vulkan Image");
        }
    }
    public void CleanUp(VulkanContext VkCtx){
        vkDestroyImage(VkCtx.GetDevice().FetchVulkanDevice(), VulkanImage,null);
        vkFreeMemory(VkCtx.GetDevice().FetchVulkanDevice(), VulkanMemory,null);
    }
    public int GetFormat(){return Format;}
    public int GetMipLevels(){return MipLevels;}
    public long getVulkanImage(){return VulkanImage;}

    public static class ImageData{
        private int ArrayLayers;
        private int Format;
        private int Height;
        private int Width;
        private int MipMapLevels;
        private int SampleCount;
        private int Usage;

        public ImageData(){
            Format = VK_FORMAT_R8G8B8A8_SRGB;
            MipMapLevels = 1;
            SampleCount = 1;
            ArrayLayers = 1;
        }
        public ImageData ArrayLayers(int ArrayLayers){
            this.ArrayLayers = ArrayLayers;
            return this;
        }
        public ImageData Format(int Format){
            this.Format = Format;
            return this;
        }
        public ImageData Height(int Height){
            this.Height = Height;
            return this;
        }
        public ImageData Width(int Width){
            this.Width = Width;
            return this;
        }
        public ImageData MipMapLevels(int MipLevel){
            this.MipMapLevels = MipLevel;
            return this;
        }
        public ImageData SampleCount(int Samples){
            this.SampleCount = Samples;
            return this;
        }
        public ImageData Usage(int Usage){
            this.Usage = Usage;
            return this;
        }
    }
}
