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

use crate::ActionGroup;
use glib::subclass::prelude::*;
use glib::translate::*;
use glib::{Cast, GString, ObjectExt, Quark, Variant, VariantType};
use once_cell::sync::Lazy;
use std::mem;
use std::ptr;

pub trait ActionGroupImpl: ObjectImpl {
    fn action_added(&self, action_group: &Self::Type, action_name: &str) {
        self.parent_action_added(action_group, action_name);
    }

    fn action_enabled_changed(&self, action_group: &Self::Type, action_name: &str, enabled: bool) {
        self.parent_action_enabled_changed(action_group, action_name, enabled);
    }

    fn action_removed(&self, action_group: &Self::Type, action_name: &str) {
        self.parent_action_removed(action_group, action_name);
    }

    fn action_state_changed(&self, action_group: &Self::Type, action_name: &str, state: &Variant) {
        self.parent_action_state_changed(action_group, action_name, state);
    }

    fn activate_action(
        &self,
        action_group: &Self::Type,
        action_name: &str,
        parameter: Option<&Variant>,
    ) {
        self.parent_activate_action(action_group, action_name, parameter);
    }

    fn change_action_state(&self, action_group: &Self::Type, action_name: &str, value: &Variant) {
        self.parent_change_action_state(action_group, action_name, value)
    }

    #[doc(alias = "get_action_enabled")]
    fn action_is_enabled(&self, action_group: &Self::Type, action_name: &str) -> bool {
        self.parent_action_is_enabled(action_group, action_name)
    }

