/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#pragma once

#include "Device.hpp"
#include "Image.hpp"
#include "FrameBuffer.hpp"
#include "Base/UI/IWindow.hpp"
#include "Base/EngineConfiguration.hpp"
#include "Base/Logger.hpp"
#include <vulkan/vulkan.hpp>

namespace OpenVulkano
{
	namespace Vulkan
	{
		struct SwapChainImage : public IImage
		{
			vk::Image image;
			vk::ImageView view;
			vk::Fence fence;

			vk::Image GetImage() override
			{
				return image;
			}

			vk::ImageView GetView() override
			{
				return view;
			}
		};

		class SwapChain final : public FrameBuffer
		{
			vk::SurfaceKHR surface;
			std::vector<SwapChainImage> images;
			Device* device = nullptr;
			IVulkanWindow* window = nullptr;
			vk::SurfaceFormatKHR surfaceFormat;
			vk::PresentModeKHR presentMode;
			std::vector<vk::Semaphore> imageAvailableSemaphores;
			uint32_t currentSemaphoreId = 0;
			vk::Extent2D size{ 0, 0 };

		public:
			vk::SwapchainKHR swapChain;

			SwapChain() = default;
			~SwapChain() override { if (device) SwapChain::Close(); }

			void Init(Device* device, vk::SurfaceKHR surface, IVulkanWindow* window);

			void Close() override;

			void Resize(uint32_t newWidth, uint32_t newHeight);

			[[nodiscard]] vk::Extent2D GetSize() const { return size; }
			
			uint32_t AcquireNextImage(const vk::Fence& fence = vk::Fence());

			vk::Fence& GetCurrentSubmitFence()
			{
				return images[currentFrameBufferId].fence;
			}

			void Present(vk::Queue& queue ,std::vector<vk::Semaphore>& semaphores) const
			{
				const vk::Result result = queue.presentKHR(vk::PresentInfoKHR(semaphores.size(), semaphores.data(),
					1, &swapChain, &currentFrameBufferId));
				if (result != vk::Result::eSuccess) [[unlikely]]
				{
					Logger::RENDER->error("Failed to preset swap chain image: {}", to_string(result));
				}
			}

			[[nodiscard]] uint32_t GetImageCount() const { return images.size(); }

			vk::Semaphore& GetCurrentSemaphore() { return imageAvailableSemaphores[currentSemaphoreId]; }

		private:
			void CreateSwapChain(vk::Extent2D size);

			void CreateSwapChainImages();

			void DestroySwapChain();

		protected:
			[[nodiscard]] vk::Format FindColorFormat() override { return surfaceFormat.format; }

			virtual vk::PresentModeKHR ChosePresentMode();

			virtual vk::SurfaceFormatKHR ChoseSurfaceFormat();


		public:
			std::vector<IImage*> GetImages() override;
		};
	}
}
