// This file was generated by gir (https://github.com/gtk-rs/gir)
// from
// from gir-files
// DO NOT EDIT

use std::boxed::Box as Box_;
use std::pin::Pin;

use glib::prelude::*;
use glib::signal::{connect_raw, SignalHandlerId};
use glib::translate::*;

use crate::{ffi, Image, MemoryFormatSelection, SandboxSelector};

glib::wrapper! {
    #[doc(alias = "GlyLoader")]
    pub struct Loader(Object<ffi::GlyLoader, ffi::GlyLoaderClass>);

    match fn {
        type_ => || ffi::gly_loader_get_type(),
    }
}

impl Loader {
    #[doc(alias = "gly_loader_new")]
    pub fn new(file: &impl IsA<gio::File>) -> Loader {
        assert_initialized_main_thread!();
        unsafe { from_glib_full(ffi::gly_loader_new(file.as_ref().to_glib_none().0)) }
    }

    #[doc(alias = "gly_loader_new_for_bytes")]
    #[doc(alias = "new_for_bytes")]
    pub fn for_bytes(bytes: &glib::Bytes) -> Loader {
        assert_initialized_main_thread!();
        unsafe { from_glib_full(ffi::gly_loader_new_for_bytes(bytes.to_glib_none().0)) }
    }

    #[doc(alias = "gly_loader_new_for_stream")]
    #[doc(alias = "new_for_stream")]
    pub fn for_stream(stream: &impl IsA<gio::InputStream>) -> Loader {
        assert_initialized_main_thread!();
        unsafe {
            from_glib_full(ffi::gly_loader_new_for_stream(
                stream.as_ref().to_glib_none().0,
            ))
        }
    }

    // rustdoc-stripper-ignore-next
    /// Creates a new builder-pattern struct instance to construct [`Loader`]
    /// objects.
    ///
    /// This method returns an instance of
    /// [`LoaderBuilder`](crate::builders::LoaderBuilder) which can be used to
    /// create [`Loader`] objects.
    pub fn builder() -> LoaderBuilder {
        LoaderBuilder::new()
    }

    #[doc(alias = "gly_loader_load")]
    pub fn load(&self) -> Result<Image, glib::Error> {
        unsafe {
            let mut error = std::ptr::null_mut();
            let ret = ffi::gly_loader_load(self.to_glib_none().0, &mut error);
            if error.is_null() {
                Ok(from_glib_full(ret))
            } else {
                Err(from_glib_full(error))
            }
        }
    }