    #[doc(alias = "get_action_parameter_type")]
    fn action_parameter_type(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<VariantType> {
        self.parent_action_parameter_type(action_group, action_name)
    }

    #[doc(alias = "get_action_state")]
    fn action_state(&self, action_group: &Self::Type, action_name: &str) -> Option<Variant> {
        self.parent_action_state(action_group, action_name)
    }

    #[doc(alias = "get_action_state_hint")]
    fn action_state_hint(&self, action_group: &Self::Type, action_name: &str) -> Option<Variant> {
        self.parent_action_state_hint(action_group, action_name)
    }

    #[doc(alias = "get_action_state_type")]
    fn action_state_type(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<VariantType> {
        self.parent_action_state_type(action_group, action_name)
    }

    fn has_action(&self, action_group: &Self::Type, action_name: &str) -> bool {
        self.parent_has_action(action_group, action_name)
    }

    fn list_actions(&self, action_group: &Self::Type) -> Vec<String>;
    fn query_action(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<(
        bool,
        Option<VariantType>,
        Option<VariantType>,
        Option<Variant>,
        Option<Variant>,
    )>;
}

pub trait ActionGroupImplExt: ObjectSubclass {
    fn parent_action_added(&self, action_group: &Self::Type, action_name: &str);
    fn parent_action_enabled_changed(
        &self,
        action_group: &Self::Type,
        action_name: &str,
        enabled: bool,
    );
    fn parent_action_removed(&self, action_group: &Self::Type, action_name: &str);
    fn parent_action_state_changed(
        &self,
        action_group: &Self::Type,
        action_name: &str,
        state: &Variant,
    );
    fn parent_activate_action(
        &self,
        action_group: &Self::Type,
        action_name: &str,
        parameter: Option<&Variant>,
    );
    fn parent_change_action_state(
        &self,
        action_group: &Self::Type,
        action_name: &str,
        value: &Variant,
    );
    fn parent_action_is_enabled(&self, action_group: &Self::Type, action_name: &str) -> bool;
    fn parent_action_parameter_type(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<VariantType>;
    fn parent_action_state(&self, action_group: &Self::Type, action_name: &str) -> Option<Variant>;
    fn parent_action_state_hint(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<Variant>;
    fn parent_action_state_type(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<VariantType>;
    fn parent_has_action(&self, action_group: &Self::Type, action_name: &str) -> bool;

    fn parent_list_actions(&self, action_group: &Self::Type) -> Vec<String>;
    fn parent_query_action(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<(
        bool,
        Option<VariantType>,
        Option<VariantType>,
        Option<Variant>,
        Option<Variant>,
    )>;
}

impl<T: ActionGroupImpl> ActionGroupImplExt for T {
    fn parent_action_added(&self, action_group: &Self::Type, action_name: &str) {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            if let Some(func) = (*parent_iface).action_added {
                func(
                    action_group
                        .unsafe_cast_ref::<ActionGroup>()
                        .to_glib_none()
                        .0,
                    action_name.to_glib_none().0,
                );
            }
        }
    }

    fn parent_action_enabled_changed(
        &self,
        action_group: &Self::Type,
        action_name: &str,
        enabled: bool,
    ) {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            if let Some(func) = (*parent_iface).action_enabled_changed {
                func(
                    action_group
                        .unsafe_cast_ref::<ActionGroup>()
                        .to_glib_none()
                        .0,
                    action_name.to_glib_none().0,
                    enabled.into_glib(),
                );
            }
        }
    }

    fn parent_action_removed(&self, action_group: &Self::Type, action_name: &str) {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            if let Some(func) = (*parent_iface).action_removed {
                func(
                    action_group
                        .unsafe_cast_ref::<ActionGroup>()
                        .to_glib_none()
                        .0,
                    action_name.to_glib_none().0,
                );
            }
        }
    }

    fn parent_action_state_changed(
        &self,
        action_group: &Self::Type,
        action_name: &str,
        state: &Variant,
    ) {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            if let Some(func) = (*parent_iface).action_state_changed {
                func(
                    action_group
                        .unsafe_cast_ref::<ActionGroup>()
                        .to_glib_none()
                        .0,
                    action_name.to_glib_none().0,
                    state.to_glib_none().0,
                );
            }
        }
    }

    fn parent_activate_action(
        &self,
        action_group: &Self::Type,
        action_name: &str,
        parameter: Option<&Variant>,
    ) {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .activate_action
                .expect("no parent \"activate_action\" implementation");
            func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
                action_name.to_glib_none().0,
                parameter.to_glib_none().0,
            );
        }
    }

    fn parent_change_action_state(
        &self,
        action_group: &Self::Type,
        action_name: &str,
        value: &Variant,
    ) {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .change_action_state
                .expect("no parent \"change_action_state\" implementation");
            func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
                action_name.to_glib_none().0,
                value.to_glib_none().0,
            );
        }
    }

    fn parent_action_is_enabled(&self, action_group: &Self::Type, action_name: &str) -> bool {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .get_action_enabled
                .expect("no parent \"action_is_enabled\" implementation");
            let ret = func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
                action_name.to_glib_none().0,
            );
            from_glib(ret)
        }
    }

    fn parent_action_parameter_type(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<VariantType> {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .get_action_parameter_type
                .expect("no parent \"get_action_parameter_type\" implementation");
            let ret = func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
                action_name.to_glib_none().0,
            );
            from_glib_none(ret)
        }
    }

    fn parent_action_state(&self, action_group: &Self::Type, action_name: &str) -> Option<Variant> {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .get_action_state
                .expect("no parent \"get_action_state\" implementation");
            let ret = func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
                action_name.to_glib_none().0,
            );
            from_glib_none(ret)
        }
    }

    fn parent_action_state_hint(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<Variant> {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .get_action_state_hint
                .expect("no parent \"get_action_state_hint\" implementation");
            let ret = func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
                action_name.to_glib_none().0,
            );
            from_glib_none(ret)
        }
    }

    fn parent_action_state_type(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<VariantType> {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .get_action_state_type
                .expect("no parent \"get_action_state_type\" implementation");
            let ret = func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
                action_name.to_glib_none().0,
            );
            from_glib_none(ret)
        }
    }

    fn parent_has_action(&self, action_group: &Self::Type, action_name: &str) -> bool {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .has_action
                .expect("no parent \"has_action\" implementation");
            let ret = func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
                action_name.to_glib_none().0,
            );
            from_glib(ret)
        }
    }

    fn parent_list_actions(&self, action_group: &Self::Type) -> Vec<String> {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .list_actions
                .expect("no parent \"list_actions\" implementation");
            let ret = func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
            );
            FromGlibPtrContainer::from_glib_none(ret)
        }
    }

    fn parent_query_action(
        &self,
        action_group: &Self::Type,
        action_name: &str,
    ) -> Option<(
        bool,
        Option<VariantType>,
        Option<VariantType>,
        Option<Variant>,
        Option<Variant>,
    )> {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<ActionGroup>()
                as *const ffi::GActionGroupInterface;

            let func = (*parent_iface)
                .query_action
                .expect("no parent \"query_action\" implementation");

            let mut enabled = mem::MaybeUninit::uninit();
            let mut parameter_type = ptr::null();
            let mut state_type = ptr::null();
            let mut state_hint = ptr::null_mut();
            let mut state = ptr::null_mut();

            let ret: bool = from_glib(func(
                action_group
                    .unsafe_cast_ref::<ActionGroup>()
                    .to_glib_none()
                    .0,
                action_name.to_glib_none().0,
                enabled.as_mut_ptr(),
                &mut parameter_type,
                &mut state_type,
                &mut state_hint,
                &mut state,
            ));

            if !ret {
                None
            } else {
                Some((
                    from_glib(enabled.assume_init()),
                    from_glib_none(parameter_type),
                    from_glib_none(state_type),
                    from_glib_none(state_hint),
                    from_glib_none(state),
                ))
            }
        }
    }
}

