#include <psemek/wgpu/surface.hpp>
#include <psemek/wgpu/adapter.hpp>
#include <psemek/wgpu/detail/string_view.hpp>
#include <psemek/wgpu/detail/status.hpp>
#include <psemek/wgpu/external/webgpu.h>
#include <psemek/wgpu/external/wgpu.h>

namespace psemek::wgpu
{

	void surface::configure(configuration const & conf)
	{
		WGPUSurfaceConfiguration config = {};

		config.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(conf.chain);
		config.device = (WGPUDevice)conf.device.get();
		config.format = (WGPUTextureFormat)conf.format;
		config.usage = (WGPUTextureUsage)conf.usage;
		config.viewFormatCount = conf.view_formats.size();
		config.viewFormats = (WGPUTextureFormat const *)conf.view_formats.data();
		config.alphaMode = (WGPUCompositeAlphaMode)conf.alpha_mode;
		config.width = conf.width;
		config.height = conf.height;
		config.presentMode = (WGPUPresentMode)conf.present_mode;
		wgpuSurfaceConfigure((WGPUSurface)get(), &config);
	}

	surface::capabilities surface::get_capabilities(adapter const & adapter) const
	{
		WGPUSurfaceCapabilities caps = {};
		detail::check_status("wgpuSurfaceGetCapabilities", wgpuSurfaceGetCapabilities((WGPUSurface)get(), (WGPUAdapter)adapter.get(), &caps));

		capabilities result;
		// TODO: support out chain
		result.usage = (texture::usage)caps.usages;
		result.formats.assign((wgpu::texture::format *)(caps.formats), (wgpu::texture::format *)(caps.formats) + caps.formatCount);
		result.present_modes.assign((present_mode *)(caps.presentModes), (present_mode *)(caps.presentModes) + caps.presentModeCount);
		result.alpha_modes.assign((composite_alpha_mode *)(caps.alphaModes), (composite_alpha_mode *)(caps.alphaModes) + caps.alphaModeCount);

		wgpuSurfaceCapabilitiesFreeMembers(caps);

		return result;
	}

	surface::current_texture surface::get_current_texture() const
	{
		WGPUSurfaceTexture tex;
		wgpuSurfaceGetCurrentTexture((WGPUSurface)get(), &tex);

		return {
			.texture = wgpu::texture(tex.texture),
			.status = (enum current_texture::status)tex.status,
		};
	}

	void surface::present()
	{
		detail::check_status("wgpuSurfacePresent", wgpuSurfacePresent((WGPUSurface)get()));
	}

	void surface::unconfigure()
	{
		wgpuSurfaceUnconfigure((WGPUSurface)get());
	}

	void surface::reference(void * ptr)
	{
		wgpuSurfaceAddRef((WGPUSurface)ptr);
	}

	void surface::release(void * ptr)
	{
		wgpuSurfaceRelease((WGPUSurface)ptr);
	}

	detail::chained_struct_ptr to_chained_struct(surface::configuration::extras const & value)
	{
		WGPUSurfaceConfigurationExtras chained = {};
		chained.chain.sType = (WGPUSType)WGPUSType_SurfaceConfigurationExtras;
		chained.desiredMaximumFrameLatency = value.desired_maximum_frame_latency;
		return detail::make_chained_struct(chained);
	}

	detail::chained_struct_ptr to_chained_struct(surface::from_android_native_window const & value)
	{
		WGPUSurfaceSourceAndroidNativeWindow chained = {};
		chained.chain.sType = WGPUSType_SurfaceSourceAndroidNativeWindow;
		chained.window = value.window;
		return detail::make_chained_struct(chained);
	}

	detail::chained_struct_ptr to_chained_struct(surface::from_metal_layer const & value)
	{
		WGPUSurfaceSourceMetalLayer chained = {};
		chained.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
		chained.layer = value.layer;
		return detail::make_chained_struct(chained);
	}

	detail::chained_struct_ptr to_chained_struct(surface::from_wayland_surface const & value)
	{
		WGPUSurfaceSourceWaylandSurface chained = {};
		chained.chain.sType = WGPUSType_SurfaceSourceWaylandSurface;
		chained.display = value.display;
		chained.surface = value.surface;
		return detail::make_chained_struct(chained);
	}

	detail::chained_struct_ptr to_chained_struct(surface::from_windows_hwnd const & value)
	{
		WGPUSurfaceSourceWindowsHWND chained = {};
		chained.chain.sType = WGPUSType_SurfaceSourceWindowsHWND;
		chained.hinstance = value.hinstance;
		chained.hwnd = value.hwnd;
		return detail::make_chained_struct(chained);
	}

	detail::chained_struct_ptr to_chained_struct(surface::from_xcb_window const & value)
	{
		WGPUSurfaceSourceXCBWindow chained = {};
		chained.chain.sType = WGPUSType_SurfaceSourceXCBWindow;
		chained.connection = value.connection;
		chained.window = value.window;
		return detail::make_chained_struct(chained);
	}

	detail::chained_struct_ptr to_chained_struct(surface::from_xlib_window const & value)
	{
		WGPUSurfaceSourceXlibWindow chained = {};
		chained.chain.sType = WGPUSType_SurfaceSourceXlibWindow;
		chained.display = value.display;
		chained.window = value.window;
		return detail::make_chained_struct(chained);
	}

}
