// Take a look at the license at the top of the repository in the LICENSE file.

use std::{cmp, fmt, ops, slice};

use crate::ffi;
use glib::{prelude::*, translate::*};
use num_rational::Rational32;

#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Fraction(pub Rational32);

impl Fraction {
    // rustdoc-stripper-ignore-next
    /// Creates a new `Ratio`.
    ///
    /// # Panics
    ///
    /// Panics if `denom` is zero.
    #[inline]
    pub fn new(numer: i32, denom: i32) -> Self {
        skip_assert_initialized!();
        (numer, denom).into()
    }

    // rustdoc-stripper-ignore-next
    /// Creates a `Fraction` without checking for `denom == 0` or reducing.
    ///
    /// While this does not panic, there are several methods that will panic
    /// if used on a `Fraction` with `denom == 0`.
    #[inline]
    pub const fn new_raw(numer: i32, denom: i32) -> Self {
        skip_assert_initialized!();
        Self(Rational32::new_raw(numer, denom))
    }

    // rustdoc-stripper-ignore-next
    /// Creates a `Fraction` representing the integer `t`.
    #[inline]
    pub const fn from_integer(t: i32) -> Self {
        skip_assert_initialized!();
        Self::new_raw(t, 1)
    }

    pub fn approximate_f32(x: f32) -> Option<Self> {
        skip_assert_initialized!();
        Rational32::approximate_float(x).map(|r| r.into())
    }

    pub fn approximate_f64(x: f64) -> Option<Self> {
        skip_assert_initialized!();
        Rational32::approximate_float(x).map(|r| r.into())
    }

    #[inline]
    pub fn numer(&self) -> i32 {
        *self.0.numer()
    }

    #[inline]
    pub fn denom(&self) -> i32 {
        *self.0.denom()
    }

    #[cfg(feature = "v1_24")]
    #[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
    #[doc(alias = "gst_util_simplify_fraction")]
    pub fn simplify(&mut self, n_terms: u32, threshold: u32) {
        skip_assert_initialized!();
        unsafe {
            let mut numer = self.numer();
            let mut denom = self.denom();
            ffi::gst_util_simplify_fraction(&mut numer, &mut denom, n_terms, threshold);
            *self = Self::new(numer, denom);
        }
    }
}

impl fmt::Display for Fraction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl ops::Deref for Fraction {
    type Target = Rational32;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl ops::DerefMut for Fraction {
    #[inline]
    fn deref_mut(&mut self) -> &mut Rational32 {
        &mut self.0
    }
}

impl AsRef<Rational32> for Fraction {
    #[inline]
    fn as_ref(&self) -> &Rational32 {
        &self.0
    }
}

macro_rules! impl_fraction_binop {
    ($name:ident, $f:ident, $name_assign:ident, $f_assign:ident) => {
        impl ops::$name<Fraction> for Fraction {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: Fraction) -> Self::Output {
                Fraction((self.0).$f(other.0))
            }
        }

        impl ops::$name<Fraction> for &Fraction {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: Fraction) -> Self::Output {
                Fraction((self.0).$f(other.0))
            }
        }

        impl ops::$name<&Fraction> for Fraction {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: &Fraction) -> Self::Output {
                Fraction((self.0).$f(other.0))
            }
        }

        impl ops::$name<&Fraction> for &Fraction {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: &Fraction) -> Self::Output {
                Fraction((self.0).$f(other.0))
            }
        }

        impl ops::$name<i32> for Fraction {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: i32) -> Self::Output {
                self.$f(Fraction::from(other))
            }
        }

        impl ops::$name<i32> for &Fraction {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: i32) -> Self::Output {
                self.$f(Fraction::from(other))
            }
        }

        impl ops::$name<&i32> for Fraction {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: &i32) -> Self::Output {
                self.$f(Fraction::from(*other))
            }
        }

        impl ops::$name<&i32> for &Fraction {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: &i32) -> Self::Output {
                self.$f(Fraction::from(*other))
            }
        }

        impl ops::$name<Fraction> for i32 {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: Fraction) -> Self::Output {
                Fraction::from(self).$f(other)
            }
        }

        impl ops::$name<&Fraction> for i32 {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: &Fraction) -> Self::Output {
                Fraction::from(self).$f(other)
            }
        }

        impl ops::$name<Fraction> for &i32 {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: Fraction) -> Self::Output {
                Fraction::from(*self).$f(other)
            }
        }

        impl ops::$name<&Fraction> for &i32 {
            type Output = Fraction;

            #[inline]
            fn $f(self, other: &Fraction) -> Self::Output {
                Fraction::from(*self).$f(other)
            }
        }

        impl ops::$name_assign<Fraction> for Fraction {
            #[inline]
            fn $f_assign(&mut self, other: Fraction) {
                (self.0).$f_assign(other.0)
            }
        }

        impl ops::$name_assign<&Fraction> for Fraction {
            #[inline]
            fn $f_assign(&mut self, other: &Fraction) {
                (self.0).$f_assign(other.0)
            }
        }

        impl ops::$name_assign<i32> for Fraction {
            #[inline]
            fn $f_assign(&mut self, other: i32) {
                (self.0).$f_assign(other)
            }
        }

        impl ops::$name_assign<&i32> for Fraction {
            #[inline]
            fn $f_assign(&mut self, other: &i32) {
                (self.0).$f_assign(other)
            }
        }
    };
}

impl_fraction_binop!(Add, add, AddAssign, add_assign);
impl_fraction_binop!(Sub, sub, SubAssign, sub_assign);
impl_fraction_binop!(Div, div, DivAssign, div_assign);
impl_fraction_binop!(Mul, mul, MulAssign, mul_assign);
impl_fraction_binop!(Rem, rem, RemAssign, rem_assign);

impl ops::Neg for Fraction {
    type Output = Fraction;

    #[inline]
    fn neg(self) -> Self::Output {
        Fraction(self.0.neg())
    }
}

impl ops::Neg for &Fraction {
    type Output = Fraction;

    #[inline]
    fn neg(self) -> Self::Output {
        Fraction(self.0.neg())
    }
}

impl From<i32> for Fraction {
    #[inline]
    fn from(x: i32) -> Self {
        skip_assert_initialized!();
        Fraction(x.into())
    }
}

impl From<(i32, i32)> for Fraction {
    #[inline]
    fn from(x: (i32, i32)) -> Self {
        skip_assert_initialized!();
        Fraction(x.into())
    }
}

impl From<Fraction> for (i32, i32) {
    #[inline]
    fn from(f: Fraction) -> Self {
        skip_assert_initialized!();
        f.0.into()
    }
}