    #[doc(alias = "gly_loader_load_async")]
    pub fn load_async<P: FnOnce(Result<Image, glib::Error>) + 'static>(
        &self,
        cancellable: Option<&impl IsA<gio::Cancellable>>,
        callback: P,
    ) {
        let main_context = glib::MainContext::ref_thread_default();
        let is_main_context_owner = main_context.is_owner();
        let has_acquired_main_context = (!is_main_context_owner)
            .then(|| main_context.acquire().ok())
            .flatten();
        assert!(
            is_main_context_owner || has_acquired_main_context.is_some(),
            "Async operations only allowed if the thread is owning the MainContext"
        );

        let user_data: Box_<glib::thread_guard::ThreadGuard<P>> =
            Box_::new(glib::thread_guard::ThreadGuard::new(callback));
        unsafe extern "C" fn load_async_trampoline<
            P: FnOnce(Result<Image, glib::Error>) + 'static,
        >(
            _source_object: *mut glib::gobject_ffi::GObject,
            res: *mut gio::ffi::GAsyncResult,
            user_data: glib::ffi::gpointer,
        ) {
            let mut error = std::ptr::null_mut();
            let ret = ffi::gly_loader_load_finish(_source_object as *mut _, res, &mut error);
            let result = if error.is_null() {
                Ok(from_glib_full(ret))
            } else {
                Err(from_glib_full(error))
            };
            let callback: Box_<glib::thread_guard::ThreadGuard<P>> =
                Box_::from_raw(user_data as *mut _);
            let callback: P = callback.into_inner();
            callback(result);
        }
        let callback = load_async_trampoline::<P>;
        unsafe {
            ffi::gly_loader_load_async(
                self.to_glib_none().0,
                cancellable.map(|p| p.as_ref()).to_glib_none().0,
                Some(callback),
                Box_::into_raw(user_data) as *mut _,
            );
        }
    }

    pub fn load_future(
        &self,
    ) -> Pin<Box_<dyn std::future::Future<Output = Result<Image, glib::Error>> + 'static>> {
        Box_::pin(gio::GioFuture::new(self, move |obj, cancellable, send| {
            obj.load_async(Some(cancellable), move |res| {
                send.resolve(res);
            });
        }))
    }

    #[doc(alias = "gly_loader_set_accepted_memory_formats")]
    pub fn set_accepted_memory_formats(&self, memory_format_selection: MemoryFormatSelection) {
        unsafe {
            ffi::gly_loader_set_accepted_memory_formats(
                self.to_glib_none().0,
                memory_format_selection.into_glib(),
            );
        }
    }

    #[doc(alias = "gly_loader_set_apply_transformations")]
    pub fn set_apply_transformations(&self, apply_transformations: bool) {
        unsafe {
            ffi::gly_loader_set_apply_transformations(
                self.to_glib_none().0,
                apply_transformations.into_glib(),
            );
        }
    }

    #[doc(alias = "gly_loader_set_sandbox_selector")]
    #[doc(alias = "sandbox-selector")]
    pub fn set_sandbox_selector(&self, sandbox_selector: SandboxSelector) {
        unsafe {
            ffi::gly_loader_set_sandbox_selector(
                self.to_glib_none().0,
                sandbox_selector.into_glib(),
            );
        }
    }

    #[doc(alias = "apply-transformation")]
    pub fn is_apply_transformation(&self) -> bool {
        ObjectExt::property(self, "apply-transformation")
    }

    #[doc(alias = "apply-transformation")]
    pub fn set_apply_transformation(&self, apply_transformation: bool) {
        ObjectExt::set_property(self, "apply-transformation", apply_transformation)
    }

    pub fn bytes(&self) -> Option<glib::Bytes> {
        ObjectExt::property(self, "bytes")
    }

    pub fn cancellable(&self) -> Option<gio::Cancellable> {
        ObjectExt::property(self, "cancellable")
    }

    pub fn set_cancellable<P: IsA<gio::Cancellable>>(&self, cancellable: Option<&P>) {
        ObjectExt::set_property(self, "cancellable", cancellable)
    }

    pub fn file(&self) -> Option<gio::File> {
        ObjectExt::property(self, "file")
    }

    #[doc(alias = "memory-format-selection")]
    pub fn memory_format_selection(&self) -> MemoryFormatSelection {
        ObjectExt::property(self, "memory-format-selection")
    }

    #[doc(alias = "memory-format-selection")]
    pub fn set_memory_format_selection(&self, memory_format_selection: MemoryFormatSelection) {
        ObjectExt::set_property(self, "memory-format-selection", memory_format_selection)
    }

    #[doc(alias = "sandbox-selector")]
    pub fn sandbox_selector(&self) -> SandboxSelector {
        ObjectExt::property(self, "sandbox-selector")
    }

    pub fn stream(&self) -> Option<gio::InputStream> {
        ObjectExt::property(self, "stream")
    }

    #[doc(alias = "gly_loader_get_mime_types")]
    #[doc(alias = "get_mime_types")]
    pub fn mime_types() -> Vec<glib::GString> {
        assert_initialized_main_thread!();
        unsafe { FromGlibPtrContainer::from_glib_full(ffi::gly_loader_get_mime_types()) }
    }

    #[doc(alias = "gly_loader_get_mime_types_async")]
    #[doc(alias = "get_mime_types_async")]
    pub fn mime_types_async<P: FnOnce(Result<Vec<glib::GString>, glib::Error>) + 'static>(
        cancellable: Option<&impl IsA<gio::Cancellable>>,
        callback: P,
    ) {
        assert_initialized_main_thread!();

        let main_context = glib::MainContext::ref_thread_default();
        let is_main_context_owner = main_context.is_owner();
        let has_acquired_main_context = (!is_main_context_owner)
            .then(|| main_context.acquire().ok())
            .flatten();
        assert!(
            is_main_context_owner || has_acquired_main_context.is_some(),
            "Async operations only allowed if the thread is owning the MainContext"
        );

        let user_data: Box_<glib::thread_guard::ThreadGuard<P>> =
            Box_::new(glib::thread_guard::ThreadGuard::new(callback));
        unsafe extern "C" fn mime_types_async_trampoline<
            P: FnOnce(Result<Vec<glib::GString>, glib::Error>) + 'static,
        >(
            _source_object: *mut glib::gobject_ffi::GObject,
            res: *mut gio::ffi::GAsyncResult,
            user_data: glib::ffi::gpointer,
        ) {
            let mut error = std::ptr::null_mut();
            let ret = ffi::gly_loader_get_mime_types_finish(res, &mut error);
            let result = if error.is_null() {
                Ok(FromGlibPtrContainer::from_glib_full(ret))
            } else {
                Err(from_glib_full(error))
            };
            let callback: Box_<glib::thread_guard::ThreadGuard<P>> =
                Box_::from_raw(user_data as *mut _);
            let callback: P = callback.into_inner();
            callback(result);
        }
        let callback = mime_types_async_trampoline::<P>;
        unsafe {
            ffi::gly_loader_get_mime_types_async(
                cancellable.map(|p| p.as_ref()).to_glib_none().0,
                Some(callback),
                Box_::into_raw(user_data) as *mut _,
            );
        }
    }

    pub fn mime_types_future() -> Pin<
        Box_<dyn std::future::Future<Output = Result<Vec<glib::GString>, glib::Error>> + 'static>,
    > {
        skip_assert_initialized!();
        Box_::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
            Self::mime_types_async(Some(cancellable), move |res| {
                send.resolve(res);
            });
        }))
    }

    #[doc(alias = "apply-transformation")]
    pub fn connect_apply_transformation_notify<F: Fn(&Self) + 'static>(
        &self,
        f: F,
    ) -> SignalHandlerId {
        unsafe extern "C" fn notify_apply_transformation_trampoline<F: Fn(&Loader) + 'static>(
            this: *mut ffi::GlyLoader,
            _param_spec: glib::ffi::gpointer,
            f: glib::ffi::gpointer,
        ) {
            let f: &F = &*(f as *const F);
            f(&from_glib_borrow(this))
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                c"notify::apply-transformation".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    notify_apply_transformation_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    #[doc(alias = "cancellable")]
    pub fn connect_cancellable_notify<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
        unsafe extern "C" fn notify_cancellable_trampoline<F: Fn(&Loader) + 'static>(
            this: *mut ffi::GlyLoader,
            _param_spec: glib::ffi::gpointer,
            f: glib::ffi::gpointer,
        ) {
            let f: &F = &*(f as *const F);
            f(&from_glib_borrow(this))
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                c"notify::cancellable".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    notify_cancellable_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    #[doc(alias = "memory-format-selection")]
    pub fn connect_memory_format_selection_notify<F: Fn(&Self) + 'static>(
        &self,
        f: F,
    ) -> SignalHandlerId {
        unsafe extern "C" fn notify_memory_format_selection_trampoline<F: Fn(&Loader) + 'static>(
            this: *mut ffi::GlyLoader,
            _param_spec: glib::ffi::gpointer,
            f: glib::ffi::gpointer,
        ) {
            let f: &F = &*(f as *const F);
            f(&from_glib_borrow(this))
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                c"notify::memory-format-selection".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    notify_memory_format_selection_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    #[doc(alias = "sandbox-selector")]
    pub fn connect_sandbox_selector_notify<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
        unsafe extern "C" fn notify_sandbox_selector_trampoline<F: Fn(&Loader) + 'static>(
            this: *mut ffi::GlyLoader,
            _param_spec: glib::ffi::gpointer,
            f: glib::ffi::gpointer,
        ) {
            let f: &F = &*(f as *const F);
            f(&from_glib_borrow(this))
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                c"notify::sandbox-selector".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    notify_sandbox_selector_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }
}

