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

// rustdoc-stripper-ignore-next
//! `Value` binding and helper traits.
//!
//! The type of a [`Value`](struct.Value.html) is dynamic in that it generally
//! isn't known at compile time but once created a `Value` can't change its
//! type.
//!
//! [`SendValue`](struct.SendValue.html) is a version of [`Value`](struct.Value.html)
//! that can only store types that implement `Send` and as such implements `Send` itself. It
//! dereferences to `Value` so it can be used everywhere `Value` references are accepted.
//!
//! Supported types are `bool`, `i8`, `u8`, `i32`, `u32`, `i64`, `u64`, `f32`,
//! `f64`, `String` and objects (`T: IsA<Object>`).
//!
//! # Examples
//!
//! ```
//! use glib::prelude::*; // or `use gtk::prelude::*;`
//! use glib::Value;
//!
//! // Value implement From<&i32>, From<&str> and From<Option<&str>>.
//! // Another option is the `ToValue` trait.
//! let mut num = 10.to_value();
//! let mut hello = Value::from("Hello!");
//! let none: Option<&str> = None;
//! let str_none = none.to_value();
//!
//! // `is` tests the type of the value.
//! assert!(num.is::<i32>());
//! assert!(hello.is::<String>());
//!
//! // `get` tries to get an optional value of the specified type
//! // and returns an `Err` if the type doesn't match.
//! assert_eq!(num.get(), Ok(10));
//! assert!(num.get::<String>().is_err());
//! assert_eq!(hello.get(), Ok(String::from("Hello!")));
//! assert_eq!(hello.get::<String>(), Ok(String::from("Hello!")));
//! assert_eq!(str_none.get::<Option<String>>(), Ok(None));
//! ```

use std::{
    convert::Infallible,
    error,
    ffi::CStr,
    fmt, mem,
    num::{NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU32, NonZeroU64, NonZeroU8},
    ops::Deref,
    path::{Path, PathBuf},
    ptr,
};

use libc::{c_char, c_void};

use crate::{
    gstring::GString,
    prelude::*,
    translate::*,
    types::{Pointee, Pointer, Type},
};

// rustdoc-stripper-ignore-next
/// A type that can be stored in `Value`s.
pub trait ValueType: ToValue + for<'a> FromValue<'a> + 'static {
    // rustdoc-stripper-ignore-next
    /// Type to get the `Type` from.
    ///
    /// This exists only for handling optional types.
    // FIXME: Should default to Self once associated type defaults are stabilized
    // https://github.com/rust-lang/rust/issues/29661
    type Type: StaticType;
}

// rustdoc-stripper-ignore-next
/// A type that can be stored in `Value`s and is optional.
///
/// These are types were storing an `Option` is valid. Examples are `String` and all object types.
pub trait ValueTypeOptional:
    ValueType + ToValueOptional + FromValueOptional<'static> + StaticType
{
}

impl<T, C, E> ValueType for Option<T>
where
    T: for<'a> FromValue<'a, Checker = C> + ValueTypeOptional + StaticType + 'static,
    C: ValueTypeChecker<Error = ValueTypeMismatchOrNoneError<E>>,
    E: error::Error + Send + Sized + 'static,
{
    type Type = T::Type;
}

// rustdoc-stripper-ignore-next
/// Trait for `Value` type checkers.
pub unsafe trait ValueTypeChecker {
    type Error: error::Error + Send + Sized + 'static;

    fn check(value: &Value) -> Result<(), Self::Error>;
}

// rustdoc-stripper-ignore-next
/// An error returned from the [`get`](struct.Value.html#method.get) function
/// on a [`Value`](struct.Value.html) for non-optional types an `Option`.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ValueTypeMismatchError {
    actual: Type,
    requested: Type,
}

impl ValueTypeMismatchError {
    pub fn new(actual: Type, requested: Type) -> Self {
        Self { actual, requested }
    }
}

impl ValueTypeMismatchError {
    pub fn actual_type(&self) -> Type {
        self.actual
    }

    pub fn requested_type(&self) -> Type {
        self.requested
    }
}

impl fmt::Display for ValueTypeMismatchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Value type mismatch. Actual {:?}, requested {:?}",
            self.actual_type(),
            self.requested_type(),
        )
    }
}

impl error::Error for ValueTypeMismatchError {}

impl From<Infallible> for ValueTypeMismatchError {
    fn from(e: Infallible) -> Self {
        match e {}
    }
}

// rustdoc-stripper-ignore-next
/// Generic `Value` type checker for types.
pub struct GenericValueTypeChecker<T>(std::marker::PhantomData<T>);

unsafe impl<T: StaticType> ValueTypeChecker for GenericValueTypeChecker<T> {
    type Error = ValueTypeMismatchError;

    #[doc(alias = "g_type_check_value_holds")]
    #[inline]
    fn check(value: &Value) -> Result<(), Self::Error> {
        unsafe {
            if gobject_ffi::g_type_check_value_holds(&value.inner, T::static_type().into_glib())
                == ffi::GFALSE
            {
                Err(ValueTypeMismatchError::new(
                    Type::from_glib(value.inner.g_type),
                    T::static_type(),
                ))
            } else {
                Ok(())
            }
        }
    }
}

pub struct CharTypeChecker();
unsafe impl ValueTypeChecker for CharTypeChecker {
    type Error = InvalidCharError;

    #[inline]
    fn check(value: &Value) -> Result<(), Self::Error> {
        let v = value.get::<u32>()?;
        match char::from_u32(v) {
            Some(_) => Ok(()),
            None => Err(InvalidCharError::CharConversionError),
        }
    }
}

