#pragma once

#include <psemek/wgpu/detail/object.hpp>
#include <psemek/wgpu/texture_view.hpp>
#include <psemek/wgpu/chained_struct.hpp>
#include <psemek/math/vector.hpp>

#include <cstdint>
#include <string>
#include <vector>

namespace psemek::wgpu
{

	struct texture
		: detail::object<texture>
	{
		using detail::object<texture>::object;

		enum class format : std::uint32_t
		{
			undefined            = 0x00000000,
			r8unorm              = 0x00000001,
			r8snorm              = 0x00000002,
			r8uint               = 0x00000003,
			r8sint               = 0x00000004,
			r16uint              = 0x00000005,
			r16sint              = 0x00000006,
			r16float             = 0x00000007,
			rg8unorm             = 0x00000008,
			rg8snorm             = 0x00000009,
			rg8uint              = 0x0000000a,
			rg8sint              = 0x0000000b,
			r32float             = 0x0000000c,
			r32uint              = 0x0000000d,
			r32sint              = 0x0000000e,
			rg16uint             = 0x0000000f,
			rg16sint             = 0x00000010,
			rg16float            = 0x00000011,
			rgba8unorm           = 0x00000012,
			rgba8unorm_srgb      = 0x00000013,
			rgba8snorm           = 0x00000014,
			rgba8uint            = 0x00000015,
			rgba8sint            = 0x00000016,
			bgra8unorm           = 0x00000017,
			bgra8unorm_srgb      = 0x00000018,
			rgb10a2uint          = 0x00000019,
			rgb10a2unorm         = 0x0000001a,
			rg11b10ufloat        = 0x0000001b,
			rgb9e5ufloat         = 0x0000001c,
			rg32float            = 0x0000001d,
			rg32uint             = 0x0000001e,
			rg32sint             = 0x0000001f,
			rgba16uint           = 0x00000020,
			rgba16sint           = 0x00000021,
			rgba16float          = 0x00000022,
			rgba32float          = 0x00000023,
			rgba32uint           = 0x00000024,
			rgba32sint           = 0x00000025,
			stencil8             = 0x00000026,
			depth16unorm         = 0x00000027,
			depth24plus          = 0x00000028,
			depth24plusstencil8  = 0x00000029,
			depth32float         = 0x0000002a,
			depth32floatstencil8 = 0x0000002b,
			bc1rgbaunorm         = 0x0000002c,
			bc1rgbaunorm_srgb    = 0x0000002d,
			bc2rgbaunorm         = 0x0000002e,
			bc2rgbaunorm_srgb    = 0x0000002f,
			bc3rgbaunorm         = 0x00000030,
			bc3rgbaunorm_srgb    = 0x00000031,
			bc4runorm            = 0x00000032,
			bc4rsnorm            = 0x00000033,
			bc5rgunorm           = 0x00000034,
			bc5rgsnorm           = 0x00000035,
			bc6hrgbufloat        = 0x00000036,
			bc6hrgbfloat         = 0x00000037,
			bc7rgbaunorm         = 0x00000038,
			bc7rgbaunorm_srgb    = 0x00000039,
			etc2rgb8unorm        = 0x0000003a,
			etc2rgb8unorm_srgb   = 0x0000003b,
			etc2rgb8a1unorm      = 0x0000003c,
			etc2rgb8a1unorm_srgb = 0x0000003d,
			etc2rgba8unorm       = 0x0000003e,
			etc2rgba8unorm_srgb  = 0x0000003f,
			eacr11unorm          = 0x00000040,
			eacr11snorm          = 0x00000041,
			eacrg11unorm         = 0x00000042,
			eacrg11snorm         = 0x00000043,
			astc4x4unorm         = 0x00000044,
			astc4x4unorm_srgb    = 0x00000045,
			astc5x4unorm         = 0x00000046,
			astc5x4unorm_srgb    = 0x00000047,
			astc5x5unorm         = 0x00000048,
			astc5x5unorm_srgb    = 0x00000049,
			astc6x5unorm         = 0x0000004a,
			astc6x5unorm_srgb    = 0x0000004b,
			astc6x6unorm         = 0x0000004c,
			astc6x6unorm_srgb    = 0x0000004d,
			astc8x5unorm         = 0x0000004e,
			astc8x5unorm_srgb    = 0x0000004f,
			astc8x6unorm         = 0x00000050,
			astc8x6unorm_srgb    = 0x00000051,
			astc8x8unorm         = 0x00000052,
			astc8x8unorm_srgb    = 0x00000053,
			astc10x5unorm        = 0x00000054,
			astc10x5unorm_srgb   = 0x00000055,
			astc10x6unorm        = 0x00000056,
			astc10x6unorm_srgb   = 0x00000057,
			astc10x8unorm        = 0x00000058,
			astc10x8unorm_srgb   = 0x00000059,
			astc10x10unorm       = 0x0000005a,
			astc10x10unorm_srgb  = 0x0000005b,
			astc12x10unorm       = 0x0000005c,
			astc12x10unorm_srgb  = 0x0000005d,
			astc12x12unorm       = 0x0000005e,
			astc12x12unorm_srgb  = 0x0000005f,

			// texture_format16bit_norm
			r16unorm             = 0x00030001,
			r16snorm             = 0x00030002,
			rg16unorm            = 0x00030003,
			rg16snorm            = 0x00030004,
			rgba16unorm          = 0x00030005,
			rgba16snorm          = 0x00030006,

			// texture_format_nv12
			nv12                 = 0x00030007,
			p010                 = 0x00030008,
		};

		enum class usage : std::uint64_t
		{
			none              = 0x00000000,
			copy_src          = 0x00000001,
			copy_dst          = 0x00000002,
			texture_binding   = 0x00000004,
			storage_binding   = 0x00000008,
			render_attachment = 0x00000010,
		};

		enum class dimension : std::uint32_t
		{
			undefined = 0x00000000,
			_1d       = 0x00000001,
			_2d       = 0x00000002,
			_3d       = 0x00000003,
		};

		enum class aspect : std::uint32_t
		{
			undefined    = 0x00000000,
			all          = 0x00000001,
			stencil_only = 0x00000002,
			depth_only   = 0x00000003,
		};

		enum class sample_type : std::uint32_t
		{
			binding_not_used   = 0x00000000,
			undefined          = 0x00000001,
			_float             = 0x00000002,
			unfilterable_float = 0x00000003,
			depth              = 0x00000004,
			sint               = 0x00000005,
			uint               = 0x00000006,
		};

		struct descriptor
		{
			std::vector<chained_struct> chain = {};
			std::string label = {};
			enum usage usage;
			enum dimension dimension;
			math::vector<std::uint32_t, 3> size;
			enum format format;
			std::uint32_t mip_level_count = 1;
			std::uint32_t sample_count = 1;
			std::vector<enum format> view_formats = {};
		};

		texture_view create_view(texture_view::descriptor const & desc);
		void destroy();
		std::uint32_t get_width() const;
		std::uint32_t get_height() const;
		std::uint32_t get_depth() const;
		dimension get_dimension() const;
		format get_format() const;
		std::uint32_t get_mip_level_count() const;
		usage get_usage() const;
		void set_label(std::string const & label);

		static void reference(void * ptr);
		static void release(void * ptr);

	private:
		explicit texture(void * ptr)
			: detail::object<texture>(ptr)
		{}

		friend struct surface;
		friend struct device;
	};

	struct texture_view::descriptor
	{
		std::vector<chained_struct> chain = {};
		std::string label = {};
		texture::format format;
		texture_view::dimension dimension;
		std::uint32_t base_mip_level = 0;
		std::uint32_t mip_level_count = 1;
		std::uint32_t base_array_layer = 0;
		std::uint32_t array_layer_count = 1;
		texture::aspect aspect = texture::aspect::all;
	};

	inline texture::usage operator | (texture::usage u1, texture::usage u2)
	{
		return static_cast<texture::usage>(static_cast<unsigned int>(u1) | static_cast<unsigned int>(u2));
	}

	std::size_t memory_usage(std::uint32_t width, std::uint32_t height, std::uint32_t depth, texture::format format);
	std::size_t memory_usage(texture const & texture);

}
