//! `Executable` is a property to execute a closure

use anyhow::Result;

use std::sync::{Arc, RwLock};

use crate::prelude::*;

/// `Executable` holds a closure which can be executed
pub struct Executable<I: Send + Sync + 'static> {
    callback: RwLock<Option<Arc<dyn Fn(I) -> Result<()> + Send + Sync>>>,
}

impl<I: Send + Sync + 'static> Executable<I> {
    /// Factory method for `Executable`, returns `Arc<Executable>`
    pub fn new() -> Arc<Self> {
        Arc::new(Executable {
            callback: RwLock::new(None),
        })
    }

    /// Set callback closure
    ///
    /// # Arguments
    ///
    /// * `callback` is a `Option<Callback>` closure
    pub fn set_callback<F>(&self, callback: impl Into<Option<F>>)
    where
        F: Fn(I) -> Result<()> + Send + Sync + 'static,
    {
        let mut function = self.callback.write().unwrap();

        match callback.into() {
            Some(f) => *function = Some(Arc::new(f)),
            None => *function = None,
        }
    }

    /// Execute the callback closure if possible
    pub fn execute(&self, gui_handler: &mut GuiHandler<'_>, input: I) -> Result<()> {
        if let Some(callback) = self.callback.read().unwrap().as_ref() {
            let callback = callback.clone();

            gui_handler.add_callback(move || (callback)(input));
        }

        Ok(())
    }
}