// rustdoc-stripper-ignore-next
/// An error returned from the [`get`](struct.Value.html#method.get) function
/// on a [`Value`](struct.Value.html) for char (which are internally u32) types.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum InvalidCharError {
    WrongValueType(ValueTypeMismatchError),
    CharConversionError,
}
impl fmt::Display for InvalidCharError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::WrongValueType(err) => err.fmt(f),
            Self::CharConversionError => {
                write!(f, "couldn't convert to char, invalid u32 contents")
            }
        }
    }
}
impl error::Error for InvalidCharError {}

impl From<ValueTypeMismatchError> for InvalidCharError {
    fn from(err: ValueTypeMismatchError) -> Self {
        Self::WrongValueType(err)
    }
}

impl From<Infallible> for InvalidCharError {
    fn from(e: Infallible) -> Self {
        match e {}
    }
}

// rustdoc-stripper-ignore-next
/// An error returned from the [`get`](struct.Value.html#method.get)
/// function on a [`Value`](struct.Value.html) for optional types.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ValueTypeMismatchOrNoneError<E: error::Error> {
    WrongValueType(E),
    UnexpectedNone,
}

impl<E: error::Error> fmt::Display for ValueTypeMismatchOrNoneError<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::WrongValueType(err) => <E as fmt::Display>::fmt(err, f),
            Self::UnexpectedNone => write!(f, "Unexpected None",),
        }
    }
}

impl<E: error::Error> error::Error for ValueTypeMismatchOrNoneError<E> {}

impl<E: error::Error> From<E> for ValueTypeMismatchOrNoneError<E> {
    fn from(err: E) -> Self {
        Self::WrongValueType(err)
    }
}

// rustdoc-stripper-ignore-next
/// Generic `Value` type checker for optional types.
pub struct GenericValueTypeOrNoneChecker<T>(std::marker::PhantomData<T>);

unsafe impl<T: StaticType> ValueTypeChecker for GenericValueTypeOrNoneChecker<T> {
    type Error = ValueTypeMismatchOrNoneError<ValueTypeMismatchError>;

    #[inline]
    fn check(value: &Value) -> Result<(), Self::Error> {
        GenericValueTypeChecker::<T>::check(value)?;

        unsafe {
            // Values are always zero-initialized so even if pointers are only 32 bits then the
            // whole 64 bit value will be 0 for NULL pointers.
            if value.inner.data[0].v_uint64 == 0 {
                return Err(Self::Error::UnexpectedNone);
            }
        }

        Ok(())
    }
}

// rustdoc-stripper-ignore-next
/// Trait to retrieve the contained value from a `Value`.
///
/// Usually this would not be used directly but from the [`get`](struct.Value.html#method.get)
/// function on a [`Value`](struct.Value.html)
pub unsafe trait FromValue<'a>: Sized {
    // rustdoc-stripper-ignore-next
    /// Value type checker.
    type Checker: ValueTypeChecker;

    // rustdoc-stripper-ignore-next
    /// Get the contained value from a `Value`.
    ///
    /// # Safety
    /// `Self::Checker::check()` must be called first and must not fail.
    unsafe fn from_value(value: &'a Value) -> Self;
}

// rustdoc-stripper-ignore-next
/// Trait for types that implement `FromValue` and are Optional.
///
/// This trait is auto-implemented for the appropriate types and is sealed.
pub trait FromValueOptional<'a>: private::FromValueOptionalSealed<'a> {}

impl<'a, T, C, E> FromValueOptional<'a> for T
where
    T: FromValue<'a, Checker = C>,
    C: ValueTypeChecker<Error = ValueTypeMismatchOrNoneError<E>>,
    E: error::Error + Send + Sized + 'static,
{
}

mod private {
    pub trait FromValueOptionalSealed<'a> {}

    impl<'a, T, C, E> FromValueOptionalSealed<'a> for T
    where
        T: super::FromValue<'a, Checker = C>,
        C: super::ValueTypeChecker<Error = super::ValueTypeMismatchOrNoneError<E>>,
        E: super::error::Error + Send + Sized + 'static,
    {
    }
}

// rustdoc-stripper-ignore-next
/// Wrapped `Value` type checker for optional types.
pub struct ValueTypeOrNoneChecker<T, C, E>(std::marker::PhantomData<(T, C, E)>);

unsafe impl<'a, T, C, E> ValueTypeChecker for ValueTypeOrNoneChecker<T, C, E>
where
    T: FromValue<'a, Checker = C> + StaticType,
    C: ValueTypeChecker<Error = ValueTypeMismatchOrNoneError<E>>,
    E: error::Error + Send + Sized + 'static,
{
    type Error = E;

    #[inline]
    fn check(value: &Value) -> Result<(), Self::Error> {
        match T::Checker::check(value) {
            Err(ValueTypeMismatchOrNoneError::UnexpectedNone) => Ok(()),
            Err(ValueTypeMismatchOrNoneError::WrongValueType(err)) => Err(err),
            Ok(_) => Ok(()),
        }
    }
}

// rustdoc-stripper-ignore-next
/// Blanket implementation for all optional types.
unsafe impl<'a, T, C, E> FromValue<'a> for Option<T>
where
    T: FromValue<'a, Checker = C> + StaticType,
    C: ValueTypeChecker<Error = ValueTypeMismatchOrNoneError<E>>,
    E: error::Error + Send + Sized + 'static,
{
    type Checker = ValueTypeOrNoneChecker<T, C, E>;

    #[inline]
    unsafe fn from_value(value: &'a Value) -> Self {
        match T::Checker::check(value) {
            Err(ValueTypeMismatchOrNoneError::UnexpectedNone) => None,
            Err(ValueTypeMismatchOrNoneError::WrongValueType(_err)) => {
                // This should've been caught by the caller already.
                unreachable!();
            }
            Ok(_) => Some(T::from_value(value)),
        }
    }
}

