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.Rendering.Pipeline.GraphUtils;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.IndexedLinkedHashMap;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandPool;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Queues.Queue;
import org.tinylog.Logger;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

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

public class TextureCache {
    public static final int MAX_TEXTURES = 100;
    private final IndexedLinkedHashMap<String, Texture> TextureMap;

    public TextureCache(){
        TextureMap = new IndexedLinkedHashMap<>();
    }

    public Texture AddTexture(VulkanContext VkCtx, String ID, ImageSrc imageSrc, int Format){
        if(TextureMap.size() > MAX_TEXTURES){
            throw new IllegalArgumentException("Texture Cache Is Full");
        }
        Texture newTexture = TextureMap.get(ID);
        if(newTexture == null){
            newTexture = new Texture(VkCtx, ID, imageSrc, Format);
            TextureMap.put(ID, newTexture);
        }
        return newTexture;
    }
    public Texture AddTexture(VulkanContext VkCtx, String ID, String TexturePath, int Format) {
        ImageSrc imageSrc = null;
        Texture result = null;
        try{
            imageSrc = GraphUtils.LoadImage(TexturePath);
            result = AddTexture(VkCtx, ID, imageSrc, Format);
        } catch (IOException exception){
            Logger.error("Could not load texture from patch [{}], exception: {}",TexturePath, exception);
        } finally{
            if(imageSrc != null){
                GraphUtils.CleanImageData(imageSrc);
            }
        }
        return result;
    }

    public void TransitionTexts(VulkanContext VkCtx, CommandPool CmdPool, Queue queue){
        Logger.debug("Recording Texture Transition");
        int TextureCount = TextureMap.size();
        if(TextureCount < MAX_TEXTURES){
            int PaddingTextCount = MAX_TEXTURES - TextureCount;
            String DefaultTexturePath = EngineConfig.getInstance().GetDefaultTexturePath();
            for(int i = 0; i < PaddingTextCount; i++){
                AddTexture(VkCtx, UUID.randomUUID().toString(), DefaultTexturePath, VK_FORMAT_R8G8B8A8_SRGB);
            }
        }
        var CommandBuffer = new CommandBuffer(VkCtx, CmdPool, true, true);
        CommandBuffer.BeginRecording();
        TextureMap.forEach((key,value)->value.RecordTextureTransition(CommandBuffer));
        CommandBuffer.EndRecording();
        CommandBuffer.SubmitAndWait(VkCtx, queue);
        CommandBuffer.cleanup(VkCtx, CmdPool);
        TextureMap.forEach((key,value)->value.CleanUpStgBuffer(VkCtx));
        Logger.debug("Recorded Texture Transition");
    }

    public List<Texture> GetTextureList(){return new ArrayList<>(TextureMap.values());}
    public int GetPosition(String ID){
        int result = -1;
        if(ID != null){
            result = TextureMap.GetIndexOf(ID);
        }
        return result;
    }
    public void CleanUp(VulkanContext VkCtx){
        TextureMap.forEach((k,t)->t.CleanUp(VkCtx));
        TextureMap.clear();
    }
}
