#[cfg(feature = "serde")]
pub use self::builder::{SpaJsonChildSerializer, SpaJsonPropertySerializer, SpaJsonSerializer};
pub use {
	self::{
		builder::{BuildError, SpaJsonObjectBuilder},
		parser::{ParseError, SpaJsonObjectParser, SpaJsonParserRef},
		r#ref::SpaJsonRef,
	},
	crate::auto::{SpaJson, SpaJsonBuilder, SpaJsonParser},
};
use {
	crate::{prelude::*, spa::SpaType},
	libspa_sys::spa_json,
	std::str::from_utf8_unchecked,
};
mod builder;
mod parser;
mod r#ref;
impl SpaJson {
	#[doc(alias = "wp_spa_json_new_from_string")]
	#[doc(alias = "new_from_string")]
	#[doc(alias = "wp_spa_json_new_from_stringn")]
	#[doc(alias = "new_from_stringn")]
	pub fn from_string(json: &str) -> Self {
		match () {
			#[cfg(feature = "v0_4_10")]
			() => unsafe { Self::wrap_string(json).copy() },
			#[cfg(not(feature = "v0_4_10"))]
			() => unsafe {
				let s = GString::from(json);
				let wrapped = Self::wrap_gstr(s.as_gstr());
				wrapped.copy()
			},
		}
	}
	#[doc(alias = "wp_spa_json_new_from_string")]
	#[doc(alias = "wp_spa_json_new_from_stringn")]
	#[doc(alias = "new_from_string")]
	#[doc(alias = "new_from_stringn")]
	#[cfg(feature = "v0_4_10")]
	pub unsafe fn wrap_string(json: &str) -> Self {
		let len = json.len() as _;
		from_glib_full(ffi::wp_spa_json_new_from_stringn(json.as_ptr() as _, len))
	}
	#[doc(alias = "wp_spa_json_new_from_string")]
	#[doc(alias = "wp_spa_json_new_from_stringn")]
	#[doc(alias = "new_from_string")]
	#[doc(alias = "new_from_stringn")]
	pub unsafe fn wrap_gstr(json: &GStr) -> Self {
		from_glib_full(ffi::wp_spa_json_new_from_string(json.as_ptr()))
	}
	#[doc(alias = "wp_spa_json_new_wrap")]
	#[doc(alias = "new_wrap")]
	pub unsafe fn wrap(spa: NonNull<spa_json>) -> Self {
		from_glib_full(ffi::wp_spa_json_new_wrap(spa.as_ptr()))
	}
	#[doc(alias = "wp_spa_json_ensure_unique_owner")]
	#[doc(alias = "ensure_unique_owner")]
	pub fn make_unique(&mut self) {
		if !self.is_unique_owner() {
			let empty = unsafe { SpaJson::wrap_gstr(gstr!("")) };
			let this = mem::replace(self, empty);
			drop(mem::replace(self, this.ensure_unique_owner()))
		}
	}
	#[doc(alias = "wp_spa_json_parse_string")]
	#[doc(alias = "parse_string")]
	pub fn parse_str(&self) -> Cow<str> {
		self
			.parse_str_ptr()
			.map(|inner| unsafe { &*inner })
			.map(|inner| match self.is_unique_owner() {
				true => Cow::Borrowed(inner),
				false => Cow::Owned(String::from(inner)),
			})
			.unwrap_or_else(|| Cow::Owned(self.parse_string().into()))
	}
	#[doc(alias = "wp_spa_json_parse_string")]
	#[doc(alias = "parse_string")]
	pub fn parse_str_ptr(&self) -> Option<*const str> {
		let data = unsafe { self.data_unchecked() };
		let len = data.len();
		let inner = data.get(1..len.saturating_sub(1))?;
		if inner.contains(['\\', '"']) {
			None
		} else {
			Some(inner)
		}
	}
	#[doc(alias = "wp_spa_json_get_spa_json")]
	#[doc(alias = "get_spa_json")]
	pub fn spa_json(&self) -> *const spa_json {
		unsafe { ffi::wp_spa_json_get_spa_json(self.to_glib_none().0) }
	}
	pub fn borrow<'a>(&'a self) -> &'a SpaJsonRef<'a> {
		SpaJsonRef::with_json(self)
	}
	#[doc(alias = "wp_spa_json_get_data")]
	#[doc(alias = "get_data")]
	pub fn data(&self) -> *const str {
		let data = unsafe { ffi::wp_spa_json_get_data(self.to_glib_none().0) };
		let size = self.size();
		unsafe { from_utf8_unchecked(slice::from_raw_parts(data as *const _, size)) }
	}
	#[doc(alias = "wp_spa_json_get_data")]
	#[doc(alias = "get_data")]
	pub unsafe fn data_unchecked(&self) -> &str {
		&*self.data()
	}
	#[doc(alias = "wp_spa_json_get_data")]
	pub fn get_data(&mut self) -> &str {
		self.make_unique();
		unsafe { self.data_unchecked() }
	}
	#[doc(alias = "wp_spa_json_get_data")]
	#[doc(alias = "get_data")]
	pub fn run_with_data<R, F: FnOnce(&str) -> R>(&self, f: F) -> R {
		f(unsafe { self.data_unchecked() })
	}
	#[doc(alias = "wp_spa_json_parse_string")]
	#[doc(alias = "parse_string")]
	pub fn parse_with_str<R, F: FnOnce(Cow<str>) -> R>(&self, f: F) -> Option<R> {
		if !self.is_string() {
			return None
		};
		let s = self
			.parse_str_ptr()
			.map(|inner| Cow::Borrowed(unsafe { &*inner }))
			.unwrap_or_else(|| Cow::Owned(self.parse_string().into()));
		Some(f(s))
	}
	#[doc(alias = "wp_spa_json_get_data")]
	#[doc(alias = "wp_spa_json_to_string")]
	pub fn to_string(&self) -> String {
		String::from(unsafe { self.data_unchecked() })
	}
	#[doc(alias = "wp_spa_json_to_string")]
	#[doc(alias = "to_string")]
	#[cfg(feature = "v0_4_11")]
	pub fn to_gstring(&self) -> glib::GString {
		unsafe { from_glib_full(ffi::wp_spa_json_to_string(self.to_glib_none().0)) }
	}
	pub fn parse_char(&self) -> Result<char, ParseError> {
		let c = self.parse_with_str(|s| {
			let mut chars = s.chars();
			let c = match chars.next() {
				Some(c) => {
					let eof = chars.next().is_none();
					if eof {
						Some(c)
					} else {
						None
					}
				},
				None => None,
			};
			c.ok_or_else(|| ParseError::Char { string: s.into() })
		});
		c.ok_or_else(|| ParseError::TypeMismatch {
			expected: SpaType::STRING,
			found: self.spa_type().ok(),
		})
		.and_then(|res| res)
	}
	#[doc(alias = "wp_spa_json_is_null")]
	#[doc(alias = "is_null")]
	pub fn parse_null(&self) -> Option<()> {
		if self.is_null() {
			Some(())
		} else {
			None
		}
	}
	pub fn parse_array<'a>(&'a self) -> Option<SpaJsonParserRef<'a, 'a>> {
		self.borrow().parse_array()
	}
	pub fn parse_object<'a>(&'a self) -> Option<SpaJsonObjectParser<'a, 'a>> {
		self.borrow().parse_object()
	}
	#[doc(alias = "wp_spa_json_new_iterator")]
	pub fn parse_values(&self) -> ValueIterator<SpaJson> {
		ValueIterator::with_inner(self.new_iterator())
	}
	pub fn parse_variant(&self) -> Result<Variant, ParseError> {
		let ty = self.spa_type()?;
		match ty {
			SpaType::NONE => return Ok(().to_variant()),
			SpaType::INT => self.parse_int().map(|v| v.to_variant()),
			SpaType::FLOAT => self.parse_float().map(|v| f64::from(v).to_variant()),
			SpaType::BOOL => self.parse_boolean().map(|v| v.to_variant()),
			SpaType::STRING => return Ok(self.parse_string().to_variant()),
			SpaType::OBJECT => self.parse_object().map(|p| p.into_vardict_variant()),
			SpaType::ARRAY => self.parse_array().map(|p| p.into_variant_array()),
			_ => None,
		}
		.ok_or_else(|| ParseError::TypeMismatch {
			expected: ty,
			found: None,
		})
	}
	pub fn check_parse(&self) -> Result<(), ParseError> {
		match self.spa_type()? {
			SpaType::OBJECT => {
				let mut parser = self.parse_object().unwrap();
				while let Some((_k, value)) = parser.parse_property() {
					value.check_parse()?;
				}
				parser.parse_end()
			},
			SpaType::ARRAY => {
				let mut parser = self.parse_array().unwrap();
				while let Some(value) = parser.parse_json() {
					value.check_parse()?;
				}
				parser.parse_end()
			},
			ty @ SpaType::INT => self.parse_int().map(drop).ok_or(ParseError::TypeMismatch {
				expected: ty,
				found: None,
			}),
			ty @ SpaType::FLOAT => self.parse_int().map(drop).ok_or(ParseError::TypeMismatch {
				expected: ty,
				found: None,
			}),
			ty @ SpaType::BOOL => self.parse_boolean().map(drop).ok_or(ParseError::TypeMismatch {
				expected: ty,
				found: None,
			}),
			SpaType::STRING | SpaType::NONE => Ok(()),
			ty => Err(ParseError::TypeMismatch {
				expected: ty,
				found: Some(ty),
			}),
		}
	}
	pub fn spa_type(&self) -> Result<SpaType, ParseError> {
		Ok(if self.is_null() {
			SpaType::NONE
		} else if self.is_int() {
			SpaType::INT
		} else if self.is_float() {
			SpaType::FLOAT
		} else if self.is_string() {
			SpaType::STRING
		} else if self.is_boolean() {
			SpaType::BOOL
		} else if self.is_object() {
			SpaType::OBJECT
		} else if self.is_array() {
			SpaType::ARRAY
		} else {
			return Err(ParseError::InvalidType)
		})
	}
}
impl From<()> for SpaJson {
	fn from(_: ()) -> Self {
		Self::new_null()
	}
}
impl From<bool> for SpaJson {
	fn from(value: bool) -> Self {
		Self::new_boolean(value)
	}
}
impl From<i32> for SpaJson {
	fn from(value: i32) -> Self {
		Self::new_int(value)
	}
}
impl From<f32> for SpaJson {
	fn from(value: f32) -> Self {
		Self::new_float(value)
	}
}
impl<'a, 'j> From<&'a SpaJsonRef<'j>> for SpaJson {
	fn from(json: &'a SpaJsonRef) -> Self {
		json.copy()
	}
}
impl<'j> From<SpaJsonRef<'j>> for SpaJson {
	fn from(json: SpaJsonRef<'j>) -> Self {
		json.copy()
	}
}
impl IntoIterator for SpaJson {
	type Item = SpaJson;
	type IntoIter = ValueIterator<SpaJson>;
	fn into_iter(self) -> Self::IntoIter {
		self.parse_values()
	}
}
impl<'a> IntoIterator for &'a SpaJson {
	type Item = SpaJsonRef<'a>;
	type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
	fn into_iter(self) -> Self::IntoIter {
		Box::new(self.borrow().parse_values())
	}
}
impl ToVariant for SpaJson {
	fn to_variant(&self) -> Variant {
		self.borrow().to_variant()
	}
}
impl FromVariant for SpaJson {
	fn from_variant(variant: &Variant) -> Option<Self> {
		Self::try_from_variant(variant).ok()
	}
}
impl StaticVariantType for SpaJson {
	fn static_variant_type() -> Cow<'static, VariantTy> {
		Cow::Borrowed(VariantTy::VARIANT)
	}
}
impl Display for SpaJson {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		self.run_with_data(|data| Display::fmt(data, f))
	}
}
impl Debug for SpaJson {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		let stash: Stash<*mut ffi::WpSpaJson, Self> = self.to_glib_none();
		self.run_with_data(|data| {
			f.debug_struct("SpaJson")
				.field("inner", &stash.1)
				.field("data", &data)
				.finish()
		})
	}
}