fn optional_features()->wgpu::Features{
	wgpu::Features::TEXTURE_COMPRESSION_ASTC
	|wgpu::Features::TEXTURE_COMPRESSION_ETC2
}
fn required_features()->wgpu::Features{
	wgpu::Features::TEXTURE_COMPRESSION_BC
}
fn required_downlevel_capabilities()->wgpu::DownlevelCapabilities{
	wgpu::DownlevelCapabilities{
		flags:wgpu::DownlevelFlags::empty(),
		shader_model:wgpu::ShaderModel::Sm5,
		..wgpu::DownlevelCapabilities::default()
	}
}

pub mod step1{
	pub fn create_instance()->wgpu::Instance{
		Default::default()
	}
}

pub mod step2{
	pub fn create_surface<'window>(instance:&wgpu::Instance,target:impl Into<wgpu::SurfaceTarget<'window>>)->Result<wgpu::Surface<'window>,wgpu::CreateSurfaceError>{
		instance.create_surface(target)
	}
}

pub mod step3{
	pub async fn pick_adapter(instance:&wgpu::Instance,surface:&wgpu::Surface<'_>)->Option<wgpu::Adapter>{
		let backends=wgpu::Backends::from_env().unwrap_or_default();
		//TODO: prefer adapter that implements optional features
		//let optional_features=optional_features();
		let required_features=super::required_features();
		let required_downlevel_capabilities=super::required_downlevel_capabilities();

		//no helper function smh gotta write it myself
		let adapters=instance.enumerate_adapters(backends).await;

		let adapter=adapters.into_iter()
			// reverse because we want to select adapters that appear first in ties,
			// and max_by_key selects the last equal element in the iterator.
			.rev()
			.filter(|adapter|
				adapter.is_surface_supported(surface)
				&&adapter.features().contains(required_features)
				&&{
					let downlevel_capabilities=adapter.get_downlevel_capabilities();
					downlevel_capabilities.shader_model>=required_downlevel_capabilities.shader_model
					&&downlevel_capabilities.flags.contains(required_downlevel_capabilities.flags)
				}
			)
			.max_by_key(|adapter|match adapter.get_info().device_type{
				wgpu::DeviceType::IntegratedGpu=>3,
				wgpu::DeviceType::DiscreteGpu=>4,
				wgpu::DeviceType::VirtualGpu=>2,
				wgpu::DeviceType::Other|wgpu::DeviceType::Cpu=>1,
			})?;

		Some(adapter)
	}
}

pub mod step4{
	pub async fn request_device(adapter:&wgpu::Adapter,limits:wgpu::Limits)->Result<(wgpu::Device,wgpu::Queue),wgpu::RequestDeviceError>{
		let optional_features=super::optional_features();
		let required_features=super::required_features();

		// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface.
		let needed_limits=limits.using_resolution(adapter.limits());

		let (device, queue)=adapter
			.request_device(
				&wgpu::DeviceDescriptor{
					label:None,
					required_features:(optional_features&adapter.features())|required_features,
					required_limits:needed_limits,
					memory_hints:wgpu::MemoryHints::Performance,
					trace:wgpu::Trace::Off,
					experimental_features:wgpu::ExperimentalFeatures::disabled(),
				},
			).await?;

		Ok((
			device,
			queue,
		))
	}
}

pub mod step5{
	use crate::surface::Surface;
	#[derive(Debug)]
	pub struct ErrorSurfaceNotSupported;
	impl std::fmt::Display for ErrorSurfaceNotSupported{
		fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
			write!(f,"Surface isn't supported by the adapter.")
		}
	}
	pub fn configure_surface<'window>(
		adapter:&wgpu::Adapter,
		device:&wgpu::Device,
		surface:wgpu::Surface<'window>,
		(width,height):(u32,u32),
	)->Result<Surface<'window>,ErrorSurfaceNotSupported>{
		let mut config=surface
			.get_default_config(adapter, width, height)
			.ok_or(ErrorSurfaceNotSupported)?;

		let surface_view_format=config.format.add_srgb_suffix();
		config.view_formats.push(surface_view_format);
		config.present_mode=wgpu::PresentMode::AutoNoVsync;
		surface.configure(device,&config);

		Ok(Surface::new(surface,config))
	}
}