unsafe impl<T: ActionGroupImpl> IsImplementable<T> for ActionGroup {
    fn interface_init(iface: &mut glib::Interface<Self>) {
        let iface = iface.as_mut();

        iface.action_added = Some(action_group_action_added::<T>);
        iface.action_enabled_changed = Some(action_group_action_enabled_changed::<T>);
        iface.action_removed = Some(action_group_action_removed::<T>);
        iface.action_state_changed = Some(action_group_action_state_changed::<T>);
        iface.activate_action = Some(action_group_activate_action::<T>);
        iface.change_action_state = Some(action_group_change_action_state::<T>);
        iface.get_action_enabled = Some(action_group_get_action_enabled::<T>);
        iface.get_action_parameter_type = Some(action_group_get_action_parameter_type::<T>);
        iface.get_action_state = Some(action_group_get_action_state::<T>);
        iface.get_action_state_hint = Some(action_group_get_action_state_hint::<T>);
        iface.get_action_state_type = Some(action_group_get_action_state_type::<T>);
        iface.has_action = Some(action_group_has_action::<T>);
        iface.list_actions = Some(action_group_list_actions::<T>);
        iface.query_action = Some(action_group_query_action::<T>);
    }

    fn instance_init(_instance: &mut glib::subclass::InitializingObject<T>) {}
}

unsafe extern "C" fn action_group_has_action<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
) -> glib::ffi::gboolean {
    let instance = &*(action_group as *mut T::Instance);
    let action_name = GString::from_glib_borrow(action_nameptr);
    let imp = instance.impl_();

    imp.has_action(
        from_glib_borrow::<_, ActionGroup>(action_group).unsafe_cast_ref(),
        &action_name,
    )
    .into_glib()
}

unsafe extern "C" fn action_group_get_action_enabled<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
) -> glib::ffi::gboolean {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);

    imp.action_is_enabled(
        from_glib_borrow::<_, ActionGroup>(action_group).unsafe_cast_ref(),
        &action_name,
    )
    .into_glib()
}

/// Struct to hold a pointer and free it on `Drop::drop`
struct PtrHolder<T, F: Fn(*mut T) + 'static>(*mut T, F);

impl<T, F: Fn(*mut T) + 'static> Drop for PtrHolder<T, F> {
    fn drop(&mut self) {
        (self.1)(self.0)
    }
}

static ACTION_GROUP_GET_ACTION_PARAMETER_QUARK: Lazy<Quark> =
    Lazy::new(|| Quark::from_string("gtk-rs-subclass-action-group-get-action-parameter"));

unsafe extern "C" fn action_group_get_action_parameter_type<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
) -> *const glib::ffi::GVariantType {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);
    let wrap = from_glib_borrow::<_, ActionGroup>(action_group);

    let ret = imp.action_parameter_type(wrap.unsafe_cast_ref(), &action_name);

    if let Some(param_type) = ret {
        let param_type = param_type.to_glib_full();
        wrap.set_qdata(
            *ACTION_GROUP_GET_ACTION_PARAMETER_QUARK,
            PtrHolder(param_type, |ptr| glib::ffi::g_free(ptr as *mut _)),
        );
        param_type
    } else {
        ptr::null()
    }
}

static ACTION_GROUP_GET_ACTION_STATE_TYPE_QUARK: Lazy<Quark> =
    Lazy::new(|| Quark::from_string("gtk-rs-subclass-action-group-get-action-state-type"));

