#pragma once

#include "Gpu.hpp"
#include "props.hpp"
#include "resources/Image.hpp"

#include <vector>
#include <volk.h>

struct ImageResourceLayout;

class Swapchain {
private:
	const Gpu* m_gpu;
	VkSurfaceKHR m_surface;
	VkSwapchainKHR m_swapchain;

	VkSurfaceFormatKHR m_format;
	VkExtent2D m_extent;

	unsigned m_numImages;
	unsigned m_numFrames;

	VkImage *m_pImages;
	VkImageView *m_pImageViews;

	VkSurfaceFormatKHR choose_format();
	VkPresentModeKHR choose_present_mode();
	VkExtent2D choose_extent(VkExtent2D actualExtent = {0, 0});

	ResultCode create_swapchain();

	ResultCode get_images(unsigned *pOutCount, VkImage *pOut);

	ResultCode create_views();

	std::vector<Image> m_images;
	std::vector<ImageView> m_views;

public:
	GET(m_format, format);
	GET(m_swapchain, swapchain);
	GET(m_extent, extent);
	GET(m_numImages, num_images);
	REF(m_images, images);
	REF(m_views, views);
    GET(m_surface, surface);

	const Image* get_image(uint32_t idx) const {
		if(idx >= m_numImages) {
			return nullptr;
		}

		return &m_images[idx];
	}

    VkResult get_next_image_idx(VkSemaphore signalSemaphore, VkFence signalFence, uint32_t *pOutImageIdx) const {
        return vkAcquireNextImageKHR(m_gpu->dev(), m_swapchain, UINT64_MAX, signalSemaphore, signalFence, pOutImageIdx);
    }

    void present(std::vector<VkSemaphore> waitSemaphores,
                 uint32_t imageIdx) const {
        VkPresentInfoKHR presentInfo = {};
		presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
		presentInfo.waitSemaphoreCount = (uint32_t)waitSemaphores.size();
		presentInfo.pWaitSemaphores = waitSemaphores.data();
		presentInfo.swapchainCount = 1;
		presentInfo.pSwapchains = &m_swapchain;
		presentInfo.pImageIndices = &imageIdx;

        m_gpu->enqueue_present(&presentInfo);
    }

    void cleanup();

    void resize(VkExtent2D size);

	Swapchain(const Gpu* gpu, VkExtent2D extent, Surface* surface);
};
