#pragma once

#include <psemek/wgpu/detail/object.hpp>
#include <psemek/wgpu/chained_struct.hpp>
#include <psemek/wgpu/shader_stage.hpp>
#include <psemek/wgpu/texture_view.hpp>
#include <psemek/wgpu/texture.hpp>

#include <string>
#include <vector>

namespace psemek::wgpu
{

	enum class buffer_binding_type : std::uint32_t
	{
		binding_not_used  = 0x00000000,
		undefined         = 0x00000001,
		uniform           = 0x00000002,
		storage           = 0x00000003,
		read_only_storage = 0x00000004,
	};

	struct buffer_binding_layout
	{
		std::vector<chained_struct> chain = {};
		buffer_binding_type type = buffer_binding_type::binding_not_used;
		bool has_dynamic_offset = false;
		std::uint64_t min_binding_size = 0;
	};

	enum class sampler_binding_type : std::uint32_t
	{
		binding_not_used  = 0x00000000,
		undefined         = 0x00000001,
		filtering         = 0x00000002,
		nonfiltering      = 0x00000003,
		comparison        = 0x00000004,
	};

	struct sampler_binding_layout
	{
		std::vector<chained_struct> chain = {};
		sampler_binding_type type = sampler_binding_type::binding_not_used;
	};

	struct texture_binding_layout
	{
		std::vector<chained_struct> chain = {};
		texture::sample_type sample_type = texture::sample_type::binding_not_used;
		texture_view::dimension view_dimension = texture_view::dimension::undefined;
		bool multisampled = false;
	};

	enum class storage_texture_access : std::uint32_t
	{
		binding_not_used  = 0x00000000,
		undefined         = 0x00000001,
		write_only        = 0x00000002,
		read_only         = 0x00000003,
		read_write        = 0x00000004,
	};

	struct storage_texture_binding_layout
	{
		std::vector<chained_struct> chain = {};
		storage_texture_access access = storage_texture_access::binding_not_used;
		texture::format format = texture::format::undefined;
		texture_view::dimension view_dimension = texture_view::dimension::undefined;
	};

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

		struct entry
		{
			std::vector<chained_struct> chain = {};
			std::uint32_t binding;
			shader_stage visibility;
			buffer_binding_layout buffer = {};
			sampler_binding_layout sampler = {};
			texture_binding_layout texture = {};
			storage_texture_binding_layout storage_texture = {};
		};

		struct descriptor
		{
			std::vector<chained_struct> chain = {};
			std::string label = {};
			std::vector<entry> entries;
		};

		void set_label(std::string const & label);

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

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

		friend struct device;
		friend struct render_pipeline;
		friend struct compute_pipeline;
	};

}
