use {
	crate::{
		prelude::*,
		pw::{self, FromPipewirePropertyString, PipewireObject, Proxy, ProxyFeatures},
		spa::SpaPod,
	},
	pipewire_sys::pw_proxy,
};
impl ProxyFeatures {
	#[doc(alias = "WP_PIPEWIRE_OBJECT_FEATURES_ALL")]
	pub const ALL: Self = Self::from_bits_retain(ffi::WP_PIPEWIRE_OBJECT_FEATURES_ALL as u32);
	#[doc(alias = "WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL")]
	pub const MINIMAL: Self = Self::from_bits_retain(ffi::WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL as u32);
}
pub trait ProxyExt2: 'static {
	#[doc(alias = "wp_proxy_get_pw_proxy")]
	#[doc(alias = "get_pw_proxy")]
	fn pw_proxy_raw(&self) -> *mut pw_proxy;
	#[doc(alias = "wp_proxy_set_pw_proxy")]
	fn set_pw_proxy_raw(&self, proxy: *mut pw_proxy);
	}
impl<O: IsA<Proxy>> ProxyExt2 for O {
	fn pw_proxy_raw(&self) -> *mut pw_proxy {
		unsafe { ffi::wp_proxy_get_pw_proxy(self.as_ref().to_glib_none().0) }
	}
	fn set_pw_proxy_raw(&self, proxy: *mut pw_proxy) {
		unsafe { ffi::wp_proxy_set_pw_proxy(self.as_ref().to_glib_none().0, proxy) }
	}
}
pub trait PipewireObjectExt2: 'static {
	#[doc(alias = "wp_pipewire_object_get_native_info")]
	#[doc(alias = "get_native_info")]
	fn native_info(&self) -> gconstpointer;
	fn object_id(&self) -> Result<u32, Error>;
	#[doc(alias = "wp_pipewire_object_get_property")]
	#[doc(alias = "get_property")]
	fn get_pw_property(&self, key: &str) -> Option<String>;
	fn get_pw_property_cstring(&self, key: &str) -> Option<CString>;
	fn with_pw_property_cstr<R, F: FnOnce(&CStr) -> R>(&self, key: &str, f: F) -> Option<R>;
	fn with_pw_property<R, F: FnOnce(&str) -> R>(&self, key: &str, f: F) -> Option<R>;
	#[doc(alias = "wp_pipewire_object_get_property")]
	#[doc(alias = "get_property")]
	fn pw_property<T: FromPipewirePropertyString>(&self, key: &str) -> Result<T, Error>;
	fn pw_property_optional<T: FromPipewirePropertyString>(&self, key: &str) -> Result<Option<T>, Error>;
	#[doc(alias = "wp_pipewire_object_enum_params")]
	fn params_future(
		&self,
		id: Option<&str>,
		filter: Option<&SpaPod>,
	) -> Pin<Box<dyn Future<Output = Result<ValueIterator<SpaPod>, Error>> + 'static>>;
}
impl<O: IsA<PipewireObject>> PipewireObjectExt2 for O {
	fn native_info(&self) -> gconstpointer {
		unsafe { ffi::wp_pipewire_object_get_native_info(self.as_ref().to_glib_none().0) }
	}
	fn object_id(&self) -> Result<u32, Error> {
		self.pw_property(pw::PW_KEY_OBJECT_ID)
	}
	fn get_pw_property_cstring(&self, key: &str) -> Option<CString> {
		self.with_pw_property_cstr(key, |cstr| cstr.to_owned())
	}
	fn get_pw_property(&self, key: &str) -> Option<String> {
		self.with_pw_property(key, |str| str.to_owned())
	}
	fn with_pw_property_cstr<R, F: FnOnce(&CStr) -> R>(&self, key: &str, f: F) -> Option<R> {
		let this = self.as_ref();
		unsafe {
			let value = ffi::wp_pipewire_object_get_property(this.to_glib_none().0, key.to_glib_none().0);
			if value.is_null() {
				None
			} else {
				Some(f(CStr::from_ptr(value)))
			}
		}
	}
	fn with_pw_property<R, F: FnOnce(&str) -> R>(&self, key: &str, f: F) -> Option<R> {
		let this = self.as_ref();
		self
			.with_pw_property_cstr(key, move |cstr| match cstr.to_str() {
				Err(e) => {
					wp_warning!(self: this, "pw_property {key} ({cstr:?}) was not valid UTF-8: {e:?}");
					None
				},
				Ok(str) => Some(f(str)),
			})
			.flatten()
	}
	fn pw_property<T: FromPipewirePropertyString>(&self, key: &str) -> Result<T, Error> {
		match self.with_pw_property(key, T::from_pipewire_string) {
			None => Err(Error::new(
				LibraryErrorEnum::InvalidArgument,
				&format!("pw_property {key} not found"),
			)),
			Some(Err(e)) => Err(Error::new(
				LibraryErrorEnum::InvalidArgument,
				&format!("pw_property {key} failed to parse: {e:?}"),
			)),
			Some(Ok(v)) => Ok(v),
		}
	}
	fn pw_property_optional<T: FromPipewirePropertyString>(&self, key: &str) -> Result<Option<T>, Error> {
		self
			.with_pw_property(key, T::from_pipewire_string)
			.transpose()
			.map_err(|e| {
				Error::new(
					LibraryErrorEnum::InvalidArgument,
					&format!("pw_property {key} failed to parse: {e:?}"),
				)
			})
	}
	fn params_future(
		&self,
		id: Option<&str>,
		filter: Option<&SpaPod>,
	) -> Pin<Box<dyn Future<Output = Result<ValueIterator<SpaPod>, Error>> + 'static>> {
		let res = self.enum_params_future(id, filter);
		Box::pin(async move { res.await.map(|r| ValueIterator::with_inner(r.unwrap())) })
	}
}
impl Display for PipewireObject {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		if let Some(res) = self.with_pw_property(pw::PW_KEY_OBJECT_PATH, |path| f.write_str(path)) {
			return res
		}
		f.write_str("pw.object")?;
		self
			.with_pw_property(pw::PW_KEY_OBJECT_ID, |id| write!(f, "({id})"))
			.unwrap_or(Ok(()))
	}
}