// rustdoc-stripper-ignore-next
/// Trait to convert a value to a `Value`.
///
/// Similar to other common conversion traits, the following invariants are guaranteed:
///
/// - **Invertibility**: `x.to_value().get().unwrap() == x`. In words, [`FromValue`] is the inverse of `ToValue`.
/// - **Idempotence**: `x.to_value() == x.to_value().to_value()`.
///   In words, applying `ToValue` multiple times yields the same result as applying it once.
///   Idempotence also applies the other way around: `value.get::<Value>()` is a no-op.
///
/// There is also the possibility to wrap values within values, see [`BoxedValue`]. All (un-)boxing needs to be done
/// manually, and will be preserved under the conversion methods.
///
/// The conversion methods may cause values to be cloned, which may result in reference counter changes or heap allocations depending
/// on the source and target type.
pub trait ToValue {
    // rustdoc-stripper-ignore-next
    /// Convert a value to a `Value`.
    fn to_value(&self) -> Value;

    // rustdoc-stripper-ignore-next
    /// Returns the type identifer of `self`.
    ///
    /// This is the type of the value to be returned by `to_value`.
    fn value_type(&self) -> Type;
}

// rustdoc-stripper-ignore-next
/// Blanket implementation for all references.
impl<T: ToValue + StaticType> ToValue for &T {
    #[inline]
    fn to_value(&self) -> Value {
        T::to_value(*self)
    }

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

// rustdoc-stripper-ignore-next
/// Trait to convert an `Option` to a `Value` for optional types.
pub trait ToValueOptional {
    // rustdoc-stripper-ignore-next
    /// Convert an `Option` to a `Value`.
    #[allow(clippy::wrong_self_convention)]
    fn to_value_optional(s: Option<&Self>) -> Value;
}

// rustdoc-stripper-ignore-next
/// Blanket implementation for all optional types.
impl<T: ToValueOptional + StaticType> ToValue for Option<T> {
    #[inline]
    fn to_value(&self) -> Value {
        T::to_value_optional(self.as_ref())
    }

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

impl<T: Into<Value> + ToValueOptional> From<Option<T>> for Value {
    #[inline]
    fn from(t: Option<T>) -> Self {
        match t {
            None => T::to_value_optional(None),
            Some(t) => t.into(),
        }
    }
}

impl<T: ToValueOptional + StaticType> StaticType for Option<T> {
    #[inline]
    fn static_type() -> Type {
        T::static_type()
    }
}

impl<T: ToValueOptional + StaticType + ?Sized> ToValueOptional for &T {
    #[inline]
    fn to_value_optional(s: Option<&Self>) -> Value {
        <T as ToValueOptional>::to_value_optional(s.as_ref().map(|s| **s))
    }
}

#[inline]
unsafe fn copy_value(value: *const gobject_ffi::GValue) -> *mut gobject_ffi::GValue {
    let copy = ffi::g_malloc0(mem::size_of::<gobject_ffi::GValue>()) as *mut gobject_ffi::GValue;
    copy_into_value(copy, value);
    copy
}

#[inline]
unsafe fn free_value(value: *mut gobject_ffi::GValue) {
    clear_value(value);
    ffi::g_free(value as *mut _);
}

#[inline]
unsafe fn init_value(value: *mut gobject_ffi::GValue) {
    ptr::write(value, mem::zeroed());
}

#[inline]
unsafe fn copy_into_value(dest: *mut gobject_ffi::GValue, src: *const gobject_ffi::GValue) {
    gobject_ffi::g_value_init(dest, (*src).g_type);
    gobject_ffi::g_value_copy(src, dest);
}

#[inline]
unsafe fn clear_value(value: *mut gobject_ffi::GValue) {
    // Before GLib 2.48, unsetting a zeroed GValue would give critical warnings
    // https://bugzilla.gnome.org/show_bug.cgi?id=755766
    if (*value).g_type != gobject_ffi::G_TYPE_INVALID {
        gobject_ffi::g_value_unset(value);
    }
}

// TODO: Should use impl !Send for Value {} once stable
crate::wrapper! {
    // rustdoc-stripper-ignore-next
    /// A generic value capable of carrying various types.
    ///
    /// Once created the type of the value can't be changed.
    ///
    /// Some types (e.g. `String` and objects) support `None` values while others
    /// (e.g. numeric types) don't.
    ///
    /// `Value` does not implement the `Send` trait, but [`SendValue`](struct.SendValue.html) can be
    /// used instead.
    ///
    /// See the [module documentation](index.html) for more details.
    #[doc(alias = "GValue")]
    pub struct Value(BoxedInline<gobject_ffi::GValue>);

    match fn {
        copy => |ptr| copy_value(ptr),
        free => |ptr| free_value(ptr),
        init => |ptr| init_value(ptr),
        copy_into => |dest, src| copy_into_value(dest, src),
        clear => |ptr| clear_value(ptr),
    }
}

impl Value {
    // rustdoc-stripper-ignore-next
    /// Creates a new `Value` that is initialized with `type_`.
    ///
    /// # Panics
    ///
    /// If `type_` can't be stored in a `Value` this function panics.
    pub fn from_type(type_: Type) -> Self {
        unsafe {
            assert_eq!(
                gobject_ffi::g_type_check_is_value_type(type_.into_glib()),
                ffi::GTRUE
            );
            Self::from_type_unchecked(type_)
        }
    }

    // rustdoc-stripper-ignore-next
    /// Creates a new `Value` that is initialized with `type_`.
    ///
    /// # SAFETY
    ///
    /// This must be called with a valid `type_` that can be stored in `Value`s.
    #[inline]
    pub unsafe fn from_type_unchecked(type_: Type) -> Self {
        unsafe {
            let mut value = Value::uninitialized();
            gobject_ffi::g_value_init(value.to_glib_none_mut().0, type_.into_glib());
            value
        }
    }

    // rustdoc-stripper-ignore-next
    /// Creates a new `Value` that is initialized for a given `ValueType`.
    #[inline]
    pub fn for_value_type<T: ValueType>() -> Self {
        unsafe { Value::from_type_unchecked(T::Type::static_type()) }
    }

