///
/// Retrieve a widget from a gtk::Builder
///
/// - Before:
///
///     Example:
///
///     ```no_run
///     let builder = gtk::Builder::new_frm_resource("/org/gnome/App/ui/widget.ui");
///     let widget: gtk::Label = builder.object("my_label").expect("Failed to find my_label object");
///     widget.set_text("Hello world!");
///
///     ```
///
/// - After:
///
///     Example 1:
///
///     ```no_run
///     let builder = gtk::Builder::new_from_resource("/org/gnome/App/ui/widget.ui");
///     get_widget!(builder, gtk::Label, my_label);
///     my_label.set_text("Hello world!");
///     ```
///     Example 2:
///
///     ```no_run
///     let builder = gtk::Builder::new_from_resource("/org/gnome/App/ui/widget.ui");
///     get_widget!(builder, gtk::Label, @my_label).set_text("Hello world!");
///     ```
#[macro_export]
macro_rules! get_widget {
    ($builder:expr, $wtype:ty, @$name:ident) => {{
        let $name: $wtype = $builder
            .object(stringify!($name))
            .expect(&format!("Could not find widget \"{}\" of type \"{}\"", stringify!($name), stringify!($wtype)));
        $name
    }};
    ($builder:expr, $wtype:ty, $name:ident) => {
        let $name: $wtype = $builder
            .object(stringify!($name))
            .expect(&format!("Could not find widget \"{}\" of type \"{}\"", stringify!($name), stringify!($wtype)));
    };
}

/// Spawn a future
///
/// - Before:
///
///     Example:
///
///     ```no_run
///     let ctx = glib::MainContext::default();
///     ctx.spawn_local(async {
///         something.await;
///     });
///     ```
///
/// - After:
///
///     Example:
///
///     ```no_run
///     spawn!(async {
///         something.await;
///     });
///     ```
#[macro_export]
macro_rules! spawn {
    ($future:expr) => {
        let ctx = glib::MainContext::default();
        ctx.spawn_local($future);
    };
}

/// Send an event through a glib::Sender
///
/// - Before:
///
///     Example:
///
///     ```no_run
///     sender.send(Action::DoThing).expect("Failed to send DoThing through the glib channel?");
///     ```
///
/// - After:
///
///     Example:
///
///     ```no_run
///     send!(self.sender, Action::DoThing);
///     ```
#[macro_export]
macro_rules! send {
    ($sender:expr, $action:expr) => {
        if let Err(err) = $sender.send($action) {
            error!("Failed to send \"{}\" action due to {}", stringify!($action), err);
        }
    };
}

/// Create a new action
///
/// - Before:
///
///     Example:
///
///     ```no_run
///     let widget = get_widget!(builder, gtk::Window, widget);
///     let actions = gio::SimpleActionGroup::new();
///     widget.insert_action_group("export", Some(&actions));
///
///     let action = gio::SimpleAction::new("do", None);
///     action.connect_activate(move |action, _| {
///         // Do something
///     });
///     actions.add_action(&action);
///     ```
///
/// - After:
///
///     Example:
///
///     ```no_run
///     let widget = get_widget!(builder, gtk::Window, widget);
///     let actions = gio::SimpleActionGroup::new();
///     widget.insert_action_group("export", Some(&actions));
///     action!(
///         actions,
///         "do",
///         move |action, _| {
///             // Do something
///         },
///     );
///     ```
#[macro_export]
macro_rules! action {
    ($actions_group:expr, $name:expr, $callback:expr) => {
        let simple_action = gio::SimpleAction::new($name, None);
        simple_action.connect_activate($callback);
        $actions_group.add_action(&simple_action);
    };
    ($actions_group:expr, $name:expr, $param_type:expr, $callback:expr) => {
        let simple_action = gio::SimpleAction::new($name, $param_type);
        simple_action.connect_activate($callback);
        $actions_group.add_action(&simple_action);
    };
}