impl From<Rational32> for Fraction {
    #[inline]
    fn from(x: Rational32) -> Self {
        skip_assert_initialized!();
        Fraction(x)
    }
}

impl From<Fraction> for Rational32 {
    #[inline]
    fn from(x: Fraction) -> Self {
        skip_assert_initialized!();
        x.0
    }
}

impl glib::types::StaticType for Fraction {
    #[inline]
    fn static_type() -> glib::types::Type {
        unsafe { from_glib(ffi::gst_fraction_get_type()) }
    }
}

impl glib::value::ValueType for Fraction {
    type Type = Self;
}

unsafe impl<'a> glib::value::FromValue<'a> for Fraction {
    type Checker = glib::value::GenericValueTypeChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a glib::Value) -> Self {
        skip_assert_initialized!();
        let n = ffi::gst_value_get_fraction_numerator(value.to_glib_none().0);
        let d = ffi::gst_value_get_fraction_denominator(value.to_glib_none().0);

        Fraction::new(n, d)
    }
}

impl glib::value::ToValue for Fraction {
    #[inline]
    fn to_value(&self) -> glib::Value {
        let mut value = glib::Value::for_value_type::<Self>();
        unsafe {
            ffi::gst_value_set_fraction(value.to_glib_none_mut().0, self.numer(), self.denom());
        }
        value
    }

    #[inline]
    fn value_type(&self) -> glib::Type {
        Self::static_type()
    }
}

impl From<Fraction> for glib::Value {
    #[inline]
    fn from(v: Fraction) -> glib::Value {
        skip_assert_initialized!();
        glib::value::ToValue::to_value(&v)
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct IntRange<T> {
    min: T,
    max: T,
    step: T,
}

impl<T: Copy> IntRange<T> {
    #[inline]
    pub fn min(&self) -> T {
        self.min
    }

    #[inline]
    pub fn max(&self) -> T {
        self.max
    }

    #[inline]
    pub fn step(&self) -> T {
        self.step
    }
}

#[doc(hidden)]
pub trait IntRangeType: Sized + Clone + Copy + 'static {
    fn with_min_max(min: Self, max: Self) -> IntRange<Self>;
    fn with_step(min: Self, max: Self, step: Self) -> IntRange<Self>;
}

impl IntRangeType for i32 {
    #[inline]
    fn with_min_max(min: i32, max: i32) -> IntRange<Self> {
        skip_assert_initialized!();
        IntRange { min, max, step: 1 }
    }

    #[inline]
    fn with_step(min: i32, max: i32, step: i32) -> IntRange<Self> {
        skip_assert_initialized!();

        assert!(
            min < max,
            "maximum value must be greater than minimum value"
        );
        assert!(step > 0, "step size must be greater than zero");
        assert!(
            min % step == 0,
            "minimum value must be evenly dividable by step size"
        );
        assert!(
            max % step == 0,
            "maximum value must be evenly dividable by step size"
        );

        IntRange { min, max, step }
    }
}

impl IntRangeType for i64 {
    #[inline]
    fn with_min_max(min: i64, max: i64) -> IntRange<Self> {
        skip_assert_initialized!();
        IntRange { min, max, step: 1 }
    }

    #[inline]
    fn with_step(min: i64, max: i64, step: i64) -> IntRange<Self> {
        skip_assert_initialized!();

        assert!(
            min < max,
            "maximum value must be greater than minimum value"
        );
        assert!(step > 0, "step size must be greater than zero");
        assert!(
            min % step == 0,
            "minimum value must be evenly dividable by step size"
        );
        assert!(
            max % step == 0,
            "maximum value must be evenly dividable by step size"
        );

        IntRange { min, max, step }
    }
}

impl<T: IntRangeType> IntRange<T> {
    #[inline]
    pub fn new(min: T, max: T) -> IntRange<T> {
        skip_assert_initialized!();
        T::with_min_max(min, max)
    }

    #[inline]
    pub fn with_step(min: T, max: T, step: T) -> IntRange<T> {
        skip_assert_initialized!();
        T::with_step(min, max, step)
    }
}

impl From<(i32, i32)> for IntRange<i32> {
    #[inline]
    fn from((min, max): (i32, i32)) -> Self {
        skip_assert_initialized!();
        Self::new(min, max)
    }
}

impl From<(i32, i32, i32)> for IntRange<i32> {
    #[inline]
    fn from((min, max, step): (i32, i32, i32)) -> Self {
        skip_assert_initialized!();
        Self::with_step(min, max, step)
    }
}

impl From<(i64, i64)> for IntRange<i64> {
    #[inline]
    fn from((min, max): (i64, i64)) -> Self {
        skip_assert_initialized!();
        Self::new(min, max)
    }
}

impl From<(i64, i64, i64)> for IntRange<i64> {
    #[inline]
    fn from((min, max, step): (i64, i64, i64)) -> Self {
        skip_assert_initialized!();
        Self::with_step(min, max, step)
    }
}

impl glib::types::StaticType for IntRange<i32> {
    #[inline]
    fn static_type() -> glib::types::Type {
        unsafe { from_glib(ffi::gst_int_range_get_type()) }
    }
}

impl glib::value::ValueType for IntRange<i32> {
    type Type = Self;
}

unsafe impl<'a> glib::value::FromValue<'a> for IntRange<i32> {
    type Checker = glib::value::GenericValueTypeChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a glib::Value) -> Self {
        skip_assert_initialized!();
        let min = ffi::gst_value_get_int_range_min(value.to_glib_none().0);
        let max = ffi::gst_value_get_int_range_max(value.to_glib_none().0);
        let step = ffi::gst_value_get_int_range_step(value.to_glib_none().0);

        Self::with_step(min, max, step)
    }
}

impl glib::value::ToValue for IntRange<i32> {
    #[inline]
    fn to_value(&self) -> glib::Value {
        let mut value = glib::Value::for_value_type::<Self>();
        unsafe {
            ffi::gst_value_set_int_range_step(
                value.to_glib_none_mut().0,
                self.min(),
                self.max(),
                self.step(),
            );
        }
        value
    }

    #[inline]
    fn value_type(&self) -> glib::Type {
        Self::static_type()
    }
}

impl From<IntRange<i32>> for glib::Value {
    #[inline]
    fn from(v: IntRange<i32>) -> glib::Value {
        skip_assert_initialized!();
        glib::value::ToValue::to_value(&v)
    }
}