    // rustdoc-stripper-ignore-next
    /// Tries to get a value of type `T`.
    ///
    /// Returns `Ok` if the type is correct.
    #[inline]
    pub fn get<'a, T>(&'a self) -> Result<T, <<T as FromValue>::Checker as ValueTypeChecker>::Error>
    where
        T: FromValue<'a>,
    {
        unsafe {
            T::Checker::check(self)?;
            Ok(T::from_value(self))
        }
    }

    // rustdoc-stripper-ignore-next
    /// Tries to get a value of an owned type `T`.
    #[inline]
    pub fn get_owned<T>(&self) -> Result<T, <<T as FromValue>::Checker as ValueTypeChecker>::Error>
    where
        T: for<'b> FromValue<'b> + 'static,
    {
        unsafe {
            T::Checker::check(self)?;
            Ok(FromValue::from_value(self))
        }
    }

    // rustdoc-stripper-ignore-next
    /// Returns `true` if the type of the value corresponds to `T`
    /// or is a sub-type of `T`.
    #[inline]
    pub fn is<T: StaticType>(&self) -> bool {
        self.is_type(T::static_type())
    }

    // rustdoc-stripper-ignore-next
    /// Returns `true` if the type of the value corresponds to `type_`
    /// or is a sub-type of `type_`.
    #[inline]
    pub fn is_type(&self, type_: Type) -> bool {
        self.type_().is_a(type_)
    }

    // rustdoc-stripper-ignore-next
    /// Returns the type of the value.
    #[inline]
    pub fn type_(&self) -> Type {
        unsafe { from_glib(self.inner.g_type) }
    }

    // rustdoc-stripper-ignore-next
    /// Returns whether `Value`s of type `src` can be transformed to type `dst`.
    #[doc(alias = "g_value_type_transformable")]
    pub fn type_transformable(src: Type, dst: Type) -> bool {
        unsafe {
            from_glib(gobject_ffi::g_value_type_transformable(
                src.into_glib(),
                dst.into_glib(),
            ))
        }
    }

    // rustdoc-stripper-ignore-next
    /// Tries to transform the value into a value of the target type
    #[doc(alias = "g_value_transform")]
    pub fn transform<T: ValueType>(&self) -> Result<Value, crate::BoolError> {
        self.transform_with_type(T::Type::static_type())
    }

    // rustdoc-stripper-ignore-next
    /// Tries to transform the value into a value of the target type
    #[doc(alias = "g_value_transform")]
    pub fn transform_with_type(&self, type_: Type) -> Result<Value, crate::BoolError> {
        unsafe {
            let mut dest = Value::from_type(type_);
            if from_glib(gobject_ffi::g_value_transform(
                self.to_glib_none().0,
                dest.to_glib_none_mut().0,
            )) {
                Ok(dest)
            } else {
                Err(crate::bool_error!(
                    "Can't transform value of type '{}' into '{}'",
                    self.type_(),
                    type_
                ))
            }
        }
    }

    // rustdoc-stripper-ignore-next
    /// Consumes `Value` and returns the corresponding `GValue`.
    #[inline]
    pub fn into_raw(self) -> gobject_ffi::GValue {
        unsafe {
            let s = mem::ManuallyDrop::new(self);
            ptr::read(&s.inner)
        }
    }

    #[inline]
    pub fn try_into_send_value<T: Send + StaticType>(self) -> Result<SendValue, Self> {
        if self.type_().is_a(T::static_type()) {
            unsafe { Ok(SendValue::unsafe_from(self.into_raw())) }
        } else {
            Err(self)
        }
    }

    fn content_debug_string(&self) -> GString {
        unsafe { from_glib_full(gobject_ffi::g_strdup_value_contents(self.to_glib_none().0)) }
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "({}) {}", self.type_(), self.content_debug_string())
    }
}

impl<'a, T: ?Sized + ToValue> From<&'a T> for Value {
    #[inline]
    fn from(value: &'a T) -> Self {
        value.to_value()
    }
}

impl From<SendValue> for Value {
    #[inline]
    fn from(value: SendValue) -> Self {
        unsafe { Value::unsafe_from(value.into_raw()) }
    }
}

impl ToValue for Value {
    #[inline]
    fn to_value(&self) -> Value {
        self.clone()
    }

    #[inline]
    fn value_type(&self) -> Type {
        self.type_()
    }
}

impl<'a> ToValue for &'a Value {
    #[inline]
    fn to_value(&self) -> Value {
        (*self).clone()
    }

    #[inline]
    fn value_type(&self) -> Type {
        self.type_()
    }
}

pub struct NopChecker;

unsafe impl ValueTypeChecker for NopChecker {
    type Error = Infallible;

    #[inline]
    fn check(_value: &Value) -> Result<(), Self::Error> {
        Ok(())
    }
}

unsafe impl<'a> FromValue<'a> for Value {
    type Checker = NopChecker;

    #[inline]
    unsafe fn from_value(value: &'a Value) -> Self {
        value.clone()
    }
}

unsafe impl<'a> FromValue<'a> for &'a Value {
    type Checker = NopChecker;

    #[inline]
    unsafe fn from_value(value: &'a Value) -> Self {
        value
    }
}

impl ToValue for SendValue {
    #[inline]
    fn to_value(&self) -> Value {
        unsafe { from_glib_none(self.to_glib_none().0) }
    }

    #[inline]
    fn value_type(&self) -> Type {
        self.type_()
    }
}

impl<'a> ToValue for &'a SendValue {
    #[inline]
    fn to_value(&self) -> Value {
        unsafe { from_glib_none(self.to_glib_none().0) }
    }

    #[inline]
    fn value_type(&self) -> Type {
        self.type_()
    }
}

