#pragma once

#include "../VulkanAPI.h"
#include "../../Common/TextureUtils.h"

#include "commands.h"
#include "device.h"
#include "ex.h"
#include "memory.h"
#include "unique_resource.h"

#include <stack>

//using enum rsx::format_class;
using namespace ::rsx::format_class_;

#define VK_DISABLE_COMPONENT_SWIZZLE 0

namespace vk
{
	enum : u32// special remap_encoding enums
	{
		VK_REMAP_IDENTITY = 0xCAFEBABE,                   // Special view encoding to return an identity image view
		VK_REMAP_VIEW_MULTISAMPLED = 0xDEADBEEF,          // Special encoding for multisampled images; returns a multisampled image view
		VK_IMAGE_CREATE_ALLOW_NULL_RPCS3 = 0x80000000,    // Special flag that allows null images to be created if there is no memory
		VK_IMAGE_CREATE_SHAREABLE_RPCS3  = 0x40000000,    // Special flag to create a shareable image

		VK_IMAGE_CREATE_SPECIAL_FLAGS_RPCS3 = (VK_IMAGE_CREATE_ALLOW_NULL_RPCS3 | VK_IMAGE_CREATE_SHAREABLE_RPCS3)
	};

	class image : public unique_resource
	{
		std::stack<VkImageLayout> m_layout_stack;
		VkImageAspectFlags m_storage_aspect = 0;

		rsx::format_class m_format_class = RSX_FORMAT_CLASS_UNDEFINED;
		std::string m_debug_name;

		void validate(const vk::render_device& dev, const VkImageCreateInfo& info) const;

	protected:
		image() = default;
		void create_impl(const vk::render_device& dev, u32 access_flags, const memory_type_info& memory_type, vmm_allocation_pool allocation_pool);

	public:
		VkImage value = VK_NULL_HANDLE;
		VkComponentMapping native_component_map = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
		VkImageLayout current_layout = VK_IMAGE_LAYOUT_UNDEFINED;
		u32 current_queue_family = VK_QUEUE_FAMILY_IGNORED;
		VkImageCreateInfo info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
		std::shared_ptr<vk::memory_block> memory;

		image(const vk::render_device& dev,
			const memory_type_info& memory_type,
			u32 access_flags,
			VkImageType image_type,
			const VkFormatEx& format,
			u32 width, u32 height, u32 depth,
			u32 mipmaps, u32 layers,
			VkSampleCountFlagBits samples,
			VkImageLayout initial_layout,
			VkImageTiling tiling,
			VkImageUsageFlags usage,
			VkImageCreateFlags image_flags,
			vmm_allocation_pool allocation_pool,
			rsx::format_class format_class = RSX_FORMAT_CLASS_UNDEFINED);

		virtual ~image();

		image(const image&) = delete;
		image(image&&) = delete;

		// Identifiers
		VkImage handle() const { return value; }

		// Properties
		u32 width() const;
		u32 height() const;
		u32 depth() const;
		u32 mipmaps() const;
		u32 layers() const;
		u8 samples() const;
		VkFormat format() const;
		VkImageType type() const;
		VkSharingMode sharing_mode() const;
		VkImageAspectFlags aspect() const;
		rsx::format_class format_class() const;
		std::string debug_name() const;

		// Pipeline management
		void push_layout(const command_buffer& cmd, VkImageLayout layout);
		void push_barrier(const command_buffer& cmd, VkImageLayout layout);
		void pop_layout(const command_buffer& cmd);
		void change_layout(const command_buffer& cmd, VkImageLayout new_layout);

		// Queue transfer
		void queue_acquire(const command_buffer& cmd, VkImageLayout new_layout);
		void queue_release(const command_buffer& src_queue_cmd, u32 dst_queue_family, VkImageLayout new_layout);

		// Debug utils
		void set_debug_name(const std::string& name);

	protected:
		VkDevice m_device = VK_NULL_HANDLE;
	};

	struct image_view
	{
		VkImageView value = VK_NULL_HANDLE;
		VkImageViewCreateInfo info = {};

		image_view(VkDevice dev, VkImage image, VkImageViewType view_type, VkFormat format, VkComponentMapping mapping, VkImageSubresourceRange range);
		image_view(VkDevice dev, VkImageViewCreateInfo create_info);

		image_view(VkDevice dev, vk::image* resource,
			VkFormat format = VK_FORMAT_UNDEFINED,
			VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_MAX_ENUM,
			const VkComponentMapping& mapping = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A },
			const VkImageSubresourceRange& range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });

		~image_view();

		vk::image_view* as(VkFormat new_format);

		u32 encoded_component_map() const;
		vk::image* image() const;

		image_view(const image_view&) = delete;
		image_view(image_view&&) = delete;

		VkFormat format() const { return info.format; }

	private:
		std::unordered_map<VkFormat, std::unique_ptr<vk::image_view>> m_subviews;

		VkDevice m_device;
		vk::image* m_resource = nullptr;
		vk::image_view* m_root_view = nullptr;

		void create_impl();
		void set_debug_name(std::string_view name);
	};

	class viewable_image : public image
	{
	protected:
		std::unordered_map<u64, std::unique_ptr<vk::image_view>> views;
		viewable_image* clone();

	public:
		using image::image;

		virtual image_view* get_view(
			const rsx::texture_channel_remap_t& remap,
			VkImageAspectFlags mask = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT);

		void set_native_component_layout(VkComponentMapping new_layout);
	};
}
