#pragma once

#include <psemek/wgpu/shader_stage.hpp>
#include <psemek/wgpu/chained_struct.hpp>
#include <psemek/wgpu/pipeline_layout.hpp>
#include <psemek/wgpu/callback_mode.hpp>
#include <psemek/wgpu/detail/object.hpp>
#include <psemek/util/span.hpp>

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

namespace psemek::wgpu
{

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

		enum class compilation_info_request_status : std::uint32_t
		{
			success            = 0x00000001,
			callback_cancelled = 0x00000002,
		};

		enum class compilation_message_type : std::uint32_t
		{
			error   = 0x00000001,
			warning = 0x00000002,
			info    = 0x00000003,
		};

		struct compilation_message
		{
			std::vector<chained_struct> chain = {};
			std::string message = {};
			compilation_message_type type;
			std::uint64_t line_num;
			std::uint64_t line_pos;
			std::uint64_t offset;
			std::uint64_t length;
		};

		struct compilation_info
		{
			std::vector<chained_struct> chain = {};
			std::vector<compilation_message> messages = {};
		};

		struct define
		{
			std::string_view name;
			std::string_view value;
		};

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

		struct spirv_descriptor
		{
			util::span<std::uint32_t const> code;
		};

		struct wgsl_descriptor
		{
			std::string_view code;
		};

		struct glsl_descriptor
		{
			shader_stage stage;
			std::string_view code;
			std::vector<define> defines = {};
		};

		enum class runtime_checks : std::uint64_t
		{
			bounds_checks = 0x0000000000000001,
			force_loop_bounding = 0x0000000000000002,
			ray_query_initialization_tracking = 0x0000000000000004,
			task_shader_dispatch_tracking = 0x0000000000000008,
			mesh_shader_primitive_indices_clamp = 0x0000000000000010,
		};

		using compilation_info_callback = std::function<void(compilation_info_request_status, compilation_info const &)>;

		void get_compilation_info(callback_mode mode, compilation_info_callback const & callback) const;
		void set_label(std::string const & label);

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

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

		friend struct device;
	};

	inline shader_module::runtime_checks operator | (shader_module::runtime_checks c1, shader_module::runtime_checks c2)
	{
		return static_cast<shader_module::runtime_checks>(static_cast<std::uint64_t>(c1) | static_cast<std::uint64_t>(c2));
	}

	detail::chained_struct_ptr to_chained_struct(shader_module::spirv_descriptor const & value);
	detail::chained_struct_ptr to_chained_struct(shader_module::wgsl_descriptor const & value);
	detail::chained_struct_ptr to_chained_struct(shader_module::glsl_descriptor && value);

}