unsafe extern "C" fn action_group_get_action_state_type<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
) -> *const glib::ffi::GVariantType {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);
    let wrap = from_glib_borrow::<_, ActionGroup>(action_group);

    let ret = imp.action_state_type(wrap.unsafe_cast_ref(), &action_name);

    if let Some(state_type) = ret {
        let state_type = state_type.to_glib_full();
        wrap.set_qdata(
            *ACTION_GROUP_GET_ACTION_STATE_TYPE_QUARK,
            PtrHolder(state_type, |ptr| glib::ffi::g_free(ptr as *mut _)),
        );
        state_type
    } else {
        ptr::null()
    }
}

static ACTION_GROUP_GET_ACTION_STATE_HINT_QUARK: Lazy<Quark> =
    Lazy::new(|| Quark::from_string("gtk-rs-subclass-action-group-get-action-state-hint"));

unsafe extern "C" fn action_group_get_action_state_hint<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
) -> *mut glib::ffi::GVariant {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);

    let wrap = from_glib_borrow::<_, ActionGroup>(action_group);

    let ret = imp.action_state_hint(wrap.unsafe_cast_ref(), &action_name);
    if let Some(state_hint) = ret {
        let state_hint_ptr = state_hint.to_glib_full();
        wrap.set_qdata(
            *ACTION_GROUP_GET_ACTION_STATE_HINT_QUARK,
            PtrHolder(state_hint_ptr, |ptr| glib::ffi::g_variant_unref(ptr)),
        );
        state_hint_ptr
    } else {
        ptr::null_mut()
    }
}
static ACTION_GROUP_GET_ACTION_STATE_QUARK: Lazy<Quark> =
    Lazy::new(|| Quark::from_string("gtk-rs-subclass-action-group-get-action-state"));

unsafe extern "C" fn action_group_get_action_state<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
) -> *mut glib::ffi::GVariant {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);
    let wrap = from_glib_borrow::<_, ActionGroup>(action_group);

    let ret = imp.action_state(wrap.unsafe_cast_ref(), &action_name);
    if let Some(state) = ret {
        let state_ptr = state.to_glib_full();
        wrap.set_qdata(
            *ACTION_GROUP_GET_ACTION_STATE_QUARK,
            PtrHolder(state_ptr, |ptr| glib::ffi::g_variant_unref(ptr)),
        );
        state_ptr
    } else {
        ptr::null_mut()
    }
}

unsafe extern "C" fn action_group_change_action_state<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
    stateptr: *mut glib::ffi::GVariant,
) {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);
    let state = Variant::from_glib_borrow(stateptr);

    imp.change_action_state(
        from_glib_borrow::<_, ActionGroup>(action_group).unsafe_cast_ref(),
        &action_name,
        &state,
    )
}

unsafe extern "C" fn action_group_activate_action<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
    parameterptr: *mut glib::ffi::GVariant,
) {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);
    let param: Borrowed<Option<Variant>> = from_glib_borrow(parameterptr);

    imp.activate_action(
        from_glib_borrow::<_, ActionGroup>(action_group).unsafe_cast_ref(),
        &action_name,
        param.as_ref().as_ref(),
    )
}

unsafe extern "C" fn action_group_action_added<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
) {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);

    imp.action_added(
        from_glib_borrow::<_, ActionGroup>(action_group).unsafe_cast_ref(),
        &action_name,
    )
}

unsafe extern "C" fn action_group_action_removed<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
) {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);

    imp.action_removed(
        from_glib_borrow::<_, ActionGroup>(action_group).unsafe_cast_ref(),
        &action_name,
    )
}

unsafe extern "C" fn action_group_action_enabled_changed<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
    enabled: glib::ffi::gboolean,
) {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);

    imp.action_enabled_changed(
        from_glib_borrow::<_, ActionGroup>(action_group).unsafe_cast_ref(),
        &action_name,
        from_glib(enabled),
    )
}

unsafe extern "C" fn action_group_action_state_changed<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
    stateptr: *mut glib::ffi::GVariant,
) {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);
    let state = Variant::from_glib_borrow(stateptr);

    imp.action_state_changed(
        from_glib_borrow::<_, ActionGroup>(action_group).unsafe_cast_ref(),
        &action_name,
        &state,
    )
}

static ACTION_GROUP_LIST_ACTIONS_QUARK: Lazy<Quark> =
    Lazy::new(|| Quark::from_string("gtk-rs-subclass-action-group-list-actions"));