impl Default for Loader {
    fn default() -> Self {
        glib::object::Object::new::<Self>()
    }
}

// rustdoc-stripper-ignore-next
/// A [builder-pattern] type to construct [`Loader`] objects.
///
/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
#[must_use = "The builder must be built to be used"]
pub struct LoaderBuilder {
    builder: glib::object::ObjectBuilder<'static, Loader>,
}

impl LoaderBuilder {
    fn new() -> Self {
        Self {
            builder: glib::object::Object::builder(),
        }
    }

    pub fn apply_transformation(self, apply_transformation: bool) -> Self {
        Self {
            builder: self
                .builder
                .property("apply-transformation", apply_transformation),
        }
    }

    pub fn bytes(self, bytes: &glib::Bytes) -> Self {
        Self {
            builder: self.builder.property("bytes", bytes.clone()),
        }
    }

    pub fn cancellable(self, cancellable: &impl IsA<gio::Cancellable>) -> Self {
        Self {
            builder: self
                .builder
                .property("cancellable", cancellable.clone().upcast()),
        }
    }

    pub fn file(self, file: &impl IsA<gio::File>) -> Self {
        Self {
            builder: self.builder.property("file", file.clone().upcast()),
        }
    }

    pub fn memory_format_selection(self, memory_format_selection: MemoryFormatSelection) -> Self {
        Self {
            builder: self
                .builder
                .property("memory-format-selection", memory_format_selection),
        }
    }

    pub fn sandbox_selector(self, sandbox_selector: SandboxSelector) -> Self {
        Self {
            builder: self.builder.property("sandbox-selector", sandbox_selector),
        }
    }

    pub fn stream(self, stream: &impl IsA<gio::InputStream>) -> Self {
        Self {
            builder: self.builder.property("stream", stream.clone().upcast()),
        }
    }

    // rustdoc-stripper-ignore-next
    /// Build the [`Loader`].
    #[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"]
    pub fn build(self) -> Loader {
        assert_initialized_main_thread!();
        self.builder.build()
    }
}
