#include <psemek/wgpu/device.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>

#include <cstring>

namespace psemek::wgpu
{

	queue device::get_queue() const
	{
		return queue(wgpuDeviceGetQueue((WGPUDevice)get()));
	}

	bind_group device::create_bind_group(bind_group::descriptor const & desc)
	{
		std::vector<WGPUBindGroupEntry> entries;
		for (auto const & entry_in : desc.entries)
		{
			auto & entry_out = entries.emplace_back();
			entry_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.chain);
			entry_out.binding = entry_in.binding;
			entry_out.buffer = (WGPUBuffer)entry_in.buffer.get();
			entry_out.offset = entry_in.offset;
			entry_out.size = entry_in.size;
			entry_out.sampler = (WGPUSampler)entry_in.sampler.get();
			entry_out.textureView = (WGPUTextureView)entry_in.texture_view.get();
		}

		WGPUBindGroupDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		descriptor.layout = (WGPUBindGroupLayout)desc.layout.get();
		descriptor.entryCount = entries.size();
		descriptor.entries = entries.data();

		return bind_group(wgpuDeviceCreateBindGroup((WGPUDevice)get(), &descriptor));
	}

	bind_group_layout device::create_bind_group_layout(bind_group_layout::descriptor const & desc)
	{
		std::vector<WGPUBindGroupLayoutEntry> entries;
		for (auto const & entry_in : desc.entries)
		{
			auto & entry_out = entries.emplace_back();
			entry_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.chain);
			entry_out.binding = entry_in.binding;
			entry_out.visibility = (WGPUShaderStage)entry_in.visibility;
			entry_out.buffer.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.buffer.chain);
			entry_out.buffer.type = (WGPUBufferBindingType)entry_in.buffer.type;
			entry_out.buffer.hasDynamicOffset = entry_in.buffer.has_dynamic_offset;
			entry_out.buffer.minBindingSize = entry_in.buffer.min_binding_size;
			entry_out.sampler.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.sampler.chain);
			entry_out.sampler.type = (WGPUSamplerBindingType)entry_in.sampler.type;
			entry_out.texture.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.texture.chain);
			entry_out.texture.sampleType = (WGPUTextureSampleType)entry_in.texture.sample_type;
			entry_out.texture.viewDimension = (WGPUTextureViewDimension)entry_in.texture.view_dimension;
			entry_out.texture.multisampled = entry_in.texture.multisampled;
			entry_out.storageTexture.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.storage_texture.chain);
			entry_out.storageTexture.access = (WGPUStorageTextureAccess)entry_in.storage_texture.access;
			entry_out.storageTexture.format = (WGPUTextureFormat)entry_in.storage_texture.format;
			entry_out.storageTexture.viewDimension = (WGPUTextureViewDimension)entry_in.storage_texture.view_dimension;
		}

		WGPUBindGroupLayoutDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		descriptor.entryCount = entries.size();
		descriptor.entries = entries.data();

		return bind_group_layout(wgpuDeviceCreateBindGroupLayout((WGPUDevice)get(), &descriptor));
	}

	buffer device::create_buffer(buffer::descriptor const & desc)
	{
		WGPUBufferDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		descriptor.usage = (WGPUBufferUsage)desc.usage;
		descriptor.size = desc.size;
		descriptor.mappedAtCreation = desc.mapped_at_creation;
		return buffer(wgpuDeviceCreateBuffer((WGPUDevice)get(), &descriptor));
	}

	command_encoder device::create_command_encoder(command_encoder::descriptor const & desc)
	{
		WGPUCommandEncoderDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		return command_encoder(wgpuDeviceCreateCommandEncoder((WGPUDevice)get(), &descriptor));
	}

	namespace
	{

		void fill_compute_pipeline_descriptor(compute_pipeline::descriptor const & desc, std::vector<WGPUConstantEntry> & constants, WGPUComputePipelineDescriptor & descriptor)
		{
			for (auto const & constant_in : desc.compute.constants)
			{
				auto & constant_out = constants.emplace_back();
				constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain);
				constant_out.key = detail::to_string_view(constant_in.key);
				constant_out.value = constant_in.value;
			}

			descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
			descriptor.label = detail::to_string_view(desc.label);
			descriptor.layout = (WGPUPipelineLayout)desc.layout.get();
			descriptor.compute.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.compute.chain);
			descriptor.compute.module = (WGPUShaderModule)desc.compute.module.get();
			descriptor.compute.entryPoint = detail::to_string_view(desc.compute.entry_point);
			descriptor.compute.constantCount = constants.size();
			descriptor.compute.constants = constants.data();
		}

	}

	compute_pipeline device::create_compute_pipeline(compute_pipeline::descriptor const & desc)
	{
		std::vector<WGPUConstantEntry> constants;
		WGPUComputePipelineDescriptor descriptor = {};
		fill_compute_pipeline_descriptor(desc, constants, descriptor);

		return compute_pipeline(wgpuDeviceCreateComputePipeline((WGPUDevice)get(), &descriptor));
	}

	void device::create_compute_pipeline_async(callback_mode mode, compute_pipeline::descriptor const & desc, create_compute_pipeline_async_callback const & callback)
	{
		std::vector<WGPUConstantEntry> constants;
		WGPUComputePipelineDescriptor descriptor = {};
		fill_compute_pipeline_descriptor(desc, constants, descriptor);

		WGPUCreateComputePipelineAsyncCallbackInfo callback_info = {};
		callback_info.mode = (WGPUCallbackMode)mode;
		callback_info.callback = [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, WGPUStringView message, void * userdata, void *)
		{
			std::unique_ptr<create_compute_pipeline_async_callback> callback((create_compute_pipeline_async_callback *)userdata);
			if (*callback) (*callback)((create_pipeline_async_status)status, compute_pipeline(pipeline), std::string_view(message.data, message.length));
		};
		callback_info.userdata1 = new create_compute_pipeline_async_callback(callback);

		wgpuDeviceCreateComputePipelineAsync((WGPUDevice)get(), &descriptor, callback_info);
	}

	pipeline_layout device::create_pipeline_layout(pipeline_layout::descriptor const & desc)
	{
		WGPUPipelineLayoutDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		descriptor.bindGroupLayoutCount = desc.layouts.size();
		static_assert(sizeof(WGPUBindGroupLayout) == sizeof(bind_group_layout));
		descriptor.bindGroupLayouts = (WGPUBindGroupLayout const *)desc.layouts.data();
		return pipeline_layout(wgpuDeviceCreatePipelineLayout((WGPUDevice)get(), &descriptor));
	}

	query_set device::create_query_set(query_set::descriptor const & desc)
	{
		WGPUQuerySetDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		descriptor.type = (WGPUQueryType)desc.type;
		descriptor.count = desc.count;
		return query_set(wgpuDeviceCreateQuerySet((WGPUDevice)get(), &descriptor));
	}

	namespace
	{

		WGPUOptionalBool to_optional_bool(std::optional<bool> value)
		{
			return value ? (*value ? WGPUOptionalBool_True : WGPUOptionalBool_False) : WGPUOptionalBool_Undefined;
		}

		void fill_render_pipeline_descriptor(render_pipeline::descriptor const & desc,
			WGPURenderPipelineDescriptor & descriptor,
			std::vector<WGPUConstantEntry> & vertex_constants,
			std::vector<WGPUVertexBufferLayout> & vertex_buffers,
			WGPUDepthStencilState & depth_stencil_state,
			WGPUFragmentState & fragment_state,
			std::vector<WGPUConstantEntry> & fragment_constants,
			std::vector<WGPUColorTargetState> & color_targets)
		{
			descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
			descriptor.label = detail::to_string_view(desc.label);
			descriptor.layout = (WGPUPipelineLayout)desc.layout.get();

			for (auto const & constant_in : desc.vertex.constants)
			{
				auto & constant_out = vertex_constants.emplace_back();
				constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain);
				constant_out.key = detail::to_string_view(constant_in.key);
				constant_out.value = constant_in.value;
			}

			for (auto const & buffer_in : desc.vertex.buffers)
			{
				auto & buffer_out = vertex_buffers.emplace_back();
				buffer_out.arrayStride = buffer_in.array_stride;
				buffer_out.stepMode = (WGPUVertexStepMode)buffer_in.step_mode;
				buffer_out.attributeCount = buffer_in.attributes.size();
				static_assert(sizeof(WGPUVertexAttribute) == sizeof(vertex_attribute));
				buffer_out.attributes = (WGPUVertexAttribute const *)buffer_in.attributes.data();
			}

			descriptor.vertex.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.vertex.chain);
			descriptor.vertex.module = (WGPUShaderModule)desc.vertex.module.get();
			descriptor.vertex.entryPoint = detail::to_string_view(desc.vertex.entry_point);
			descriptor.vertex.constantCount = vertex_constants.size();
			descriptor.vertex.constants = vertex_constants.data();
			descriptor.vertex.bufferCount = vertex_buffers.size();
			descriptor.vertex.buffers = vertex_buffers.data();

			descriptor.primitive.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.primitive.chain);
			descriptor.primitive.topology = (WGPUPrimitiveTopology)desc.primitive.topology;
			descriptor.primitive.stripIndexFormat = (WGPUIndexFormat)desc.primitive.strip_index_format;
			descriptor.primitive.frontFace = (WGPUFrontFace)desc.primitive.front_face;
			descriptor.primitive.cullMode = (WGPUCullMode)desc.primitive.cull_mode;
			descriptor.primitive.unclippedDepth = desc.primitive.clip_depth ? 0 : 1;

			if (desc.depth_stencil)
			{
				depth_stencil_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.depth_stencil->chain);
				depth_stencil_state.format = (WGPUTextureFormat)desc.depth_stencil->format;
				depth_stencil_state.depthWriteEnabled = to_optional_bool(desc.depth_stencil->depth_write);
				depth_stencil_state.depthCompare = (WGPUCompareFunction)desc.depth_stencil->depth_compare;
				depth_stencil_state.stencilFront.compare = (WGPUCompareFunction)desc.depth_stencil->stencil_front.compare;
				depth_stencil_state.stencilFront.failOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.fail_op;
				depth_stencil_state.stencilFront.depthFailOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.depth_fail_op;
				depth_stencil_state.stencilFront.passOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.pass_op;
				depth_stencil_state.stencilBack.compare = (WGPUCompareFunction)desc.depth_stencil->stencil_back.compare;
				depth_stencil_state.stencilBack.failOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.fail_op;
				depth_stencil_state.stencilBack.depthFailOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.depth_fail_op;
				depth_stencil_state.stencilBack.passOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.pass_op;
				depth_stencil_state.stencilReadMask = desc.depth_stencil->stencil_read_mask;
				depth_stencil_state.stencilWriteMask = desc.depth_stencil->stencil_write_mask;
				depth_stencil_state.depthBias = desc.depth_stencil->depth_bias;
				depth_stencil_state.depthBiasSlopeScale = desc.depth_stencil->depth_bias_slope_scale;
				depth_stencil_state.depthBiasClamp = desc.depth_stencil->depth_bias_clamp;

				descriptor.depthStencil = &depth_stencil_state;
			}

			descriptor.multisample.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.multisample.chain);
			descriptor.multisample.count = desc.multisample.count;
			descriptor.multisample.mask = desc.multisample.mask;
			descriptor.multisample.alphaToCoverageEnabled = desc.multisample.alpha_to_coverage;

			if (desc.fragment)
			{
				for (auto const & constant_in : desc.fragment->constants)
				{
					auto & constant_out = fragment_constants.emplace_back();
					constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain);
					constant_out.key = detail::to_string_view(constant_in.key);
					constant_out.value = constant_in.value;
				}

				for (auto const & target_in : desc.fragment->targets)
				{
					auto & target_out = color_targets.emplace_back();
					target_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(target_in.chain);
					target_out.format = (WGPUTextureFormat)target_in.format;
					static_assert(sizeof(WGPUBlendState) == sizeof(blend_state));
					target_out.blend = (WGPUBlendState *)(target_in.blend ? &(*target_in.blend) : nullptr);
					target_out.writeMask = (WGPUColorWriteMask)target_in.write_mask;
				}

				fragment_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.fragment->chain);
				fragment_state.module = (WGPUShaderModule)desc.fragment->module.get();
				fragment_state.entryPoint = detail::to_string_view(desc.fragment->entry_point);
				fragment_state.constantCount = fragment_constants.size();
				fragment_state.constants = fragment_constants.data();
				fragment_state.targetCount = color_targets.size();
				fragment_state.targets = color_targets.data();

				descriptor.fragment = &fragment_state;
			}
		}

	}

	render_pipeline device::create_render_pipeline(render_pipeline::descriptor const & desc)
	{
		WGPURenderPipelineDescriptor descriptor = {};
		std::vector<WGPUConstantEntry> vertex_constants;
		std::vector<WGPUVertexBufferLayout> vertex_buffers;
		WGPUDepthStencilState depth_stencil_state = {};
		WGPUFragmentState fragment_state = {};
		std::vector<WGPUConstantEntry> fragment_constants;
		std::vector<WGPUColorTargetState> color_targets;

		fill_render_pipeline_descriptor(desc, descriptor, vertex_constants, vertex_buffers, depth_stencil_state, fragment_state, fragment_constants, color_targets);

		return render_pipeline(wgpuDeviceCreateRenderPipeline((WGPUDevice)get(), &descriptor));
	}

	void device::create_render_pipeline_async(callback_mode mode, render_pipeline::descriptor const & desc, create_render_pipeline_async_callback const & callback)
	{
		WGPURenderPipelineDescriptor descriptor = {};
		std::vector<WGPUConstantEntry> vertex_constants;
		std::vector<WGPUVertexBufferLayout> vertex_buffers;
		WGPUDepthStencilState depth_stencil_state = {};
		WGPUFragmentState fragment_state = {};
		std::vector<WGPUConstantEntry> fragment_constants;
		std::vector<WGPUColorTargetState> color_targets;

		fill_render_pipeline_descriptor(desc, descriptor, vertex_constants, vertex_buffers, depth_stencil_state, fragment_state, fragment_constants, color_targets);

		WGPUCreateRenderPipelineAsyncCallbackInfo callback_info = {};
		callback_info.mode = (WGPUCallbackMode)mode;
		callback_info.callback = [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline, WGPUStringView message, void * userdata, void *)
		{
			std::unique_ptr<create_render_pipeline_async_callback> callback((create_render_pipeline_async_callback *)userdata);
			if (*callback) (*callback)((create_pipeline_async_status)status, render_pipeline(pipeline), std::string_view(message.data, message.length));
		};
		callback_info.userdata1 = new create_render_pipeline_async_callback(callback);

		wgpuDeviceCreateRenderPipelineAsync((WGPUDevice)get(), &descriptor, callback_info);
	}

	render_bundle_encoder device::create_render_bundle_encoder(render_bundle_encoder::descriptor const & desc)
	{
		WGPURenderBundleEncoderDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		descriptor.colorFormatCount = desc.color_formats.size();
		descriptor.colorFormats = (WGPUTextureFormat const *)desc.color_formats.data();
		descriptor.depthStencilFormat = (WGPUTextureFormat)desc.depth_stencil_format;
		descriptor.sampleCount = desc.sample_count;
		descriptor.depthReadOnly = desc.depth_read_only;
		descriptor.stencilReadOnly = desc.stencil_read_only;
		return render_bundle_encoder(wgpuDeviceCreateRenderBundleEncoder((WGPUDevice)get(), &descriptor));
	}

	sampler device::create_sampler(sampler::descriptor const & desc)
	{
		WGPUSamplerDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		descriptor.addressModeU = (WGPUAddressMode)desc.address_mode_u;
		descriptor.addressModeV = (WGPUAddressMode)desc.address_mode_v;
		descriptor.addressModeW = (WGPUAddressMode)desc.address_mode_w;
		descriptor.magFilter = (WGPUFilterMode)desc.mag_filter;
		descriptor.minFilter = (WGPUFilterMode)desc.min_filter;
		descriptor.mipmapFilter = (WGPUMipmapFilterMode)desc.mipmap_filter;
		descriptor.lodMinClamp = desc.lod_clamp.min;
		descriptor.lodMaxClamp = desc.lod_clamp.max;
		descriptor.compare = (WGPUCompareFunction)desc.compare;
		descriptor.maxAnisotropy = desc.max_anisotropy;
		return sampler(wgpuDeviceCreateSampler((WGPUDevice)get(), &descriptor));
	}

	shader_module device::create_shader_module(shader_module::descriptor const & desc)
	{
		WGPUShaderModuleDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		return shader_module(wgpuDeviceCreateShaderModule((WGPUDevice)get(), &descriptor));
	}

	texture device::create_texture(texture::descriptor const & desc)
	{
		WGPUTextureDescriptor descriptor = {};
		descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain);
		descriptor.label = detail::to_string_view(desc.label);
		descriptor.usage = (WGPUTextureUsage)desc.usage;
		descriptor.dimension = (WGPUTextureDimension)desc.dimension;
		descriptor.size = {desc.size[0], desc.size[1], desc.size[2]};
		descriptor.format = (WGPUTextureFormat)desc.format;
		descriptor.mipLevelCount = desc.mip_level_count;
		descriptor.sampleCount = desc.sample_count;
		descriptor.viewFormatCount = desc.view_formats.size();
		descriptor.viewFormats = (WGPUTextureFormat *)desc.view_formats.data();
		return texture(wgpuDeviceCreateTexture((WGPUDevice)get(), &descriptor));
	}

	void device::destroy()
	{
		wgpuDeviceDestroy((WGPUDevice)get());
	}

	std::vector<feature> device::enumerate_features() const
	{
		WGPUSupportedFeatures supported_features;
		wgpuDeviceGetFeatures((WGPUDevice)get(), &supported_features);

		std::vector<feature> result((feature const *)supported_features.features, (feature const *)(supported_features.features + supported_features.featureCount));
		wgpuSupportedFeaturesFreeMembers(supported_features);
		return result;
	}

	limits device::get_limits() const
	{
		WGPULimits limits = {};
		detail::check_status("wgpuDeviceGetLimits", wgpuDeviceGetLimits((WGPUDevice)get(), &limits));

		// TODO: support out chain
		wgpu::limits result;
		result.max_texture_dimension_1D = limits.maxTextureDimension1D;
		result.max_texture_dimension_2D = limits.maxTextureDimension2D;
		result.max_texture_dimension_3D = limits.maxTextureDimension3D;
		result.max_texture_array_layers = limits.maxTextureArrayLayers;
		result.max_bind_groups = limits.maxBindGroups;
		result.max_bind_groups_plus_vertex_buffers = limits.maxBindGroupsPlusVertexBuffers;
		result.max_bindings_per_bind_group = limits.maxBindingsPerBindGroup;
		result.max_dynamic_uniform_buffers_per_pipeline_layout = limits.maxDynamicUniformBuffersPerPipelineLayout;
		result.max_dynamic_storage_buffers_per_pipeline_layout = limits.maxDynamicStorageBuffersPerPipelineLayout;
		result.max_sampled_textures_per_shader_stage = limits.maxSampledTexturesPerShaderStage;
		result.max_samplers_per_shader_stage = limits.maxSamplersPerShaderStage;
		result.max_storage_buffers_per_shader_stage = limits.maxStorageBuffersPerShaderStage;
		result.max_storage_textures_per_shader_stage = limits.maxStorageTexturesPerShaderStage;
		result.max_uniform_buffers_per_shader_stage = limits.maxUniformBuffersPerShaderStage;
		result.max_uniform_buffer_binding_size = limits.maxUniformBufferBindingSize;
		result.max_storage_buffer_binding_size = limits.maxStorageBufferBindingSize;
		result.min_uniform_buffer_offset_alignment = limits.minUniformBufferOffsetAlignment;
		result.min_storage_buffer_offset_alignment = limits.minStorageBufferOffsetAlignment;
		result.max_vertex_buffers = limits.maxVertexBuffers;
		result.max_buffer_size = limits.maxBufferSize;
		result.max_vertex_attributes = limits.maxVertexAttributes;
		result.max_vertex_buffer_array_stride = limits.maxVertexBufferArrayStride;
		result.max_inter_stage_shader_variables = limits.maxInterStageShaderVariables;
		result.max_color_attachments = limits.maxColorAttachments;
		result.max_color_attachment_bytes_per_sample = limits.maxColorAttachmentBytesPerSample;
		result.max_compute_workgroup_storage_size = limits.maxComputeWorkgroupStorageSize;
		result.max_compute_invocations_per_workgroup = limits.maxComputeInvocationsPerWorkgroup;
		result.max_compute_workgroup_size_x = limits.maxComputeWorkgroupSizeX;
		result.max_compute_workgroup_size_y = limits.maxComputeWorkgroupSizeY;
		result.max_compute_workgroup_size_z = limits.maxComputeWorkgroupSizeZ;
		result.max_compute_workgroups_per_dimension = limits.maxComputeWorkgroupsPerDimension;

		return result;
	}

	bool device::has_feature(feature feature) const
	{
		return wgpuDeviceHasFeature((WGPUDevice)get(), (WGPUFeatureName)feature);
	}

	void device::push_error_scope(error_filter filter)
	{
		wgpuDevicePushErrorScope((WGPUDevice)get(), (WGPUErrorFilter)filter);
	}

	void device::pop_error_scope(callback_mode mode, pop_error_callback const & callback)
	{
		WGPUPopErrorScopeCallbackInfo callback_info = {};
		callback_info.mode = (WGPUCallbackMode)mode;
		callback_info.callback = [](WGPUPopErrorScopeStatus status, WGPUErrorType type, WGPUStringView message, void * userdata, void *)
		{
			std::unique_ptr<pop_error_callback> callback((pop_error_callback *)userdata);
			if (*callback) (*callback)((pop_error_scope_status)status, (error_type)type, std::string_view(message.data, message.length));
		};
		callback_info.userdata1 = new pop_error_callback(callback);

		wgpuDevicePopErrorScope((WGPUDevice)get(), callback_info);
	}

	void device::set_label(std::string const & label)
	{
		wgpuDeviceSetLabel((WGPUDevice)get(), detail::to_string_view(label));
	}

	void device::reference(void * ptr)
	{
		wgpuDeviceAddRef((WGPUDevice)ptr);
	}

	void device::release(void * ptr)
	{
		wgpuDeviceRelease((WGPUDevice)ptr);
	}

	detail::chained_struct_ptr to_chained_struct(native_limits && value)
	{
		WGPUNativeLimits chained = {};
		chained.chain.sType = (WGPUSType)WGPUSType_NativeLimits;
		chained.maxPushConstantSize = value.max_push_constant_size;
		chained.maxNonSamplerBindings = value.max_non_sampler_bindings;
		return detail::make_chained_struct(chained);
	}

}