unsafe extern "C" fn action_group_list_actions<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
) -> *mut *mut libc::c_char {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let wrap = from_glib_borrow::<_, ActionGroup>(action_group);

    let actions = imp.list_actions(wrap.unsafe_cast_ref());

    {
        let actionsptr = actions.to_glib_full();
        wrap.set_qdata(*ACTION_GROUP_LIST_ACTIONS_QUARK, actionsptr);
        actionsptr
    }
}

static ACTION_GROUP_QUERY_ACTION_PARAM_TYPE_QUARK: Lazy<Quark> =
    Lazy::new(|| Quark::from_string("gtk-rs-subclass-action-group-query-action-parameter-type"));

static ACTION_GROUP_QUERY_ACTION_STATE_TYPE_QUARK: Lazy<Quark> =
    Lazy::new(|| Quark::from_string("gtk-rs-subclass-action-group-query-action-state-type"));

static ACTION_GROUP_QUERY_ACTION_STATE_HINT_QUARK: Lazy<Quark> =
    Lazy::new(|| Quark::from_string("gtk-rs-subclass-action-group-query-action-state-hint"));

static ACTION_GROUP_QUERY_ACTION_STATE_QUARK: Lazy<Quark> =
    Lazy::new(|| Quark::from_string("gtk-rs-subclass-action-group-query-action-state"));

unsafe extern "C" fn action_group_query_action<T: ActionGroupImpl>(
    action_group: *mut ffi::GActionGroup,
    action_nameptr: *const libc::c_char,
    enabled: *mut glib::ffi::gboolean,
    parameter_type: *mut *const glib::ffi::GVariantType,
    state_type: *mut *const glib::ffi::GVariantType,
    state_hint: *mut *mut glib::ffi::GVariant,
    state: *mut *mut glib::ffi::GVariant,
) -> glib::ffi::gboolean {
    let instance = &*(action_group as *mut T::Instance);
    let imp = instance.impl_();
    let action_name = GString::from_glib_borrow(action_nameptr);
    let wrap = from_glib_borrow::<_, ActionGroup>(action_group);

    let ret = imp.query_action(wrap.unsafe_cast_ref(), &action_name);
    if let Some((rs_enabled, rs_parameter_type, rs_state_type, rs_state_hint, rs_state)) = ret {
        if !enabled.is_null() {
            *enabled = rs_enabled.into_glib();
        }
        if !parameter_type.is_null() {
            if let Some(rs_parameter_type) = rs_parameter_type {
                let ret = rs_parameter_type.to_glib_full();
                wrap.set_qdata(
                    *ACTION_GROUP_QUERY_ACTION_PARAM_TYPE_QUARK,
                    PtrHolder(ret, |ptr| glib::ffi::g_free(ptr as *mut _)),
                );
                *parameter_type = ret;
            } else {
                *parameter_type = ptr::null_mut();
            }
        }
        if !state_type.is_null() {
            if let Some(rs_state_type) = rs_state_type {
                let ret = rs_state_type.to_glib_full();
                wrap.set_qdata(
                    *ACTION_GROUP_QUERY_ACTION_STATE_TYPE_QUARK,
                    PtrHolder(ret, |ptr| glib::ffi::g_free(ptr as *mut _)),
                );
                *state_type = ret;
            } else {
                *state_type = ptr::null_mut();
            }
        }
        if !state_hint.is_null() {
            if let Some(rs_state_hint) = rs_state_hint {
                let ret = rs_state_hint.to_glib_full();
                wrap.set_qdata(
                    *ACTION_GROUP_QUERY_ACTION_STATE_HINT_QUARK,
                    PtrHolder(ret, |ptr| glib::ffi::g_variant_unref(ptr)),
                );
                *state_hint = ret;
            } else {
                *state_hint = ptr::null_mut();
            }
        }
        if !state.is_null() {
            if let Some(rs_state) = rs_state {
                let ret = rs_state.to_glib_full();
                wrap.set_qdata(
                    *ACTION_GROUP_QUERY_ACTION_STATE_QUARK,
                    PtrHolder(ret, |ptr| glib::ffi::g_variant_unref(ptr)),
                );
                *state = ret;
            } else {
                *state = ptr::null_mut();
            }
        }
        true
    } else {
        false
    }
    .into_glib()
}