impl StaticType for BoxedValue {
    #[inline]
    fn static_type() -> Type {
        unsafe { from_glib(gobject_ffi::g_value_get_type()) }
    }
}

crate::wrapper! {
    // rustdoc-stripper-ignore-next
    /// A version of [`Value`](struct.Value.html) for storing `Send` types, that implements Send
    /// itself.
    ///
    /// See the [module documentation](index.html) for more details.
    #[doc(alias = "GValue")]
    pub struct SendValue(BoxedInline<gobject_ffi::GValue>);

    match fn {
        copy => |ptr| copy_value(ptr),
        free => |ptr| free_value(ptr),
        init => |ptr| init_value(ptr),
        copy_into => |dest, src| copy_into_value(dest, src),
        clear => |ptr| clear_value(ptr),
    }
}

unsafe impl Send for SendValue {}

impl SendValue {
    // rustdoc-stripper-ignore-next
    /// Consumes `SendValue` and returns the corresponding `GValue`.
    #[inline]
    pub fn into_raw(self) -> gobject_ffi::GValue {
        unsafe {
            let s = mem::ManuallyDrop::new(self);
            ptr::read(&s.inner)
        }
    }
    #[inline]
    pub fn from_owned<T: Send + Into<Value> + ?Sized>(t: T) -> Self {
        unsafe { Self::unsafe_from(t.into().into_raw()) }
    }
}

impl fmt::Debug for SendValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "({}) {}", self.type_(), self.content_debug_string())
    }
}

impl Deref for SendValue {
    type Target = Value;

    #[inline]
    fn deref(&self) -> &Value {
        unsafe { &*(self as *const SendValue as *const Value) }
    }
}

impl<'a, T: ?Sized + ToSendValue> From<&'a T> for SendValue {
    #[inline]
    fn from(value: &'a T) -> Self {
        value.to_send_value()
    }
}

// rustdoc-stripper-ignore-next
/// Converts to `SendValue`.
pub trait ToSendValue: Send + ToValue {
    // rustdoc-stripper-ignore-next
    /// Returns a `SendValue` clone of `self`.
    fn to_send_value(&self) -> SendValue;
}

impl<T: Send + ToValue + ?Sized> ToSendValue for T {
    #[inline]
    fn to_send_value(&self) -> SendValue {
        unsafe { SendValue::unsafe_from(self.to_value().into_raw()) }
    }
}

unsafe impl<'a> FromValue<'a> for &'a str {
    type Checker = GenericValueTypeOrNoneChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a Value) -> Self {
        let ptr = gobject_ffi::g_value_get_string(value.to_glib_none().0);
        CStr::from_ptr(ptr).to_str().expect("Invalid UTF-8")
    }
}

impl ToValue for str {
    fn to_value(&self) -> Value {
        unsafe {
            let mut value = Value::for_value_type::<String>();

            gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, self.to_glib_full());

            value
        }
    }

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

impl ToValue for &str {
    fn to_value(&self) -> Value {
        (*self).to_value()
    }

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

impl ToValueOptional for str {
    fn to_value_optional(s: Option<&Self>) -> Value {
        let mut value = Value::for_value_type::<String>();
        unsafe {
            gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, s.to_glib_full());
        }

        value
    }
}

impl ValueType for String {
    type Type = String;
}

impl ValueTypeOptional for String {}

unsafe impl<'a> FromValue<'a> for String {
    type Checker = GenericValueTypeOrNoneChecker<Self>;

    unsafe fn from_value(value: &'a Value) -> Self {
        String::from(<&str>::from_value(value))
    }
}

impl ToValue for String {
    fn to_value(&self) -> Value {
        <&str>::to_value(&self.as_str())
    }

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

impl From<String> for Value {
    #[inline]
    fn from(s: String) -> Self {
        s.to_value()
    }
}

impl ToValueOptional for String {
    fn to_value_optional(s: Option<&Self>) -> Value {
        <str>::to_value_optional(s.as_ref().map(|s| s.as_str()))
    }
}

impl ValueType for Box<str> {
    type Type = String;
}

impl ValueTypeOptional for Box<str> {}

unsafe impl<'a> FromValue<'a> for Box<str> {
    type Checker = GenericValueTypeOrNoneChecker<Self>;

    unsafe fn from_value(value: &'a Value) -> Self {
        Box::<str>::from(<&str>::from_value(value))
    }
}

impl StaticType for Box<str> {
    fn static_type() -> Type {
        String::static_type()
    }
}

impl ToValue for Box<str> {
    fn to_value(&self) -> Value {
        <&str>::to_value(&self.as_ref())
    }

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

impl From<Box<str>> for Value {
    #[inline]
    fn from(s: Box<str>) -> Self {
        s.to_value()
    }
}

impl ToValueOptional for Box<str> {
    fn to_value_optional(s: Option<&Self>) -> Value {
        <str>::to_value_optional(s.as_ref().map(|s| s.as_ref()))
    }
}

impl ValueType for Vec<String> {
    type Type = Vec<String>;
}

unsafe impl<'a> FromValue<'a> for Vec<String> {
    type Checker = GenericValueTypeChecker<Self>;

    unsafe fn from_value(value: &'a Value) -> Self {
        let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
        FromGlibPtrContainer::from_glib_none(ptr)
    }
}

impl ToValue for Vec<String> {
    fn to_value(&self) -> Value {
        unsafe {
            let mut value = Value::for_value_type::<Self>();
            let ptr: *mut *mut c_char = self.to_glib_full();
            gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, ptr as *const c_void);
            value
        }
    }

