use {
	crate::{
		prelude::*,
		spa::{SpaIdTable, SpaType},
	},
	ffi::WpSpaType,
};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct SpaIdValue {
	inner: NonNull<libc::c_void>,
}
impl SpaIdValue {
	pub fn number(&self) -> WpSpaType {
		unsafe { ffi::wp_spa_id_value_number(self.into_glib()) }
	}
	pub fn name(&self) -> Option<String> {
		unsafe { from_glib_none(ffi::wp_spa_id_value_name(self.into_glib())) }
	}
	pub fn short_name(&self) -> Option<String> {
		unsafe { from_glib_none(ffi::wp_spa_id_value_short_name(self.into_glib())) }
	}
	pub fn value_type(&self) -> (Option<SpaType>, Option<SpaIdTable>) {
		unsafe {
			let mut table = ptr::null();
			let res = ffi::wp_spa_id_value_get_value_type(self.into_glib(), &mut table);
			(from_glib(res), from_glib(table))
		}
	}
	pub fn array_item_type(&self) -> (Option<SpaType>, Option<SpaIdTable>) {
		unsafe {
			let mut table = ptr::null();
			let res = ffi::wp_spa_id_value_array_get_item_type(self.into_glib(), &mut table);
			(from_glib(res), from_glib(table))
		}
	}
	pub fn parse_unknown_name(id_name: &str) -> Option<WpSpaType> {
		if id_name.starts_with("id-") {
			<WpSpaType>::from_str_radix(&id_name[2..], 16).ok()
		} else {
			None
		}
	}
	pub(crate) fn value_or_name<C: Debug>(context: &C, key_name: &str, v: Option<Self>) -> Result<Self, WpSpaType> {
		let raw = match v {
			Some(v) => return Ok(v),
			None => Self::parse_unknown_name(key_name),
		};
		Err(raw.unwrap_or_else(|| {
			wp_warning!("expected to find spa key {key_name:?} of {context:?}");
			ffi::WP_SPA_TYPE_INVALID
		}))
	}
	pub fn result_number(res: Result<Self, WpSpaType>) -> WpSpaType {
		res.map(|v| v.number()).unwrap_or_else(|e| e)
	}
}
unsafe impl<'a> FromValue<'a> for SpaIdValue {
	type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>;
	unsafe fn from_value(value: &'a Value) -> Self {
		let optional: Option<Self> = from_glib(value.get::<Pointer>().unwrap() as ffi::WpSpaIdValue);
		optional.unwrap() }
}
impl fmt::Debug for SpaIdValue {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		let mut f = f.debug_struct("SpaIdValue");
		f.field("raw", &self.into_glib());
		f.field("number", &self.number());
		if let Some(name) = self.name() {
			f.field("name", &name);
		}
		f.finish()
	}
}
impl StaticType for SpaIdValue {
	fn static_type() -> Type {
		unsafe { from_glib(ffi::wp_spa_id_value_get_type()) }
	}
}
impl UnsafeFrom<NonNull<libc::c_void>> for SpaIdValue {
	unsafe fn unsafe_from(inner: NonNull<libc::c_void>) -> Self {
		Self { inner }
	}
}
impl UnsafeFrom<ffi::WpSpaIdValue> for SpaIdValue {
	unsafe fn unsafe_from(inner: ffi::WpSpaIdValue) -> Self {
		Self {
			inner: NonNull::new_unchecked(inner as *mut _),
		}
	}
}
impl TryFromGlib<ffi::WpSpaIdValue> for SpaIdValue {
	type Error = GlibNoneError;
	unsafe fn try_from_glib(val: ffi::WpSpaIdValue) -> Result<Self, Self::Error> {
		NonNull::new(val as *mut _)
			.map(|ptr| Self::unsafe_from(ptr))
			.ok_or(GlibNoneError)
	}
}
impl IntoGlib for SpaIdValue {
	type GlibType = ffi::WpSpaIdValue;
	fn into_glib(self) -> Self::GlibType {
		self.inner.as_ptr() as *const _
	}
}
impl OptionIntoGlib for SpaIdValue {
	const GLIB_NONE: ffi::WpSpaIdValue = ptr::null();
}