#include <psemek/wgpu/instance.hpp>
#include <psemek/wgpu/detail/string_view.hpp>
#include <psemek/wgpu/external/webgpu.h>
#include <psemek/wgpu/external/wgpu.h>

namespace psemek::wgpu
{

	namespace
	{

		struct extras
		{};

		detail::chained_struct_ptr to_chained_struct(extras const &)
		{
			WGPUInstanceExtras chained = {};
			chained.chain.sType = (WGPUSType)WGPUSType_InstanceExtras;
			chained.backends = WGPUInstanceBackend_Primary;
			return detail::make_chained_struct(chained);
		}

	}

	instance instance::create(descriptor const & desc)
	{
		std::vector<chained_struct> chain = desc.chain;
		chain.push_back(extras{});

		WGPUInstanceDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(chain);
		return instance(wgpuCreateInstance(&descriptor));
	}

	void instance::process_events()
	{
		wgpuInstanceProcessEvents((WGPUInstance)get());
	}

	surface instance::create_surface(surface::descriptor const & desc)
	{
		WGPUSurfaceDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		return surface{wgpuInstanceCreateSurface((WGPUInstance)get(), &descriptor)};
	}

	void instance::request_adapter(callback_mode mode, adapter::request_options const & request_options, adapter::request_callback callback)
	{
		WGPURequestAdapterOptions options = {};
		options.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(request_options.chain);
		options.featureLevel = (WGPUFeatureLevel)request_options.feature_level;
		options.powerPreference = (WGPUPowerPreference)request_options.power_preference;
		options.forceFallbackAdapter = request_options.force_fallback_adapter;
		options.backendType = (WGPUBackendType)request_options.backend_type;
		options.compatibleSurface = (WGPUSurface)request_options.compatible_surface.get();

		WGPURequestAdapterCallbackInfo callback_info = {};
		callback_info.mode = (WGPUCallbackMode)mode;
		callback_info.callback = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void * userdata, void *)
		{
			std::unique_ptr<adapter::request_callback> callback{(adapter::request_callback *)userdata};
			if (*callback) (*callback)(adapter::request_status{status}, wgpu::adapter(adapter), std::string_view(message.data, message.length));
		};
		callback_info.userdata1 = new adapter::request_callback(callback);

		wgpuInstanceRequestAdapter((WGPUInstance)get(), &options, callback_info);
	}

	void instance::reference(void * ptr)
	{
		wgpuInstanceAddRef((WGPUInstance)ptr);
	}

	void instance::release(void * ptr)
	{
		wgpuInstanceRelease((WGPUInstance)ptr);
	}

}
