#pragma once

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

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

namespace psemek::wgpu
{

	struct adapter;

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

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

		enum class composite_alpha_mode : std::uint32_t
		{
			_auto           = 0x00000000,
			opaque          = 0x00000001,
			premultiplied   = 0x00000002,
			unpremultiplied = 0x00000003,
			inherit         = 0x00000004,
		};

		enum class present_mode : std::uint32_t
		{
			undefined    = 0x00000000,
			fifo         = 0x00000001,
			fifo_relaxed = 0x00000002,
			immediate    = 0x00000003,
			mailbox      = 0x00000004,
		};

		struct configuration
		{
			std::vector<chained_struct> chain = {};
			wgpu::device device;
			texture::format format;
			texture::usage usage;
			std::vector<texture::format> view_formats = {};
			composite_alpha_mode alpha_mode;
			std::uint32_t width;
			std::uint32_t height;
			surface::present_mode present_mode;

			struct extras
			{
				bool desired_maximum_frame_latency;
			};
		};

		struct capabilities
		{
			std::vector<chained_struct> chain = {};
			texture::usage usage = texture::usage::none;
			std::vector<texture::format> formats = {};
			std::vector<present_mode> present_modes = {};
			std::vector<composite_alpha_mode> alpha_modes = {};
		};

		struct current_texture
		{
			enum class status : std::uint32_t
			{
				success_optimal    = 0x00000001,
				success_suboptimal = 0x00000002,
				timeout            = 0x00000003,
				outdated           = 0x00000004,
				lost               = 0x00000005,
				out_of_memory      = 0x00000006,
				device_lost        = 0x00000007,
				error              = 0x00000008,
			};

			wgpu::texture texture;
			enum status status;
		};

		void configure(configuration const & conf);
		capabilities get_capabilities(adapter const & adapter) const;
		current_texture get_current_texture() const;
		void present();
		void unconfigure();

		struct from_android_native_window
		{
			void * window;
		};

		struct from_metal_layer
		{
			void * layer;
		};

		struct from_wayland_surface
		{
			void * display;
			void * surface;
		};

		struct from_windows_hwnd
		{
			void * hinstance;
			void * hwnd;
		};

		struct from_xcb_window
		{
			void * connection;
			std::uint32_t window;
		};

		struct from_xlib_window
		{
			void * display;
			std::uint64_t window;
		};

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

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

		friend struct instance;
	};

	detail::chained_struct_ptr to_chained_struct(surface::configuration::extras const & value);
	detail::chained_struct_ptr to_chained_struct(surface::from_android_native_window const & value);
	detail::chained_struct_ptr to_chained_struct(surface::from_metal_layer const & value);
	detail::chained_struct_ptr to_chained_struct(surface::from_wayland_surface const & value);
	detail::chained_struct_ptr to_chained_struct(surface::from_windows_hwnd const & value);
	detail::chained_struct_ptr to_chained_struct(surface::from_xcb_window const & value);
	detail::chained_struct_ptr to_chained_struct(surface::from_xlib_window const & value);

}