impl glib::types::StaticType for IntRange<i64> {
    #[inline]
    fn static_type() -> glib::types::Type {
        unsafe { from_glib(ffi::gst_int64_range_get_type()) }
    }
}

impl glib::value::ValueType for IntRange<i64> {
    type Type = Self;
}

unsafe impl<'a> glib::value::FromValue<'a> for IntRange<i64> {
    type Checker = glib::value::GenericValueTypeChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a glib::Value) -> Self {
        skip_assert_initialized!();
        let min = ffi::gst_value_get_int64_range_min(value.to_glib_none().0);
        let max = ffi::gst_value_get_int64_range_max(value.to_glib_none().0);
        let step = ffi::gst_value_get_int64_range_step(value.to_glib_none().0);

        Self::with_step(min, max, step)
    }
}

impl glib::value::ToValue for IntRange<i64> {
    #[inline]
    fn to_value(&self) -> glib::Value {
        let mut value = glib::Value::for_value_type::<Self>();
        unsafe {
            ffi::gst_value_set_int64_range_step(
                value.to_glib_none_mut().0,
                self.min(),
                self.max(),
                self.step(),
            );
        }
        value
    }

    #[inline]
    fn value_type(&self) -> glib::Type {
        Self::static_type()
    }
}

impl From<IntRange<i64>> for glib::Value {
    #[inline]
    fn from(v: IntRange<i64>) -> glib::Value {
        skip_assert_initialized!();
        glib::value::ToValue::to_value(&v)
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FractionRange {
    min: Fraction,
    max: Fraction,
}

impl FractionRange {
    #[inline]
    pub fn new<T: Into<Fraction>, U: Into<Fraction>>(min: T, max: U) -> Self {
        skip_assert_initialized!();

        let min = min.into();
        let max = max.into();

        assert!(min <= max);

        FractionRange { min, max }
    }

    #[inline]
    pub fn min(&self) -> Fraction {
        self.min
    }

    #[inline]
    pub fn max(&self) -> Fraction {
        self.max
    }
}

impl From<(Fraction, Fraction)> for FractionRange {
    #[inline]
    fn from((min, max): (Fraction, Fraction)) -> Self {
        skip_assert_initialized!();

        Self::new(min, max)
    }
}

impl glib::types::StaticType for FractionRange {
    #[inline]
    fn static_type() -> glib::types::Type {
        unsafe { from_glib(ffi::gst_fraction_range_get_type()) }
    }
}

impl glib::value::ValueType for FractionRange {
    type Type = Self;
}

unsafe impl<'a> glib::value::FromValue<'a> for FractionRange {
    type Checker = glib::value::GenericValueTypeChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a glib::Value) -> Self {
        skip_assert_initialized!();
        let min = ffi::gst_value_get_fraction_range_min(value.to_glib_none().0);
        let max = ffi::gst_value_get_fraction_range_max(value.to_glib_none().0);

        let min_n = ffi::gst_value_get_fraction_numerator(min);
        let min_d = ffi::gst_value_get_fraction_denominator(min);
        let max_n = ffi::gst_value_get_fraction_numerator(max);
        let max_d = ffi::gst_value_get_fraction_denominator(max);

        Self::new((min_n, min_d), (max_n, max_d))
    }
}

impl glib::value::ToValue for FractionRange {
    #[inline]
    fn to_value(&self) -> glib::Value {
        let mut value = glib::Value::for_value_type::<Self>();
        unsafe {
            ffi::gst_value_set_fraction_range_full(
                value.to_glib_none_mut().0,
                self.min().numer(),
                self.min().denom(),
                self.max().numer(),
                self.max().denom(),
            );
        }
        value
    }

    #[inline]
    fn value_type(&self) -> glib::Type {
        Self::static_type()
    }
}

impl From<FractionRange> for glib::Value {
    #[inline]
    fn from(v: FractionRange) -> glib::Value {
        skip_assert_initialized!();
        glib::value::ToValue::to_value(&v)
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Bitmask(pub u64);

impl Bitmask {
    #[inline]
    pub fn new(v: u64) -> Self {
        skip_assert_initialized!();
        Bitmask(v)
    }
}

impl ops::Deref for Bitmask {
    type Target = u64;

    #[inline]
    fn deref(&self) -> &u64 {
        &self.0
    }
}

impl ops::DerefMut for Bitmask {
    #[inline]
    fn deref_mut(&mut self) -> &mut u64 {
        &mut self.0
    }
}

impl ops::BitAnd for Bitmask {
    type Output = Self;

    #[inline]
    fn bitand(self, rhs: Self) -> Self {
        Bitmask(self.0.bitand(rhs.0))
    }
}

impl ops::BitOr for Bitmask {
    type Output = Self;

    #[inline]
    fn bitor(self, rhs: Self) -> Self {
        Bitmask(self.0.bitor(rhs.0))
    }
}

impl ops::BitXor for Bitmask {
    type Output = Self;

    #[inline]
    fn bitxor(self, rhs: Self) -> Self {
        Bitmask(self.0.bitxor(rhs.0))
    }
}

impl ops::Not for Bitmask {
    type Output = Self;

    #[inline]
    fn not(self) -> Self {
        Bitmask(self.0.not())
    }
}

impl From<u64> for Bitmask {
    #[inline]
    fn from(v: u64) -> Self {
        skip_assert_initialized!();
        Self::new(v)
    }
}

impl glib::types::StaticType for Bitmask {
    #[inline]
    fn static_type() -> glib::types::Type {
        unsafe { from_glib(ffi::gst_bitmask_get_type()) }
    }
}

impl glib::value::ValueType for Bitmask {
    type Type = Self;
}

unsafe impl<'a> glib::value::FromValue<'a> for Bitmask {
    type Checker = glib::value::GenericValueTypeChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a glib::Value) -> Self {
        skip_assert_initialized!();
        let v = ffi::gst_value_get_bitmask(value.to_glib_none().0);
        Self::new(v)
    }
}

impl glib::value::ToValue for Bitmask {
    #[inline]
    fn to_value(&self) -> glib::Value {
        let mut value = glib::Value::for_value_type::<Self>();
        unsafe {
            ffi::gst_value_set_bitmask(value.to_glib_none_mut().0, self.0);
        }
        value
    }

