/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
*/

module;
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
#include "vulkan/vulkan.h"
#include <assert.h>
#endif // !CRAFTER_GRAPHICS_WINDOW_DOM
export module Crafter.Graphics:VulkanTransition;
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
import std;

export namespace Crafter {
 #include "vulkan/vulkan.h"



 VkAccessFlags getAccessFlags(VkImageLayout layout)
 {
     switch (layout)
     {
         case VK_IMAGE_LAYOUT_UNDEFINED:
         case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
             return 0;
         case VK_IMAGE_LAYOUT_PREINITIALIZED:
             return VK_ACCESS_HOST_WRITE_BIT;
         case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
             return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
         case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
             return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
         case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
             return VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR;
         case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
             return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
         case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
             return VK_ACCESS_TRANSFER_READ_BIT;
         case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
             return VK_ACCESS_TRANSFER_WRITE_BIT;
         case VK_IMAGE_LAYOUT_GENERAL:
             //assert(false && "Don't know how to get a meaningful VkAccessFlags for VK_IMAGE_LAYOUT_GENERAL! Don't use it!");
             return VK_ACCESS_SHADER_WRITE_BIT;
         default:
             assert(false);
             return 0;
     }
 }
 
 VkPipelineStageFlags getPipelineStageFlags(VkImageLayout layout)
 {
     switch (layout)
     {
         case VK_IMAGE_LAYOUT_UNDEFINED:
             return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
         case VK_IMAGE_LAYOUT_PREINITIALIZED:
             return VK_PIPELINE_STAGE_HOST_BIT;
         case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
         case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
             return VK_PIPELINE_STAGE_TRANSFER_BIT;
         case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
             return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
         case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
             return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
         case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
             return VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
         case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
             return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
         case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
             return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
         case VK_IMAGE_LAYOUT_GENERAL:
             //assert(false && "Don't know how to get a meaningful VkPipelineStageFlags for VK_IMAGE_LAYOUT_GENERAL! Don't use it!");
             return VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
         default:
             assert(false);
             return 0;
     }
 }

 // Create an image memory barrier for changing the layout of
 // an image and put it into an active command buffer
 // See chapter 12.4 "Image Layout" for details
 
 void image_layout_transition(VkCommandBuffer                command_buffer,
                              VkImage                        image,
                              VkPipelineStageFlags           src_stage_mask,
                              VkPipelineStageFlags           dst_stage_mask,
                              VkAccessFlags                  src_access_mask,
                              VkAccessFlags                  dst_access_mask,
                              VkImageLayout                  old_layout,
                              VkImageLayout                  new_layout,
                              VkImageSubresourceRange const &subresource_range)
 {
     // Create an image barrier object
     VkImageMemoryBarrier image_memory_barrier{};
     image_memory_barrier.sType               = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
     image_memory_barrier.srcAccessMask       = src_access_mask;
     image_memory_barrier.dstAccessMask       = dst_access_mask;
     image_memory_barrier.oldLayout           = old_layout;
     image_memory_barrier.newLayout           = new_layout;
     image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     image_memory_barrier.image               = image;
     image_memory_barrier.subresourceRange    = subresource_range;
 
     // Put barrier inside setup command buffer
     vkCmdPipelineBarrier(command_buffer, src_stage_mask, dst_stage_mask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
 }
 
 void image_layout_transition(VkCommandBuffer                command_buffer,
                              VkImage                        image,
                              VkImageLayout                  old_layout,
                              VkImageLayout                  new_layout,
                              VkImageSubresourceRange const &subresource_range)
 {
     VkPipelineStageFlags src_stage_mask  = getPipelineStageFlags(old_layout);
     VkPipelineStageFlags dst_stage_mask  = getPipelineStageFlags(new_layout);
     VkAccessFlags        src_access_mask = getAccessFlags(old_layout);
     VkAccessFlags        dst_access_mask = getAccessFlags(new_layout);
 
     image_layout_transition(command_buffer, image, src_stage_mask, dst_stage_mask, src_access_mask, dst_access_mask, old_layout, new_layout, subresource_range);
 }
 
 // Fixed sub resource on first mip level and layer
 void image_layout_transition(VkCommandBuffer command_buffer,
                              VkImage         image,
                              VkImageLayout   old_layout,
                              VkImageLayout   new_layout)
 {
     VkImageSubresourceRange subresource_range = {};
     subresource_range.aspectMask              = VK_IMAGE_ASPECT_COLOR_BIT;
     subresource_range.baseMipLevel            = 0;
     subresource_range.levelCount              = 1;
     subresource_range.baseArrayLayer          = 0;
     subresource_range.layerCount              = 1;
     image_layout_transition(command_buffer, image, old_layout, new_layout, subresource_range);
 }
 
 void image_layout_transition(VkCommandBuffer                                                 command_buffer,
                              std::vector<std::pair<VkImage, VkImageSubresourceRange>> const &imagesAndRanges,
                              VkImageLayout                                                   old_layout,
                              VkImageLayout                                                   new_layout)
 {
     VkPipelineStageFlags src_stage_mask  = getPipelineStageFlags(old_layout);
     VkPipelineStageFlags dst_stage_mask  = getPipelineStageFlags(new_layout);
     VkAccessFlags        src_access_mask = getAccessFlags(old_layout);
     VkAccessFlags        dst_access_mask = getAccessFlags(new_layout);
 
     // Create image barrier objects
     std::vector<VkImageMemoryBarrier> image_memory_barriers;
     image_memory_barriers.reserve(imagesAndRanges.size());
     for (size_t i = 0; i < imagesAndRanges.size(); i++)
     {
         image_memory_barriers.emplace_back(VkImageMemoryBarrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
                                                                 nullptr,
                                                                 src_access_mask,
                                                                 dst_access_mask,
                                                                 old_layout,
                                                                 new_layout,
                                                                 VK_QUEUE_FAMILY_IGNORED,
                                                                 VK_QUEUE_FAMILY_IGNORED,
                                                                 imagesAndRanges[i].first,
                                                                 imagesAndRanges[i].second});
     }
 
     // Put barriers inside setup command buffer
     vkCmdPipelineBarrier(command_buffer,
                          src_stage_mask,
                          dst_stage_mask,
                          0,
                          0,
                          nullptr,
                          0,
                          nullptr,
                          static_cast<uint32_t>(image_memory_barriers.size()),
                          image_memory_barriers.data());
 }
}
#endif // !CRAFTER_GRAPHICS_WINDOW_DOM