    fn value_type(&self) -> Type {
        <Vec<String>>::static_type()
    }
}

impl From<Vec<String>> for Value {
    #[inline]
    fn from(s: Vec<String>) -> Self {
        s.to_value()
    }
}

impl<'a> ToValue for [&'a str] {
    fn to_value(&self) -> Value {
        unsafe {
            let mut value = Value::for_value_type::<Vec<String>>();
            let ptr: *mut *mut c_char = self.to_glib_full();
            gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, ptr as *const c_void);
            value
        }
    }

    fn value_type(&self) -> Type {
        <Vec<String>>::static_type()
    }
}

impl<'a> ToValue for &'a [&'a str] {
    fn to_value(&self) -> Value {
        unsafe {
            let mut value = Value::for_value_type::<Vec<String>>();
            let ptr: *mut *mut c_char = self.to_glib_full();
            gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, ptr as *const c_void);
            value
        }
    }

    fn value_type(&self) -> Type {
        <Vec<String>>::static_type()
    }
}

impl ToValue for Path {
    fn to_value(&self) -> Value {
        unsafe {
            let mut value = Value::for_value_type::<PathBuf>();

            gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, self.to_glib_full());

            value
        }
    }

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

impl ToValue for &Path {
    fn to_value(&self) -> Value {
        (*self).to_value()
    }

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

impl ToValueOptional for Path {
    fn to_value_optional(s: Option<&Self>) -> Value {
        let mut value = Value::for_value_type::<PathBuf>();
        unsafe {
            gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, s.to_glib_full());
        }

        value
    }
}

impl ValueType for PathBuf {
    type Type = PathBuf;
}

impl ValueTypeOptional for PathBuf {}

unsafe impl<'a> FromValue<'a> for PathBuf {
    type Checker = GenericValueTypeOrNoneChecker<Self>;

    unsafe fn from_value(value: &'a Value) -> Self {
        from_glib_none(gobject_ffi::g_value_get_string(value.to_glib_none().0))
    }
}

impl ToValue for PathBuf {
    fn to_value(&self) -> Value {
        <&Path>::to_value(&self.as_path())
    }

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

impl From<PathBuf> for Value {
    #[inline]
    fn from(s: PathBuf) -> Self {
        s.to_value()
    }
}

impl ToValueOptional for PathBuf {
    fn to_value_optional(s: Option<&Self>) -> Value {
        <Path>::to_value_optional(s.as_ref().map(|s| s.as_path()))
    }
}

impl ValueType for bool {
    type Type = Self;
}

unsafe impl<'a> FromValue<'a> for bool {
    type Checker = GenericValueTypeChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a Value) -> Self {
        from_glib(gobject_ffi::g_value_get_boolean(value.to_glib_none().0))
    }
}

impl ToValue for bool {
    #[inline]
    fn to_value(&self) -> Value {
        let mut value = Value::for_value_type::<Self>();
        unsafe {
            gobject_ffi::g_value_set_boolean(&mut value.inner, self.into_glib());
        }
        value
    }

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

impl From<bool> for Value {
    #[inline]
    fn from(v: bool) -> Self {
        v.to_value()
    }
}

impl ValueType for Pointer {
    type Type = Self;
}

unsafe impl<'a> FromValue<'a> for Pointer {
    type Checker = GenericValueTypeChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a Value) -> Self {
        gobject_ffi::g_value_get_pointer(value.to_glib_none().0)
    }
}

impl ToValue for Pointer {
    #[inline]
    fn to_value(&self) -> Value {
        let mut value = Value::for_value_type::<Self>();
        unsafe {
            gobject_ffi::g_value_set_pointer(&mut value.inner, *self);
        }
        value
    }

    #[inline]
    fn value_type(&self) -> Type {
        <<Self as ValueType>::Type as StaticType>::static_type()
    }
}

impl From<Pointer> for Value {
    #[inline]
    fn from(v: Pointer) -> Self {
        v.to_value()
    }
}

impl ValueType for ptr::NonNull<Pointee> {
    type Type = Pointer;
}

unsafe impl<'a> FromValue<'a> for ptr::NonNull<Pointee> {
    type Checker = GenericValueTypeOrNoneChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a Value) -> Self {
        ptr::NonNull::new_unchecked(Pointer::from_value(value))
    }
}

impl ToValue for ptr::NonNull<Pointee> {
    #[inline]
    fn to_value(&self) -> Value {
        self.as_ptr().to_value()
    }

    #[inline]
    fn value_type(&self) -> Type {
        <<Self as ValueType>::Type as StaticType>::static_type()
    }
}

impl From<ptr::NonNull<Pointee>> for Value {
    #[inline]
    fn from(v: ptr::NonNull<Pointee>) -> Self {
        v.to_value()
    }
}

impl ToValueOptional for ptr::NonNull<Pointee> {
    #[inline]
    fn to_value_optional(p: Option<&Self>) -> Value {
        p.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut()).to_value()
    }
}