    #[inline]
    fn value_type(&self) -> glib::Type {
        Self::static_type()
    }
}

impl From<Bitmask> for glib::Value {
    #[inline]
    fn from(v: Bitmask) -> glib::Value {
        skip_assert_initialized!();
        glib::value::ToValue::to_value(&v)
    }
}

#[derive(Clone)]
pub struct Array(glib::SendValue);

unsafe impl Send for Array {}
unsafe impl Sync for Array {}

impl fmt::Debug for Array {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("Array").field(&self.as_slice()).finish()
    }
}

impl Array {
    pub fn new<T: Into<glib::Value> + Send>(values: impl IntoIterator<Item = T>) -> Self {
        assert_initialized_main_thread!();

        unsafe {
            let mut value = glib::Value::for_value_type::<Array>();
            for v in values.into_iter() {
                let mut v = v.into().into_raw();
                ffi::gst_value_array_append_and_take_value(value.to_glib_none_mut().0, &mut v);
            }

            Self(glib::SendValue::unsafe_from(value.into_raw()))
        }
    }

    pub fn from_values(values: impl IntoIterator<Item = glib::SendValue>) -> Self {
        skip_assert_initialized!();

        Self::new(values)
    }

    #[inline]
    pub fn as_slice(&self) -> &[glib::SendValue] {
        unsafe {
            let arr = (*self.0.as_ptr()).data[0].v_pointer as *const glib::ffi::GArray;
            if arr.is_null() || (*arr).len == 0 {
                &[]
            } else {
                #[allow(clippy::cast_ptr_alignment)]
                slice::from_raw_parts((*arr).data as *const glib::SendValue, (*arr).len as usize)
            }
        }
    }

    pub fn append_value(&mut self, value: glib::SendValue) {
        unsafe {
            ffi::gst_value_array_append_and_take_value(
                self.0.to_glib_none_mut().0,
                &mut value.into_raw(),
            );
        }
    }

    pub fn append(&mut self, value: impl Into<glib::Value> + Send) {
        self.append_value(glib::SendValue::from_owned(value));
    }
}

impl Default for Array {
    fn default() -> Self {
        skip_assert_initialized!();

        unsafe {
            let value = glib::Value::for_value_type::<Array>();

            Self(glib::SendValue::unsafe_from(value.into_raw()))
        }
    }
}

impl ops::Deref for Array {
    type Target = [glib::SendValue];

    #[inline]
    fn deref(&self) -> &[glib::SendValue] {
        self.as_slice()
    }
}

impl AsRef<[glib::SendValue]> for Array {
    #[inline]
    fn as_ref(&self) -> &[glib::SendValue] {
        self.as_slice()
    }
}

impl std::iter::FromIterator<glib::SendValue> for Array {
    fn from_iter<T: IntoIterator<Item = glib::SendValue>>(iter: T) -> Self {
        skip_assert_initialized!();
        Self::from_values(iter)
    }
}

impl std::iter::Extend<glib::SendValue> for Array {
    fn extend<T: IntoIterator<Item = glib::SendValue>>(&mut self, iter: T) {
        for v in iter.into_iter() {
            self.append_value(v);
        }
    }
}

impl glib::value::ValueType for Array {
    type Type = Self;
}

unsafe impl<'a> glib::value::FromValue<'a> for Array {
    type Checker = glib::value::GenericValueTypeChecker<Self>;

    unsafe fn from_value(value: &'a glib::Value) -> Self {
        skip_assert_initialized!();
        Self(glib::SendValue::unsafe_from(value.clone().into_raw()))
    }
}

impl glib::value::ToValue for Array {
    fn to_value(&self) -> glib::Value {
        self.0.clone().into()
    }

    fn value_type(&self) -> glib::Type {
        Self::static_type()
    }
}

impl From<Array> for glib::Value {
    fn from(v: Array) -> glib::Value {
        skip_assert_initialized!();
        v.0.into()
    }
}

impl glib::types::StaticType for Array {
    #[inline]
    fn static_type() -> glib::types::Type {
        unsafe { from_glib(ffi::gst_value_array_get_type()) }
    }
}

#[derive(Debug, Clone)]
pub struct ArrayRef<'a>(&'a [glib::SendValue]);

unsafe impl Send for ArrayRef<'_> {}
unsafe impl Sync for ArrayRef<'_> {}

impl<'a> ArrayRef<'a> {
    pub fn new(values: &'a [glib::SendValue]) -> Self {
        skip_assert_initialized!();

        Self(values)
    }

    #[inline]
    pub fn as_slice(&self) -> &'a [glib::SendValue] {
        self.0
    }
}

impl ops::Deref for ArrayRef<'_> {
    type Target = [glib::SendValue];

    #[inline]
    fn deref(&self) -> &[glib::SendValue] {
        self.as_slice()
    }
}

impl AsRef<[glib::SendValue]> for ArrayRef<'_> {
    #[inline]
    fn as_ref(&self) -> &[glib::SendValue] {
        self.as_slice()
    }
}

unsafe impl<'a> glib::value::FromValue<'a> for ArrayRef<'a> {
    type Checker = glib::value::GenericValueTypeChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a glib::Value) -> Self {
        skip_assert_initialized!();
        let arr = (*value.as_ptr()).data[0].v_pointer as *const glib::ffi::GArray;
        if arr.is_null() || (*arr).len == 0 {
            Self(&[])
        } else {
            #[allow(clippy::cast_ptr_alignment)]
            Self(slice::from_raw_parts(
                (*arr).data as *const glib::SendValue,
                (*arr).len as usize,
            ))
        }
    }
}

impl glib::value::ToValue for ArrayRef<'_> {
    #[inline]
    fn to_value(&self) -> glib::Value {
        let mut value = glib::Value::for_value_type::<Array>();
        unsafe {
            for v in self.0 {
                ffi::gst_value_array_append_value(value.to_glib_none_mut().0, v.to_glib_none().0);
            }
        }
        value
    }

    #[inline]
    fn value_type(&self) -> glib::Type {
        Self::static_type()
    }
}

impl<'a> From<ArrayRef<'a>> for glib::Value {
    #[inline]
    fn from(v: ArrayRef<'a>) -> glib::Value {
        skip_assert_initialized!();
        glib::value::ToValue::to_value(&v)
    }
}

impl glib::types::StaticType for ArrayRef<'_> {
    #[inline]
    fn static_type() -> glib::types::Type {
        unsafe { from_glib(ffi::gst_value_array_get_type()) }
    }
}

#[derive(Clone)]
pub struct List(glib::SendValue);

unsafe impl Send for List {}
unsafe impl Sync for List {}

impl fmt::Debug for List {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("List").field(&self.as_slice()).finish()
    }
}

