// 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::translate::*;

use crate::{ffi, Frame, FrameRequest};

glib::wrapper! {
    #[doc(alias = "GlyImage")]
    pub struct Image(Object<ffi::GlyImage, ffi::GlyImageClass>);

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

impl Image {
    #[doc(alias = "gly_image_get_height")]
    #[doc(alias = "get_height")]
    pub fn height(&self) -> u32 {
        unsafe { ffi::gly_image_get_height(self.to_glib_none().0) }
    }

    #[doc(alias = "gly_image_get_metadata_key_value")]
    #[doc(alias = "get_metadata_key_value")]
    pub fn metadata_key_value(&self, key: &str) -> Option<glib::GString> {
        unsafe {
            from_glib_full(ffi::gly_image_get_metadata_key_value(
                self.to_glib_none().0,
                key.to_glib_none().0,
            ))
        }
    }

    #[doc(alias = "gly_image_get_metadata_keys")]
    #[doc(alias = "get_metadata_keys")]
    pub fn metadata_keys(&self) -> Vec<glib::GString> {
        unsafe {
            FromGlibPtrContainer::from_glib_full(ffi::gly_image_get_metadata_keys(
                self.to_glib_none().0,
            ))
        }
    }

    #[doc(alias = "gly_image_get_mime_type")]
    #[doc(alias = "get_mime_type")]
    pub fn mime_type(&self) -> glib::GString {
        unsafe { from_glib_none(ffi::gly_image_get_mime_type(self.to_glib_none().0)) }
    }

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

    #[doc(alias = "gly_image_get_specific_frame_async")]
    #[doc(alias = "get_specific_frame_async")]
    pub fn specific_frame_async<P: FnOnce(Result<Frame, glib::Error>) + 'static>(
        &self,
        frame_request: &FrameRequest,
        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 specific_frame_async_trampoline<
            P: FnOnce(Result<Frame, 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_image_get_specific_frame_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 = specific_frame_async_trampoline::<P>;
        unsafe {
            ffi::gly_image_get_specific_frame_async(
                self.to_glib_none().0,
                frame_request.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 specific_frame_future(
        &self,
        frame_request: &FrameRequest,
    ) -> Pin<Box_<dyn std::future::Future<Output = Result<Frame, glib::Error>> + 'static>> {
        let frame_request = frame_request.clone();
        Box_::pin(gio::GioFuture::new(self, move |obj, cancellable, send| {
            obj.specific_frame_async(&frame_request, Some(cancellable), move |res| {
                send.resolve(res);
            });
        }))
    }

    #[doc(alias = "gly_image_get_transformation_orientation")]
    #[doc(alias = "get_transformation_orientation")]
    pub fn transformation_orientation(&self) -> u16 {
        unsafe { ffi::gly_image_get_transformation_orientation(self.to_glib_none().0) }
    }

    #[doc(alias = "gly_image_get_width")]
    #[doc(alias = "get_width")]
    pub fn width(&self) -> u32 {
        unsafe { ffi::gly_image_get_width(self.to_glib_none().0) }
    }

    #[doc(alias = "gly_image_next_frame")]
    pub fn next_frame(&self) -> Result<Frame, glib::Error> {
        unsafe {
            let mut error = std::ptr::null_mut();
            let ret = ffi::gly_image_next_frame(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_image_next_frame_async")]
    pub fn next_frame_async<P: FnOnce(Result<Frame, 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 next_frame_async_trampoline<
            P: FnOnce(Result<Frame, 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_image_next_frame_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 = next_frame_async_trampoline::<P>;
        unsafe {
            ffi::gly_image_next_frame_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 next_frame_future(
        &self,
    ) -> Pin<Box_<dyn std::future::Future<Output = Result<Frame, glib::Error>> + 'static>> {
        Box_::pin(gio::GioFuture::new(self, move |obj, cancellable, send| {
            obj.next_frame_async(Some(cancellable), move |res| {
                send.resolve(res);
            });
        }))
    }
}