/// Create a new stateful action
///
/// - Before:
///
///     Example:
///
///     ```no_run
///     let actions = gio::SimpleActionGroup::new();
///
///     let is_dark_mode = false;
///     let action = gio::SimpleAction::new_stateful("dark-mode", None, &is_dark_mode.to_variant());
///     action.connect_activate(move |action, _| {
///         let state = action.state().unwrap();
///         let action_state: bool = state.get().unwrap();
///         let is_dark_mode = !action_state;
///         action.set_state(&is_dark_mode.to_variant());
///     });
///     actions.add_action(&action);
///     ```
///
/// - After:
///
///     Example:
///     ```no_run
///     let actions = gio::SimpleActionGroup::new();
///     let is_dark_mode = false;
///     stateful_action!(actions, "dark-mode", is_dark_mode, move |action, _| {
///         let state = action.state().unwrap();
///         let action_state: bool = state.get().unwrap();
///         let is_dark_mode = !action_state;
///         action.set_state(&is_dark_mode.to_variant());
///
///         // Store the state using gsettings for example
///     });
///     ```
#[macro_export]
macro_rules! stateful_action {
    ($actions_group:expr, $name:expr, $state:expr, $callback:expr) => {
        let simple_action = gio::SimpleAction::new_stateful($name, None, &$state.to_variant());
        simple_action.connect_activate($callback);
        $actions_group.add_action(&simple_action);
    };
    ($actions_group:expr, $name:expr, $param_type:expr, $state:expr, $callback:expr) => {
        let simple_action = gio::SimpleAction::new_stateful($name, $param_type, &$state.to_variant());
        simple_action.connect_activate($callback);
        $actions_group.add_action(&simple_action);
    };
}

/// Retrieve an action from a gio::ActionGroup
///
/// - Before:
///
///     Example:
///
///     ```no_run
///     let actions = gio::SimpleActionGroup::new();
///     action!(
///         actions,
///         "delete",
///         move |action, _| {
///             // Do something
///         },
///     );
///     let action = actions.lookup_action("delete")
///                     .unwrap()
///                     .downcast::<gio::SimpleAction>()
///                     .unwrap();
///     action.set_enabled(false);
///     ```
///
/// - After:
///
///     Example:
///
///     ```no_run
///     let actions = gio::SimpleActionGroup::new();
///     action!(
///         actions,
///         "delete",
///         move |action, _| {
///             // Do something
///         },
///     );
///     get_action!(actions, @delete).set_enabled(false);
///     ```
#[macro_export]
macro_rules! get_action {
    ($actions:expr, @$name:ident) => {{
        let $name = $actions
            .lookup_action(stringify!($name))
            .expect(&format!("Could not find action \"{}\"", stringify!($name)))
            .downcast::<gio::SimpleAction>()
            .unwrap();
        $name
    }};

    ($actions:expr, $name:ident) => {
        let $name = $actions
            .lookup_action(stringify!($name))
            .expect(&format!("Could not find action \"{}\"", stringify!($name)))
            .downcast::<gio::SimpleAction>()
            .unwrap();
    };
}

/// Add a style class or list of style classes to a widget
///
/// - Before:
///
///     Example 1:
///
///     ```no_run
///     let button = gtk::Button::new();
///     let ctx = button.style_context();
///     ctx.add_class("foo");
///     ```
///
///     Example 2:
///
///     ```no_run
///     let button = gtk::Button::new();
///     let ctx = button.style_context();
///     ctx.add_class("foo");
///     ctx.add_class("bar");
///     ```
///
/// - After:
///
///     Example 1:
///
///     ```no_run
///     let button = gtk::Button::new();
///     add_style_class!(button, @foo);
///     ```
///
///     Example 2:
///
///     ```no_run
///     let button = gtk::Button::new();
///     add_style_class!(button, &["foo", "bar"]);
///     ```
#[macro_export]
macro_rules! add_style_class {
    ($widget:expr, @$name:ident) => {{
        let ctx = $widget.style_context();
        ctx.add_class(stringify!($name));
    }};
    ($widget:expr, $names:expr) => {{
        let ctx = $widget.style_context();
        for name in $names {
            ctx.add_class(name);
        }
    }};
}