impl List {
    pub fn new<T: Into<glib::Value> + Send>(values: impl IntoIterator<Item = T>) -> Self {
        assert_initialized_main_thread!();

        unsafe {
            let mut value = glib::Value::for_value_type::<List>();
            for v in values.into_iter() {
                let mut v = v.into().into_raw();
                ffi::gst_value_list_append_and_take_value(value.to_glib_none_mut().0, &mut v);
            }

            Self(glib::SendValue::unsafe_from(value.into_raw()))
        }
    }

    pub fn from_values(values: impl IntoIterator<Item = glib::SendValue>) -> Self {
        skip_assert_initialized!();

        Self::new(values)
    }

    #[inline]
    pub fn as_slice(&self) -> &[glib::SendValue] {
        unsafe {
            let arr = (*self.0.as_ptr()).data[0].v_pointer as *const glib::ffi::GArray;
            if arr.is_null() || (*arr).len == 0 {
                &[]
            } else {
                #[allow(clippy::cast_ptr_alignment)]
                slice::from_raw_parts((*arr).data as *const glib::SendValue, (*arr).len as usize)
            }
        }
    }

    pub fn append_value(&mut self, value: glib::SendValue) {
        unsafe {
            ffi::gst_value_list_append_and_take_value(
                self.0.to_glib_none_mut().0,
                &mut value.into_raw(),
            );
        }
    }

    pub fn append(&mut self, value: impl Into<glib::Value> + Send) {
        self.append_value(glib::SendValue::from_owned(value));
    }
}

impl Default for List {
    fn default() -> Self {
        skip_assert_initialized!();

        unsafe {
            let value = glib::Value::for_value_type::<List>();

            Self(glib::SendValue::unsafe_from(value.into_raw()))
        }
    }
}

impl ops::Deref for List {
    type Target = [glib::SendValue];

    #[inline]
    fn deref(&self) -> &[glib::SendValue] {
        self.as_slice()
    }
}

impl AsRef<[glib::SendValue]> for List {
    #[inline]
    fn as_ref(&self) -> &[glib::SendValue] {
        self.as_slice()
    }
}

impl std::iter::FromIterator<glib::SendValue> for List {
    fn from_iter<T: IntoIterator<Item = glib::SendValue>>(iter: T) -> Self {
        skip_assert_initialized!();
        Self::from_values(iter)
    }
}

impl std::iter::Extend<glib::SendValue> for List {
    fn extend<T: IntoIterator<Item = glib::SendValue>>(&mut self, iter: T) {
        for v in iter.into_iter() {
            self.append_value(v);
        }
    }
}

impl glib::value::ValueType for List {
    type Type = Self;
}

unsafe impl<'a> glib::value::FromValue<'a> for List {
    type Checker = glib::value::GenericValueTypeChecker<Self>;

    unsafe fn from_value(value: &'a glib::Value) -> Self {
        skip_assert_initialized!();
        Self(glib::SendValue::unsafe_from(value.clone().into_raw()))
    }
}

impl glib::value::ToValue for List {
    fn to_value(&self) -> glib::Value {
        self.0.clone().into()
    }

    fn value_type(&self) -> glib::Type {
        Self::static_type()
    }
}

impl From<List> for glib::Value {
    fn from(v: List) -> glib::Value {
        skip_assert_initialized!();
        v.0.into()
    }
}

impl glib::types::StaticType for List {
    #[inline]
    fn static_type() -> glib::types::Type {
        unsafe { from_glib(ffi::gst_value_list_get_type()) }
    }
}

#[derive(Debug, Clone)]
pub struct ListRef<'a>(&'a [glib::SendValue]);

unsafe impl Send for ListRef<'_> {}
unsafe impl Sync for ListRef<'_> {}

impl<'a> ListRef<'a> {
    pub fn new(values: &'a [glib::SendValue]) -> Self {
        skip_assert_initialized!();

        Self(values)
    }

    #[inline]
    pub fn as_slice(&self) -> &'a [glib::SendValue] {
        self.0
    }
}

impl ops::Deref for ListRef<'_> {
    type Target = [glib::SendValue];

    #[inline]
    fn deref(&self) -> &[glib::SendValue] {
        self.as_slice()
    }
}

impl AsRef<[glib::SendValue]> for ListRef<'_> {
    #[inline]
    fn as_ref(&self) -> &[glib::SendValue] {
        self.as_slice()
    }
}

unsafe impl<'a> glib::value::FromValue<'a> for ListRef<'a> {
    type Checker = glib::value::GenericValueTypeChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a glib::Value) -> Self {
        skip_assert_initialized!();
        let arr = (*value.as_ptr()).data[0].v_pointer as *const glib::ffi::GArray;
        if arr.is_null() || (*arr).len == 0 {
            Self(&[])
        } else {
            #[allow(clippy::cast_ptr_alignment)]
            Self(slice::from_raw_parts(
                (*arr).data as *const glib::SendValue,
                (*arr).len as usize,
            ))
        }
    }
}

impl glib::value::ToValue for ListRef<'_> {
    #[inline]
    fn to_value(&self) -> glib::Value {
        let mut value = glib::Value::for_value_type::<List>();
        unsafe {
            for v in self.0 {
                ffi::gst_value_list_append_value(value.to_glib_none_mut().0, v.to_glib_none().0);
            }
        }
        value
    }

    #[inline]
    fn value_type(&self) -> glib::Type {
        Self::static_type()
    }
}

impl<'a> From<ListRef<'a>> for glib::Value {
    #[inline]
    fn from(v: ListRef<'a>) -> glib::Value {
        skip_assert_initialized!();
        glib::value::ToValue::to_value(&v)
    }
}

impl glib::types::StaticType for ListRef<'_> {
    #[inline]
    fn static_type() -> glib::types::Type {
        unsafe { from_glib(ffi::gst_value_list_get_type()) }
    }
}

