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

import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
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.PointerBuffer;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.util.vma.VmaAllocationCreateInfo;
import org.lwjgl.vulkan.VkImageCreateInfo;
import org.lwjgl.vulkan.VkMemoryAllocateInfo;
import org.lwjgl.vulkan.VkMemoryRequirements;

import java.nio.LongBuffer;

import static org.lwjgl.util.vma.Vma.*;
import static org.lwjgl.vulkan.VK10.*;
import static org.lwjgl.vulkan.VK13.VK_FORMAT_R8G8B8A8_SRGB;

public class Image {

    private final long Allocation;
    private final int Format;
    private final int MipLevels;
    private final long VulkanImage;
    private final int Width;
    private final int Height;
    //private final long VulkanMemory;

    public int GetWidth(){return Width;}
    public int GetHeight(){return Height;}

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

            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);

            var AllocationCreateInfo = VmaAllocationCreateInfo.calloc(1,MemStack)
                    .get(0)
                    .usage(VMA_MEMORY_USAGE_AUTO)
                    .flags(imageData.MemoryUsage)
                    .priority(1.0f);
            PointerBuffer AllocationPointer = MemStack.callocPointer(1);
            LongBuffer LongPtr = MemStack.mallocLong(1);
            //allocate
            VulkanUtils.vkCheck(vmaCreateImage(VkCtx.GetVkMemoryAllocator().GetVmaAllocator(),vkImageCreateInfo,AllocationCreateInfo,LongPtr,AllocationPointer,null)
            ,"Failed to allocate Memory for Vulkan Image");
            VulkanImage = LongPtr.get(0);
            Allocation = AllocationPointer.get(0);
        }
    }
    public void CleanUp(VulkanContext VkCtx){
        vmaDestroyImage(VkCtx.GetVkMemoryAllocator().GetVmaAllocator(),VulkanImage,Allocation);
    }
    public int GetFormat(){return Format;}
    public int GetMipLevels(){return MipLevels;}
    public long getVulkanImage(){return VulkanImage;}

    public static class ImageData{
        private int MemoryUsage;
        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;
            MemoryUsage = 0;
        }
        public ImageData MemoryUsage(int memoryUsage){
            this.MemoryUsage = memoryUsage;
            return this;
        }
        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;
        }
    }
}
