/* //! Defines the [Step] struct which may be used when initialising the system.
//!
//! # Warning
//!
//! Use of alloc is currently required, so early initialisation will need a
//! different method. We should possibly abstract some functionality so that
//! consumers don't need to worry or care about the difference.
use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};

use crate::{arch::x86_64::drivers::ascii::WRITER, prelude::*};

/// Represents a [Step] when initialising the system.
///
/// Handles printing various information to the framebuffer using steps.
///
/// # Example
///
/// ```rs
/// // Setup a Step for Foo.
/// let foo_step: Step<Foo, FooError> = Step::new("foo", Foo::init()).register_tag(|foo| { foo.bar().to_string() });
/// // This returns a Result<T, E>
/// let foo = foo_step.run()?;
/// ```
///
/// # TODOs
///
/// * If T = Option<T> and None is returned, we print `"[Not found]"`, otherwise
///   `"[Success]`".
pub struct Step<T: Clone + ToOwned> {
    /// The initialisation function for the Step, usually this is a closure.
    init_fn: fn() -> T,
    /// A list of tag generator functions to display under the Step on success.
    /// They are displayed like:
    /// ```
    ///     Initialising module... [Success]
    ///         => Some information here.
    /// ```
    ///
    /// # TODOs
    ///
    /// * Support tags without alloc being initialised.
    tags: Vec<Box<dyn Fn(T) -> String>>,
    /// The name of the module being initialised.
    task: &'static str,
}

// This impl block might be so horrible it needs its own file.
impl<T: ToOwned + Clone> Step<T> {
    pub fn new(
        task: &'static str,
        init_fn: fn() -> T,
        tags: Vec<Box<dyn Fn(T) -> String>>,
    ) -> Self {
        Self {
            init_fn,
            tags,
            task,
        }
    }

    /// Runs the initialisation function and logs where required.
    ///
    /// This function is a little messy but it just handles pretty printing the
    /// [Step] to the terminal for us.
    pub fn run(self) -> T {
        print_log!("    {}", self.task);
        let mut success_position = WRITER.lock().pos();
        success_position.set_x(success_position.x() + 1);

        let ret = (self.init_fn)();

        println!();

        for tag in &self.tags[..] {
            // Calls the closure on the return value.
            println_log!("      => {}", (tag)(ret.clone()));
        }

        // Print whether or not the step succeeded.
        print_oneshot!(
            success_position,
            Colour::Green,
            Colour::Black,
            "[Success]"
        );

        ret
    }

    /// Registers a 'tag' or note to display under the line which says
    /// `Initialising module... [Success]`.
    ///
    /// # Notes
    ///
    /// The tag is a closure taking in a reference to T and returning a
    /// presumably formatted String.
    #[allow(unused)]
    pub fn register_tag(&mut self, tag: Box<dyn Fn(T) -> String>) -> &Self {
        self.tags.push(tag);

        self
    }
}
 */