pub trait GstValueExt: Sized {
    #[doc(alias = "gst_value_can_compare")]
    fn can_compare(&self, other: &Self) -> bool;
    #[doc(alias = "gst_value_compare")]
    fn compare(&self, other: &Self) -> Option<cmp::Ordering>;
    fn eq(&self, other: &Self) -> bool;
    #[doc(alias = "gst_value_can_intersect")]
    fn can_intersect(&self, other: &Self) -> bool;
    #[doc(alias = "gst_value_intersect")]
    fn intersect(&self, other: &Self) -> Option<Self>;
    #[doc(alias = "gst_value_can_subtract")]
    fn can_subtract(&self, other: &Self) -> bool;
    #[doc(alias = "gst_value_subtract")]
    fn subtract(&self, other: &Self) -> Option<Self>;
    #[doc(alias = "gst_value_can_union")]
    fn can_union(&self, other: &Self) -> bool;
    #[doc(alias = "gst_value_union")]
    fn union(&self, other: &Self) -> Option<Self>;
    #[doc(alias = "gst_value_fixate")]
    fn fixate(&self) -> Option<Self>;
    #[doc(alias = "gst_value_is_fixed")]
    fn is_fixed(&self) -> bool;
    #[doc(alias = "gst_value_is_subset")]
    fn is_subset(&self, superset: &Self) -> bool;
    #[doc(alias = "gst_value_serialize")]
    fn serialize(&self) -> Result<glib::GString, glib::BoolError>;
    #[doc(alias = "gst_value_deserialize")]
    fn deserialize(s: &str, type_: glib::Type) -> Result<glib::Value, glib::BoolError>;
    #[cfg(feature = "v1_20")]
    #[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
    #[doc(alias = "gst_value_deserialize_with_pspec")]
    fn deserialize_with_pspec(
        s: &str,
        pspec: &glib::ParamSpec,
    ) -> Result<glib::Value, glib::BoolError>;
    #[cfg(feature = "v1_28")]
    #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))]
    #[doc(alias = "gst_value_hash")]
    fn hash(&self) -> Result<u32, glib::BoolError>;
}

impl GstValueExt for glib::Value {
    fn can_compare(&self, other: &Self) -> bool {
        unsafe {
            from_glib(ffi::gst_value_can_compare(
                self.to_glib_none().0,
                other.to_glib_none().0,
            ))
        }
    }

    fn compare(&self, other: &Self) -> Option<cmp::Ordering> {
        unsafe {
            let val = ffi::gst_value_compare(self.to_glib_none().0, other.to_glib_none().0);

            match val {
                ffi::GST_VALUE_LESS_THAN => Some(cmp::Ordering::Less),
                ffi::GST_VALUE_EQUAL => Some(cmp::Ordering::Equal),
                ffi::GST_VALUE_GREATER_THAN => Some(cmp::Ordering::Greater),
                _ => None,
            }
        }
    }

    fn eq(&self, other: &Self) -> bool {
        self.compare(other) == Some(cmp::Ordering::Equal)
    }

    fn can_intersect(&self, other: &Self) -> bool {
        unsafe {
            from_glib(ffi::gst_value_can_intersect(
                self.to_glib_none().0,
                other.to_glib_none().0,
            ))
        }
    }

    fn intersect(&self, other: &Self) -> Option<Self> {
        unsafe {
            let mut value = glib::Value::uninitialized();
            let ret: bool = from_glib(ffi::gst_value_intersect(
                value.to_glib_none_mut().0,
                self.to_glib_none().0,
                other.to_glib_none().0,
            ));
            if ret {
                Some(value)
            } else {
                None
            }
        }
    }

    fn can_subtract(&self, other: &Self) -> bool {
        unsafe {
            from_glib(ffi::gst_value_can_subtract(
                self.to_glib_none().0,
                other.to_glib_none().0,
            ))
        }
    }

    fn subtract(&self, other: &Self) -> Option<Self> {
        unsafe {
            let mut value = glib::Value::uninitialized();
            let ret: bool = from_glib(ffi::gst_value_subtract(
                value.to_glib_none_mut().0,
                self.to_glib_none().0,
                other.to_glib_none().0,
            ));
            if ret {
                Some(value)
            } else {
                None
            }
        }
    }

    fn can_union(&self, other: &Self) -> bool {
        unsafe {
            from_glib(ffi::gst_value_can_union(
                self.to_glib_none().0,
                other.to_glib_none().0,
            ))
        }
    }

    fn union(&self, other: &Self) -> Option<Self> {
        unsafe {
            let mut value = glib::Value::uninitialized();
            let ret: bool = from_glib(ffi::gst_value_union(
                value.to_glib_none_mut().0,
                self.to_glib_none().0,
                other.to_glib_none().0,
            ));
            if ret {
                Some(value)
            } else {
                None
            }
        }
    }

    fn fixate(&self) -> Option<Self> {
        unsafe {
            let mut value = glib::Value::uninitialized();
            let ret: bool = from_glib(ffi::gst_value_fixate(
                value.to_glib_none_mut().0,
                self.to_glib_none().0,
            ));
            if ret {
                Some(value)
            } else {
                None
            }
        }
    }

    fn is_fixed(&self) -> bool {
        unsafe { from_glib(ffi::gst_value_is_fixed(self.to_glib_none().0)) }
    }

    fn is_subset(&self, superset: &Self) -> bool {
        unsafe {
            from_glib(ffi::gst_value_is_subset(
                self.to_glib_none().0,
                superset.to_glib_none().0,
            ))
        }
    }

    fn serialize(&self) -> Result<glib::GString, glib::BoolError> {
        unsafe {
            Option::<_>::from_glib_full(ffi::gst_value_serialize(self.to_glib_none().0))
                .ok_or_else(|| glib::bool_error!("Failed to serialize value"))
        }
    }

    fn deserialize(s: &str, type_: glib::Type) -> Result<glib::Value, glib::BoolError> {
        skip_assert_initialized!();

        unsafe {
            let mut value = glib::Value::from_type(type_);
            let ret: bool = from_glib(ffi::gst_value_deserialize(
                value.to_glib_none_mut().0,
                s.to_glib_none().0,
            ));
            if ret {
                Ok(value)
            } else {
                Err(glib::bool_error!("Failed to deserialize value"))
            }
        }
    }

    #[cfg(feature = "v1_20")]
    #[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
    fn deserialize_with_pspec(
        s: &str,
        pspec: &glib::ParamSpec,
    ) -> Result<glib::Value, glib::BoolError> {
        skip_assert_initialized!();

        unsafe {
            let mut value = glib::Value::from_type_unchecked(pspec.value_type());
            let ret: bool = from_glib(ffi::gst_value_deserialize_with_pspec(
                value.to_glib_none_mut().0,
                s.to_glib_none().0,
                pspec.to_glib_none().0,
            ));
            if ret {
                Ok(value)
            } else {
                Err(glib::bool_error!("Failed to deserialize value"))
            }
        }
    }

    #[cfg(feature = "v1_28")]
    fn hash(&self) -> Result<u32, glib::BoolError> {
        {
            unsafe {
                let mut hash = 0;
                glib::result_from_gboolean!(
                    gstreamer_sys::gst_value_hash(self.to_glib_none().0, &mut hash),
                    "Failed to hash {self:?}"
                )?;
                Ok(hash)
            }
        }
    }
}

