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

use crate::ffi;
use glib::{
    object::ObjectType as _,
    prelude::*,
    signal::{SignalHandlerId, connect_raw},
    translate::*,
};
use std::{boxed::Box as Box_, pin::Pin};

glib::wrapper! {
    /// For simple authentication using only a password, using the [`authenticate()`][Self::authenticate()]
    /// method is recommended. Look at the simple examples for how to use it.
    ///
    /// There is also a way to get access to the pam conversation, to allow for a more complex
    /// authentication process, like using multiple factor authentication. Generally it can be used like
    /// this:
    ///
    /// 1. create the Pam object.
    /// 2. set username and service if so required. It has sane defaults, so in most cases you can skip
    /// this.
    /// 3. connect to the signals.
    ///    After an `auth-*` signal is emitted, it has to be responded with exactly one
    /// [`supply_secret()`][Self::supply_secret()] call. The secret is a string containing the user input. For
    /// [auth-info][`auth-info`][struct@crate::Pam#auth-info]:] and [auth-error][`auth-error`][struct@crate::Pam#auth-error]:]
    /// it should be `NULL`. Not connecting those signals, is equivalent to calling
    /// [`supply_secret()`][Self::supply_secret()] with `NULL` immediately after the signal is emitted.
    /// 4. start authentication process using [`start_authenticate()`][Self::start_authenticate()].
    /// 5. it is possible to reuse the same Pam object for multiple sequential authentication attempts.
    /// Just call [`start_authenticate()`][Self::start_authenticate()] again after the `success` or `fail` signal
    /// was emitted.
    ///
    /// ## Properties
    ///
    ///
    /// #### `service`
    ///  The pam service used for authentication.
    /// Changing the value of this property has no affect on an already started authentication
    /// process.
    ///
    /// Defaults to the astal-auth pam service.
    ///
    /// Readable | Writeable | Construct
    ///
    ///
    /// #### `username`
    ///  The username used for authentication.
    /// Changing the value of this property has no affect on an already started authentication
    /// process.
    ///
    /// Defaults to the user that owns this process.
    ///
    /// Readable | Writeable | Construct
    ///
    /// ## Signals
    ///
    ///
    /// #### `auth-error`
    ///  This signal is emitted when an authentication error has occured.
    ///
    /// This signal has to be matched with exaclty one supply_secret call.
    ///
    ///
    ///
    ///
    /// #### `auth-info`
    ///  This signal is emitted when the user should receive an information (e.g., tell the user to
    /// touch a security key, or the remaining time pam has been locked after multiple failed
    /// attempts)
    ///
    /// This signal has to be matched with exaclty one supply_secret call.
    ///
    ///
    ///
    ///
    /// #### `auth-prompt-hidden`
    ///  This signal is emitted when user input is required. The input should be hidden
    /// when entered (e.g., for passwords).
    ///
    /// This signal has to be matched with exaclty one supply_secret call.
    ///
    ///
    ///
    ///
    /// #### `auth-prompt-visible`
    ///  This signal is emitted when user input is required. The input should be visible
    /// when entered (e.g., for One-Time Passwords (OTP)).
    ///
    /// This signal has to be matched with exaclty one supply_secret call.
    ///
    ///
    ///
    ///
    /// #### `fail`
    ///  This signal is emitted when authentication failed.
    ///
    ///
    ///
    ///
    /// #### `success`
    ///  This signal is emitted after successful authentication
    ///
    ///
    #[doc(alias = "AstalAuthPam")]
    pub struct Pam(Object<ffi::AstalAuthPam, ffi::AstalAuthPamClass>);

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

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

    /// Fetches the service from AsalAuthPam object.
    ///
    /// # Returns
    ///
    /// the service of the AsalAuthPam object. This string is
    /// owned by the object and must not be modified or freed.
    #[doc(alias = "astal_auth_pam_get_service")]
    #[doc(alias = "get_service")]
    pub fn service(&self) -> glib::GString {
        unsafe { from_glib_none(ffi::astal_auth_pam_get_service(self.to_glib_none().0)) }
    }

    /// Fetches the username from AsalAuthPam object.
    ///
    /// # Returns
    ///
    /// the username of the AsalAuthPam object. This string is
    /// owned by the object and must not be modified or freed.
    #[doc(alias = "astal_auth_pam_get_username")]
    #[doc(alias = "get_username")]
    pub fn username(&self) -> glib::GString {
        unsafe { from_glib_none(ffi::astal_auth_pam_get_username(self.to_glib_none().0)) }
    }

    /// Sets the service to be used for authentication. This must be set to
    /// before calling start_authenticate.
    /// Changing it afterwards has no effect on the authentication process.
    ///
    /// Defaults to `astal-auth`.
    /// ## `service`
    /// the pam service used for authentication
    #[doc(alias = "astal_auth_pam_set_service")]
    #[doc(alias = "service")]
    pub fn set_service(&self, service: &str) {
        unsafe {
            ffi::astal_auth_pam_set_service(self.to_glib_none().0, service.to_glib_none().0);
        }
    }

    /// Sets the username to be used for authentication. This must be set to
    /// before calling start_authenticate.
    /// Changing it afterwards has no effect on the authentication process.
    ///
    /// Defaults to the owner of the process.
    /// ## `username`
    /// the new username
    #[doc(alias = "astal_auth_pam_set_username")]
    #[doc(alias = "username")]
    pub fn set_username(&self, username: &str) {
        unsafe {
            ffi::astal_auth_pam_set_username(self.to_glib_none().0, username.to_glib_none().0);
        }
    }

    /// starts a new authentication process using the PAM (Pluggable Authentication Modules) system.
    /// Note that this will cancel an already running authentication process
    /// associated with this AstalAuthPam object.
    #[doc(alias = "astal_auth_pam_start_authenticate")]
    pub fn start_authenticate(&self) -> bool {
        unsafe {
            from_glib(ffi::astal_auth_pam_start_authenticate(
                self.to_glib_none().0,
            ))
        }
    }

    /// provides pam with a secret. This method must be called exactly once after a
    /// auth-* signal is emitted.
    /// ## `secret`
    /// the secret to be provided to pam. Can be NULL.
    #[doc(alias = "astal_auth_pam_supply_secret")]
    pub fn supply_secret(&self, secret: Option<&str>) {
        unsafe {
            ffi::astal_auth_pam_supply_secret(self.to_glib_none().0, secret.to_glib_none().0);
        }
    }

    /// Requests authentication of the provided password using the PAM (Pluggable Authentication Modules)
    /// system.
    /// ## `password`
    /// the password to be authenticated
    /// ## `result_callback`
    /// a GAsyncReadyCallback
    ///   to call when the request is satisfied
    #[doc(alias = "astal_auth_pam_authenticate")]
    pub fn authenticate<P: FnOnce(Result<isize, glib::Error>) + 'static>(
        password: &str,
        result_callback: P,
    ) -> bool {
        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(result_callback));
        unsafe extern "C" fn authenticate_trampoline<
            P: FnOnce(Result<isize, 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::astal_auth_pam_authenticate_finish(res, &mut error);
            let result = if error.is_null() {
                Ok(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 = authenticate_trampoline::<P>;
        unsafe {
            from_glib(ffi::astal_auth_pam_authenticate(
                password.to_glib_none().0,
                Some(callback),
                Box_::into_raw(user_data) as *mut _,
            ))
        }
    }

    pub fn authenticate_future<P: FnOnce(Result<isize, glib::Error>) + 'static>(
        password: &str,
        result_callback: P,
    ) -> Pin<Box_<dyn std::future::Future<Output = Result<isize, glib::Error>> + 'static>> {
        skip_assert_initialized!();
        let password = String::from(password);
        Box_::pin(gio::GioFuture::new(&(), move |_obj, _cancellable, send| {
            let res = Self::authenticate(&password, result_callback);
            send.resolve(Ok(res as isize));
        }))
    }

    /// This signal is emitted when an authentication error has occured.
    ///
    /// This signal has to be matched with exaclty one supply_secret call.
    /// ## `msg`
    /// the error message
    #[doc(alias = "auth-error")]
    pub fn connect_auth_error<F: Fn(&Self, &str) + 'static>(&self, f: F) -> SignalHandlerId {
        unsafe extern "C" fn auth_error_trampoline<F: Fn(&Pam, &str) + 'static>(
            this: *mut ffi::AstalAuthPam,
            msg: *mut std::ffi::c_char,
            f: glib::ffi::gpointer,
        ) {
            let f: &F = &*(f as *const F);
            f(
                &from_glib_borrow(this),
                &glib::GString::from_glib_borrow(msg),
            )
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                c"auth-error".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    auth_error_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    /// This signal is emitted when the user should receive an information (e.g., tell the user to
    /// touch a security key, or the remaining time pam has been locked after multiple failed
    /// attempts)
    ///
    /// This signal has to be matched with exaclty one supply_secret call.
    /// ## `msg`
    /// the info mssage to be shown to the user
    #[doc(alias = "auth-info")]
    pub fn connect_auth_info<F: Fn(&Self, &str) + 'static>(&self, f: F) -> SignalHandlerId {
        unsafe extern "C" fn auth_info_trampoline<F: Fn(&Pam, &str) + 'static>(
            this: *mut ffi::AstalAuthPam,
            msg: *mut std::ffi::c_char,
            f: glib::ffi::gpointer,
        ) {
            let f: &F = &*(f as *const F);
            f(
                &from_glib_borrow(this),
                &glib::GString::from_glib_borrow(msg),
            )
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                c"auth-info".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    auth_info_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    /// This signal is emitted when user input is required. The input should be hidden
    /// when entered (e.g., for passwords).
    ///
    /// This signal has to be matched with exaclty one supply_secret call.
    /// ## `msg`
    /// the prompt to be shown to the user
    #[doc(alias = "auth-prompt-hidden")]
    pub fn connect_auth_prompt_hidden<F: Fn(&Self, &str) + 'static>(
        &self,
        f: F,
    ) -> SignalHandlerId {
        unsafe extern "C" fn auth_prompt_hidden_trampoline<F: Fn(&Pam, &str) + 'static>(
            this: *mut ffi::AstalAuthPam,
            msg: *mut std::ffi::c_char,
            f: glib::ffi::gpointer,
        ) {
            let f: &F = &*(f as *const F);
            f(
                &from_glib_borrow(this),
                &glib::GString::from_glib_borrow(msg),
            )
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                c"auth-prompt-hidden".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    auth_prompt_hidden_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    /// This signal is emitted when user input is required. The input should be visible
    /// when entered (e.g., for One-Time Passwords (OTP)).
    ///
    /// This signal has to be matched with exaclty one supply_secret call.
    /// ## `msg`
    /// the prompt to be shown to the user
    #[doc(alias = "auth-prompt-visible")]
    pub fn connect_auth_prompt_visible<F: Fn(&Self, &str) + 'static>(
        &self,
        f: F,
    ) -> SignalHandlerId {
        unsafe extern "C" fn auth_prompt_visible_trampoline<F: Fn(&Pam, &str) + 'static>(
            this: *mut ffi::AstalAuthPam,
            msg: *mut std::ffi::c_char,
            f: glib::ffi::gpointer,
        ) {
            let f: &F = &*(f as *const F);
            f(
                &from_glib_borrow(this),
                &glib::GString::from_glib_borrow(msg),
            )
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                c"auth-prompt-visible".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    auth_prompt_visible_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    /// This signal is emitted when authentication failed.
    /// ## `msg`
    /// the authentication failure message
    #[doc(alias = "fail")]
    pub fn connect_fail<F: Fn(&Self, &str) + 'static>(&self, f: F) -> SignalHandlerId {
        unsafe extern "C" fn fail_trampoline<F: Fn(&Pam, &str) + 'static>(
            this: *mut ffi::AstalAuthPam,
            msg: *mut std::ffi::c_char,
            f: glib::ffi::gpointer,
        ) {
            let f: &F = &*(f as *const F);
            f(
                &from_glib_borrow(this),
                &glib::GString::from_glib_borrow(msg),
            )
        }
        unsafe {
            let f: Box_<F> = Box_::new(f);
            connect_raw(
                self.as_ptr() as *mut _,
                c"fail".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    fail_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    /// This signal is emitted after successful authentication
    #[doc(alias = "success")]
    pub fn connect_success<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
        unsafe extern "C" fn success_trampoline<F: Fn(&Pam) + 'static>(
            this: *mut ffi::AstalAuthPam,
            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"success".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    success_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    #[doc(alias = "service")]
    pub fn connect_service_notify<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
        unsafe extern "C" fn notify_service_trampoline<F: Fn(&Pam) + 'static>(
            this: *mut ffi::AstalAuthPam,
            _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::service".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    notify_service_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }

    #[doc(alias = "username")]
    pub fn connect_username_notify<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
        unsafe extern "C" fn notify_username_trampoline<F: Fn(&Pam) + 'static>(
            this: *mut ffi::AstalAuthPam,
            _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::username".as_ptr() as *const _,
                Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
                    notify_username_trampoline::<F> as *const (),
                )),
                Box_::into_raw(f),
            )
        }
    }
}

// rustdoc-stripper-ignore-next
/// A [builder-pattern] type to construct [`Pam`] 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 PamBuilder {
    builder: glib::object::ObjectBuilder<'static, Pam>,
}

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

    /// The pam service used for authentication.
    /// Changing the value of this property has no affect on an already started authentication
    /// process.
    ///
    /// Defaults to the astal-auth pam service.
    pub fn service(self, service: impl Into<glib::GString>) -> Self {
        Self {
            builder: self.builder.property("service", service.into()),
        }
    }

    /// The username used for authentication.
    /// Changing the value of this property has no affect on an already started authentication
    /// process.
    ///
    /// Defaults to the user that owns this process.
    pub fn username(self, username: impl Into<glib::GString>) -> Self {
        Self {
            builder: self.builder.property("username", username.into()),
        }
    }

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