macro_rules! numeric {
    ($name:ty, $get:expr, $set:expr) => {
        impl ValueType for $name {
            type Type = Self;
        }

        unsafe impl<'a> FromValue<'a> for $name {
            type Checker = GenericValueTypeChecker<Self>;

            #[inline]
            unsafe fn from_value(value: &'a Value) -> Self {
                $get(value.to_glib_none().0)
            }
        }

        impl ToValue for $name {
            #[inline]
            fn to_value(&self) -> Value {
                let mut value = Value::for_value_type::<Self>();
                unsafe {
                    $set(&mut value.inner, *self);
                }
                value
            }

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

        impl From<$name> for Value {
            #[inline]
            fn from(v: $name) -> Self {
                v.to_value()
            }
        }
    };
}
macro_rules! not_zero {
    ($name:ty, $num:ty) => {
        impl ValueType for $name {
            type Type = $name;
        }

        unsafe impl<'a> FromValue<'a> for $name {
            // Works because it returns `UnexpectedNone` if the value is NULL
            // by checking it against `0`.
            type Checker = GenericValueTypeOrNoneChecker<Self>;

            #[inline]
            unsafe fn from_value(value: &'a Value) -> Self {
                let res = <$num>::from_value(value);
                Self::try_from(res).unwrap()
            }
        }

        impl ToValue for $name {
            #[inline]
            fn to_value(&self) -> Value {
                <$num>::to_value(&<$num>::from(*self))
            }

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

        impl From<$name> for Value {
            #[inline]
            fn from(v: $name) -> Self {
                v.to_value()
            }
        }

        impl ToValueOptional for $name {
            fn to_value_optional(s: Option<&Self>) -> Value {
                match s {
                    Some(x) => x.to_value(),
                    None => <$num>::to_value(&0),
                }
            }
        }
    };
}

numeric!(
    i8,
    gobject_ffi::g_value_get_schar,
    gobject_ffi::g_value_set_schar
);
not_zero!(NonZeroI8, i8);
numeric!(
    u8,
    gobject_ffi::g_value_get_uchar,
    gobject_ffi::g_value_set_uchar
);
not_zero!(NonZeroU8, u8);
numeric!(
    i32,
    gobject_ffi::g_value_get_int,
    gobject_ffi::g_value_set_int
);
not_zero!(NonZeroI32, i32);
numeric!(
    u32,
    gobject_ffi::g_value_get_uint,
    gobject_ffi::g_value_set_uint
);
not_zero!(NonZeroU32, u32);
numeric!(
    i64,
    gobject_ffi::g_value_get_int64,
    gobject_ffi::g_value_set_int64
);
not_zero!(NonZeroI64, i64);
numeric!(
    u64,
    gobject_ffi::g_value_get_uint64,
    gobject_ffi::g_value_set_uint64
);
not_zero!(NonZeroU64, u64);
numeric!(
    crate::ILong,
    |v| gobject_ffi::g_value_get_long(v).into(),
    |v, i: crate::ILong| gobject_ffi::g_value_set_long(v, i.0)
);
numeric!(
    crate::ULong,
    |v| gobject_ffi::g_value_get_ulong(v).into(),
    |v, i: crate::ULong| gobject_ffi::g_value_set_ulong(v, i.0)
);
numeric!(
    f32,
    gobject_ffi::g_value_get_float,
    gobject_ffi::g_value_set_float
);
numeric!(
    f64,
    gobject_ffi::g_value_get_double,
    gobject_ffi::g_value_set_double
);

impl ValueType for char {
    type Type = u32;
}

unsafe impl<'a> FromValue<'a> for char {
    type Checker = CharTypeChecker;

    #[inline]
    unsafe fn from_value(value: &'a Value) -> Self {
        let res: u32 = gobject_ffi::g_value_get_uint(value.to_glib_none().0);
        // safe because the check is done by `Self::Checker`
        char::from_u32_unchecked(res)
    }
}

impl ToValue for char {
    #[inline]
    fn to_value(&self) -> Value {
        let mut value = Value::for_value_type::<Self>();
        unsafe {
            gobject_ffi::g_value_set_uint(&mut value.inner, *self as u32);
        }
        value
    }

    #[inline]
    fn value_type(&self) -> Type {
        crate::Type::U32
    }
}

impl From<char> for Value {
    #[inline]
    fn from(v: char) -> Self {
        v.to_value()
    }
}

// rustdoc-stripper-ignore-next
/// A [`Value`] containing another [`Value`].
pub struct BoxedValue(pub Value);

impl Deref for BoxedValue {
    type Target = Value;

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

impl ValueType for BoxedValue {
    type Type = BoxedValue;
}

impl ValueTypeOptional for BoxedValue {}

unsafe impl<'a> FromValue<'a> for BoxedValue {
    type Checker = GenericValueTypeOrNoneChecker<Self>;

    #[inline]
    unsafe fn from_value(value: &'a Value) -> Self {
        let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0);
        BoxedValue(from_glib_none(ptr as *const gobject_ffi::GValue))
    }
}

impl ToValue for BoxedValue {
    #[inline]
    fn to_value(&self) -> Value {
        unsafe {
            let mut value = Value::for_value_type::<BoxedValue>();

            gobject_ffi::g_value_set_boxed(
                value.to_glib_none_mut().0,
                self.0.to_glib_none().0 as ffi::gconstpointer,
            );

            value
        }
    }

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

impl From<BoxedValue> for Value {
    #[inline]
    fn from(v: BoxedValue) -> Self {
        unsafe {
            let mut value = Value::for_value_type::<BoxedValue>();

            gobject_ffi::g_value_take_boxed(
                value.to_glib_none_mut().0,
                v.0.to_glib_full() as ffi::gconstpointer,
            );

            value
        }
    }
}

impl ToValueOptional for BoxedValue {
    #[inline]
    fn to_value_optional(s: Option<&Self>) -> Value {
        let mut value = Value::for_value_type::<Self>();
        unsafe {
            gobject_ffi::g_value_set_boxed(
                value.to_glib_none_mut().0,
                s.map(|s| &s.0).to_glib_none().0 as ffi::gconstpointer,
            );
        }

        value
    }
}

#[cfg(test)]
mod tests {
    use std::num::NonZeroI32;

    use super::*;

    #[test]
    fn test_send_value() {
        use std::thread;

        let v = SendValue::from(&1i32);

        // Must compile, while it must fail with Value
        thread::spawn(move || drop(v)).join().unwrap();
    }

    #[test]
    fn test_strv() {
        let v = vec!["123", "456"].to_value();
        assert_eq!(
            v.get::<Vec<GString>>(),
            Ok(vec![GString::from("123"), GString::from("456")])
        );

        let v = vec![String::from("123"), String::from("456")].to_value();
        assert_eq!(
            v.get::<Vec<GString>>(),
            Ok(vec![GString::from("123"), GString::from("456")])
        );
    }

    #[test]
    fn test_from_to_value() {
        let v = 123.to_value();
        assert_eq!(v.get(), Ok(123));
        assert_eq!(
            v.get::<&str>(),
            Err(ValueTypeMismatchError::new(Type::I32, Type::STRING).into())
        );
        assert_eq!(
            v.get::<bool>(),
            Err(ValueTypeMismatchError::new(Type::I32, Type::BOOL))
        );

        // Check if &str / str / Option<&str> etc can be converted and retrieved
        let v_str = "test".to_value();
        assert_eq!(v_str.get::<&str>(), Ok("test"));
        assert_eq!(v_str.get::<Option<&str>>(), Ok(Some("test")));
        assert_eq!(
            v_str.get::<i32>(),
            Err(ValueTypeMismatchError::new(Type::STRING, Type::I32))
        );

        let some_v = Some("test").to_value();
        assert_eq!(some_v.get::<&str>(), Ok("test"));
        assert_eq!(some_v.get_owned::<String>(), Ok("test".to_string()));
        assert_eq!(
            some_v.get_owned::<Option<String>>(),
            Ok(Some("test".to_string()))
        );
        assert_eq!(some_v.get::<Option<&str>>(), Ok(Some("test")));
        assert_eq!(
            some_v.get::<i32>(),
            Err(ValueTypeMismatchError::new(Type::STRING, Type::I32))
        );

        let none_str: Option<&str> = None;
        let none_v = none_str.to_value();
        assert_eq!(none_v.get::<Option<&str>>(), Ok(None));
        assert_eq!(
            none_v.get::<i32>(),
            Err(ValueTypeMismatchError::new(Type::STRING, Type::I32))
        );

        // Check if owned T and Option<T> can be converted and retrieved
        let v_str = String::from("test").to_value();
        assert_eq!(v_str.get::<String>(), Ok(String::from("test")));
        assert_eq!(
            v_str.get::<Option<String>>(),
            Ok(Some(String::from("test")))
        );
        assert_eq!(
            v_str.get::<i32>(),
            Err(ValueTypeMismatchError::new(Type::STRING, Type::I32))
        );

        let some_v = Some(String::from("test")).to_value();
        assert_eq!(some_v.get::<String>(), Ok(String::from("test")));
        assert_eq!(
            some_v.get::<Option<String>>(),
            Ok(Some(String::from("test")))
        );
        assert_eq!(
            some_v.get::<i32>(),
            Err(ValueTypeMismatchError::new(Type::STRING, Type::I32))
        );

        let none_str: Option<String> = None;
        let none_v = none_str.to_value();
        assert_eq!(none_v.get::<Option<String>>(), Ok(None));
        assert_eq!(
            none_v.get::<i32>(),
            Err(ValueTypeMismatchError::new(Type::STRING, Type::I32))
        );

        let c_v = 'c'.to_value();
        assert_eq!(c_v.get::<char>(), Ok('c'));

        let c_v = 0xFFFFFFFFu32.to_value();
        assert_eq!(
            c_v.get::<char>(),
            Err(InvalidCharError::CharConversionError)
        );

        // Check if &T and Option<&T> can be converted and retrieved
        let v_str = String::from("test").to_value();
        assert_eq!(v_str.get::<String>(), Ok(String::from("test")));
        assert_eq!(
            v_str.get::<Option<String>>(),
            Ok(Some(String::from("test")))
        );
        assert_eq!(
            v_str.get::<i32>(),
            Err(ValueTypeMismatchError::new(Type::STRING, Type::I32))
        );

        let some_v = Some(&String::from("test")).to_value();
        assert_eq!(some_v.get::<String>(), Ok(String::from("test")));
        assert_eq!(
            some_v.get::<Option<String>>(),
            Ok(Some(String::from("test")))
        );
        assert_eq!(
            some_v.get::<i32>(),
            Err(ValueTypeMismatchError::new(Type::STRING, Type::I32))
        );

        let none_str: Option<&String> = None;
        let none_v = none_str.to_value();
        assert_eq!(none_v.get::<Option<String>>(), Ok(None));
        assert_eq!(
            none_v.get::<i32>(),
            Err(ValueTypeMismatchError::new(Type::STRING, Type::I32))
        );

        // Check handling of NonZeroT
        let v = NonZeroI32::new(123).unwrap().to_value();
        assert_eq!(v.get::<NonZeroI32>(), Ok(NonZeroI32::new(123).unwrap()));

        let v = 123i32.to_value();
        assert_eq!(v.get::<NonZeroI32>(), Ok(NonZeroI32::new(123).unwrap()));

        let v = 0i32.to_value();
        assert_eq!(
            v.get::<NonZeroI32>(),
            Err(ValueTypeMismatchOrNoneError::UnexpectedNone)
        );

        assert_eq!(v.get::<Option<NonZeroI32>>(), Ok(None));
    }

    #[test]
    fn test_transform() {
        let v = 123.to_value();
        let v2 = v
            .transform::<String>()
            .expect("Failed to transform to string");
        assert_eq!(v2.get::<&str>(), Ok("123"));
    }

    #[test]
    fn test_into_raw() {
        unsafe {
            let mut v = 123.to_value().into_raw();
            assert_eq!(gobject_ffi::g_type_check_value(&v), ffi::GTRUE);
            assert_eq!(gobject_ffi::g_value_get_int(&v), 123);
            gobject_ffi::g_value_unset(&mut v);
        }
    }

    #[test]
    fn test_debug() {
        fn value_debug_string<T: ToValue>(val: T) -> String {
            format!("{:?}", val.to_value())
        }

        assert_eq!(value_debug_string(1u32), "(guint) 1");
        assert_eq!(value_debug_string(2i32), "(gint) 2");
        assert_eq!(value_debug_string(false), "(gboolean) FALSE");
        assert_eq!(value_debug_string("FooBar"), r#"(gchararray) "FooBar""#);
    }
}
