#pragma once

#include <psemek/wgpu/callback_mode.hpp>
#include <psemek/wgpu/chained_struct.hpp>
#include <psemek/wgpu/surface.hpp>
#include <psemek/wgpu/device.hpp>
#include <psemek/wgpu/detail/object.hpp>

#include <functional>
#include <cstdint>

namespace psemek::wgpu
{

	enum class feature_level : std::uint32_t
	{
		compatibility = 1,
		core          = 2,
	};

	enum class power_preference : std::uint32_t
	{
		undefined = 0x00000000,
		low       = 0x00000001,
		high      = 0x00000002,
	};

	enum class backend_type : std::uint32_t
	{
		undefined = 0x00000000,
		null      = 0x00000001,
		webgpu    = 0x00000002,
		d3d11     = 0x00000003,
		d3d12     = 0x00000004,
		metal     = 0x00000005,
		vulkan    = 0x00000006,
		opengl    = 0x00000007,
		opengles  = 0x00000008,
	};

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

		struct request_options
		{
			std::vector<chained_struct> chain = {};
			wgpu::feature_level feature_level = feature_level::core;
			wgpu::power_preference power_preference = power_preference::high;
			bool force_fallback_adapter = false;
			wgpu::backend_type backend_type = backend_type::vulkan;
			surface compatible_surface = {};
		};

		enum class request_status : std::uint32_t
		{
			success          = 0x00000001,
			instance_dropped = 0x00000002,
			unavailable      = 0x00000003,
			error            = 0x00000004,
			unknown          = 0x00000005,
		};

		using request_callback = std::function<void(request_status, adapter, std::string_view)>;

		enum class type : std::uint32_t
		{
			discrete_gpu   = 0x00000001,
			integrated_gpu = 0x00000002,
			cpu            = 0x00000003,
			unknown        = 0x00000004,
		};

		struct info
		{
			std::string_view vendor;
			std::string_view architecture;
			std::string_view device;
			std::string_view description;
			wgpu::backend_type backend_type;
			type adapter_type;
			uint32_t vendor_id;
			uint32_t device_id;
		};

		std::vector<feature> enumerate_features() const;
		limits get_limits() const;
		info get_info() const;
		bool has_feature(feature feature) const;
		void request_device(callback_mode mode, device::descriptor const & descriptor, device::request_callback const & callback);

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

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

		friend struct instance;
	};

}