#[doc(hidden)]
#[macro_export]
macro_rules! impl_builder_gvalue_extra_setters (
    (field) => {
        // rustdoc-stripper-ignore-next
        /// Sets field `name` to the given inner value if the `predicate` evaluates to `true`.
        ///
        /// This has no effect if the `predicate` evaluates to `false`,
        /// i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn field_if(self, name: impl $crate::glib::IntoGStr, value: impl Into<$crate::glib::Value> + Send, predicate: bool) -> Self {
            if predicate {
                self.field(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` to the given inner value if the `predicate` evaluates to `true`.
        ///
        /// This has no effect if the `predicate` evaluates to `false`,
        /// i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn field_with_static_if(self, name: impl AsRef<$crate::glib::GStr> + 'static, value: impl Into<$crate::glib::Value> + Send, predicate: bool) -> Self {
            if predicate {
                self.field_with_static(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` to the given inner value if the `predicate` evaluates to `true`.
        ///
        /// This has no effect if the `predicate` evaluates to `false`,
        /// i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn field_with_id_if(self, name: impl AsRef<$crate::IdStr>, value: impl Into<$crate::glib::Value> + Send, predicate: bool) -> Self {
            if predicate {
                self.field_with_id(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` to the given inner value if `value` is `Some`.
        ///
        /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn field_if_some(self, name: impl $crate::glib::IntoGStr, value: Option<impl Into<$crate::glib::Value> + Send>) -> Self {
            if let Some(value) = value {
                self.field(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` to the given inner value if `value` is `Some`.
        ///
        /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn field_with_static_if_some(self, name: impl AsRef<$crate::glib::GStr> + 'static, value: Option<impl Into<$crate::glib::Value> + Send>) -> Self {
            if let Some(value) = value {
                self.field_with_static(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` to the given inner value if `value` is `Some`.
        ///
        /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn field_with_id_if_some(self, name: impl AsRef<$crate::IdStr>, value: Option<impl Into<$crate::glib::Value> + Send>) -> Self {
            if let Some(value) = value {
                self.field_with_id(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s.
        ///
        /// Overrides any default or previously defined value for `name`.
        #[inline]
        pub fn field_from_iter<
            V: $crate::glib::value::ValueType + Into<$crate::glib::Value> + FromIterator<$crate::glib::SendValue> + Send,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: impl $crate::glib::IntoGStr,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let iter = iter.into_iter().map(|item| item.to_send_value());
            self.field(name, V::from_iter(iter))
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s.
        ///
        /// Overrides any default or previously defined value for `name`.
        #[inline]
        pub fn field_with_static_from_iter<
            V: $crate::glib::value::ValueType + Into<$crate::glib::Value> + FromIterator<$crate::glib::SendValue> + Send,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: impl AsRef<$crate::glib::GStr> + 'static,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let iter = iter.into_iter().map(|item| item.to_send_value());
            self.field_with_static(name, V::from_iter(iter))
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s.
        ///
        /// Overrides any default or previously defined value for `name`.
        #[inline]
        pub fn field_with_id_from_iter<
            V: $crate::glib::value::ValueType + Into<$crate::glib::Value> + FromIterator<$crate::glib::SendValue> + Send,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: impl AsRef<$crate::IdStr>,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let iter = iter.into_iter().map(|item| item.to_send_value());
            self.field_with_id(name, V::from_iter(iter))
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s,
        /// if `iter` is not empty.
        ///
        /// This has no effect if `iter` is empty, i.e. previous value for `name` is unchanged.
        #[inline]
        pub fn field_if_not_empty<
            V: $crate::glib::value::ValueType + Into<$crate::glib::Value> + FromIterator<$crate::glib::SendValue> + Send,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: impl $crate::glib::IntoGStr,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let mut iter = iter.into_iter().peekable();
            if iter.peek().is_some() {
                let iter = iter.map(|item| item.to_send_value());
                self.field(name, V::from_iter(iter))
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s,
        /// if `iter` is not empty.
        ///
        /// This has no effect if `iter` is empty, i.e. previous value for `name` is unchanged.
        #[inline]
        pub fn field_with_static_if_not_empty<
            V: $crate::glib::value::ValueType + Into<$crate::glib::Value> + FromIterator<$crate::glib::SendValue> + Send,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: impl AsRef<$crate::glib::GStr> + 'static,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let mut iter = iter.into_iter().peekable();
            if iter.peek().is_some() {
                let iter = iter.map(|item| item.to_send_value());
                self.field_with_static(name, V::from_iter(iter))
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s,
        /// if `iter` is not empty.
        ///
        /// This has no effect if `iter` is empty, i.e. previous value for `name` is unchanged.
        #[inline]
        pub fn field_with_id_if_not_empty
            <V: $crate::glib::value::ValueType + Into<$crate::glib::Value> + FromIterator<$crate::glib::SendValue> + Send,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: impl AsRef<IdStr>,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let mut iter = iter.into_iter().peekable();
            if iter.peek().is_some() {
                let iter = iter.map(|item| item.to_send_value());
                self.field_with_id(name, V::from_iter(iter))
            } else {
                self
            }
        }
    };

    (other_field) => {
        // rustdoc-stripper-ignore-next
        /// Sets field `name` to the given inner value if the `predicate` evaluates to `true`.
        ///
        /// This has no effect if the `predicate` evaluates to `false`,
        /// i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn other_field_if(self, name: &'a str, value: impl $crate::glib::value::ToSendValue, predicate: bool) -> Self {
            if predicate {
                self.other_field(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` to the given inner value if `value` is `Some`.
        ///
        /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn other_field_if_some(self, name: &'a str, value: Option<impl $crate::glib::value::ToSendValue>) -> Self {
            if let Some(value) = value {
                self.other_field(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s.
        ///
        /// Overrides any default or previously defined value for `name`.
        #[inline]
        pub fn other_field_from_iter<
            V: $crate::glib::value::ValueType + $crate::glib::value::ToSendValue + FromIterator<$crate::glib::SendValue>,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: &'a str,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let iter = iter.into_iter().map(|item| item.to_send_value());
            self.other_field(name, V::from_iter(iter))
        }

        // rustdoc-stripper-ignore-next
        /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s,
        /// if `iter` is not empty.
        ///
        /// This has no effect if `iter` is empty, i.e. previous value for `name` is unchanged.
        #[inline]
        pub fn other_field_if_not_empty<
            V: $crate::glib::value::ValueType + $crate::glib::value::ToSendValue + FromIterator<$crate::glib::SendValue>,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: &'a str,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let mut iter = iter.into_iter().peekable();
            if iter.peek().is_some() {
                let iter = iter.map(|item| item.to_send_value());
                self.other_field(name, V::from_iter(iter))
            } else {
                self
            }
        }
     };

    (property_and_name) => {
        // rustdoc-stripper-ignore-next
        /// Sets property `name` to the given inner value if the `predicate` evaluates to `true`.
        ///
        /// This has no effect if the `predicate` evaluates to `false`,
        /// i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn property_if(self, name: &'a str, value: impl Into<$crate::glib::Value> + 'a, predicate: bool) -> Self {
            if predicate {
                self.property(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets property `name` to the given inner value if `value` is `Some`.
        ///
        /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn property_if_some(self, name: &'a str, value: Option<impl Into<$crate::glib::Value> + 'a>) -> Self {
            if let Some(value) = value {
                self.property(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets property `name` using the given `ValueType` `V` built from `iter`'s the `Item`s.
        ///
        /// Overrides any default or previously defined value for `name`.
        #[inline]
        pub fn property_from_iter<
            V: $crate::glib::value::ValueType + Into<$crate::glib::Value> + FromIterator<$crate::glib::SendValue>,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: &'a str,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let iter = iter.into_iter().map(|item| item.to_send_value());
            self.property(name, V::from_iter(iter))
        }

        // rustdoc-stripper-ignore-next
        /// Sets property `name` using the given `ValueType` `V` built from `iter`'s Item`s,
        /// if `iter` is not empty.
        ///
        /// This has no effect if `iter` is empty, i.e. previous value for `name` is unchanged.
        #[inline]
        pub fn property_if_not_empty<
            V: $crate::glib::value::ValueType + Into<$crate::glib::Value> + FromIterator<$crate::glib::SendValue>,
            I: $crate::glib::value::ToSendValue,
        >(
            self,
            name: &'a str,
            iter: impl IntoIterator<Item = I>,
        ) -> Self {
            let mut iter = iter.into_iter().peekable();
            if iter.peek().is_some() {
                let iter = iter.map(|item| item.to_send_value());
                self.property(name, V::from_iter(iter))
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets property `name` to the given string value `value` if the `predicate` evaluates to `true`.
        ///
        /// This has no effect if the `predicate` evaluates to `false`,
        /// i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn property_from_str_if(self, name: &'a str, value: &'a str, predicate: bool) -> Self {
            if predicate {
                self.property_from_str(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets property `name` to the given string value `value` if it is `Some`.
        ///
        /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept.
        #[inline]
        pub fn property_from_str_if_some(self, name: &'a str, value: Option<&'a str>) -> Self {
            if let Some(value) = value {
                self.property_from_str(name, value)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets the name property to the given `name`.
        #[inline]
        pub fn name(self, name: impl Into<$crate::glib::GString>) -> Self {
            self.property("name", name.into())
        }

        // rustdoc-stripper-ignore-next
        /// Sets the name property to the given `name` if the `predicate` evaluates to `true`.
        ///
        /// This has no effect if the `predicate` evaluates to `false`,
        /// i.e. default or previous name is kept.
        #[inline]
        pub fn name_if(self, name: impl Into<$crate::glib::GString>, predicate: bool) -> Self {
            if predicate {
                self.name(name)
            } else {
                self
            }
        }

        // rustdoc-stripper-ignore-next
        /// Sets the name property to the given `name` if it is `Some`.
        ///
        /// This has no effect if the value is `None`, i.e. default or previous name is kept.
        #[inline]
        pub fn name_if_some(self, name: Option<impl Into<$crate::glib::GString>>) -> Self {
            if let Some(name) = name {
                self.name(name)
            } else {
                self
            }
        }
     };
);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_fraction() {
        crate::init().unwrap();

        let f1 = crate::Fraction::new(1, 2);
        let f2 = crate::Fraction::new(2, 3);
        let mut f3 = f1 * f2;
        let f4 = f1 * f2;
        f3 *= f2;
        f3 *= f4;

        assert_eq!(f3, crate::Fraction::new(2, 27));
    }

    #[test]
    fn test_int_range_constructor() {
        crate::init().unwrap();

        // Type inference should figure out the type
        let _r1 = crate::IntRange::new(1i32, 2i32);
        let _r2 = crate::IntRange::with_step(2i64, 10i64, 2i64);
        let _r3 = crate::IntRange::with_step(0i64, 6i64, 3i64);
    }

    #[test]
    #[should_panic(expected = "step size must be greater than zero")]
    fn test_int_range_constructor_step0() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(0i32, 2i32, 0i32);
    }

    #[test]
    #[should_panic(expected = "maximum value must be greater than minimum value")]
    fn test_int_range_constructor_max_min() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(4i32, 2i32, 2i32);
    }

    #[test]
    #[should_panic(expected = "maximum value must be greater than minimum value")]
    fn test_int_range_constructor_max_eq_min() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(4i32, 4i32, 2i32);
    }

    #[test]
    #[should_panic(expected = "minimum value must be evenly dividable by step size")]
    fn test_int_range_constructor_bad_step() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(1i32, 10i32, 2i32);
    }

    #[test]
    #[should_panic(expected = "maximum value must be evenly dividable by step size")]
    fn test_int_range_constructor_bad_step_max() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(0i32, 10i32, 3i32);
    }

    #[test]
    #[should_panic(expected = "step size must be greater than zero")]
    fn test_int_range_constructor_step0_i64() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(0i64, 2i64, 0i64);
    }

    #[test]
    #[should_panic(expected = "maximum value must be greater than minimum value")]
    fn test_int_range_constructor_max_min_i64() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(4i64, 2i64, 2i64);
    }

    #[test]
    #[should_panic(expected = "maximum value must be greater than minimum value")]
    fn test_int_range_constructor_max_eq_min_i64() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(4i64, 4i64, 2i64);
    }

    #[test]
    #[should_panic(expected = "minimum value must be evenly dividable by step size")]
    fn test_int_range_constructor_bad_step_i64() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(1i64, 10i64, 2i64);
    }
    #[test]
    #[should_panic(expected = "maximum value must be evenly dividable by step size")]
    fn test_int_range_constructor_bad_step_max_i64() {
        crate::init().unwrap();
        let _r = crate::IntRange::with_step(0i64, 10i64, 3i64);
    }

    #[test]
    fn test_deserialize() {
        crate::init().unwrap();

        let v = glib::Value::deserialize("123", i32::static_type()).unwrap();
        assert_eq!(v.get::<i32>(), Ok(123));
    }
}
