//! Create a simple behavior tree implementation

pub mod composite;

use std::sync::Arc;

#[derive(Debug)]
pub enum NodeResult<B> {
    /// The node is still running
    ///
    /// This contains the node to be ticked
    Running(BehaviorArc<B>),
    /// The node succeeded
    Success,
    /// The node failed
    Failure,
}

pub type BehaviorArc<B> = Arc<dyn BehaviorNode<B>>;

// This is our main "behavior tree" trait.
// all nodes implement this trait.

pub trait BehaviorNode<B>: std::fmt::Debug + Send + Sync {
    fn tick(self: Arc<Self>, context: &mut B) -> NodeResult<B>;

    fn arc(self) -> BehaviorArc<B>
    where
        Self: Sized + Send + Sync + 'static,
    {
        Arc::new(self)
    }
}

#[derive(Debug)]
/// Takes care of executing a behavior tree
pub struct BehaviorRunner<B> {
    tree: BehaviorArc<B>,
    current_tick: Option<BehaviorArc<B>>,
}

impl<B> BehaviorRunner<B> {
    pub fn new(tree: BehaviorArc<B>) -> Self {
        Self {
            tree,
            current_tick: None,
        }
    }

    pub fn from_node<N>(node: N) -> Self
    where
        N: BehaviorNode<B> + 'static,
    {
        Self {
            tree: Arc::new(node),
            current_tick: None,
        }
    }

    pub fn into_inner(self) -> BehaviorArc<B> {
        self.current_tick.unwrap_or(self.tree)
    }

    pub fn is_running(&self) -> bool {
        self.current_tick.is_some()
    }

    fn tick_node(&mut self, node: &Arc<dyn BehaviorNode<B>>, context: &mut B) -> Option<bool> {
        match node.clone().tick(context) {
            NodeResult::Running(nbp) => {
                self.current_tick = Some(nbp);
                None
            }
            NodeResult::Success => Some(true),
            NodeResult::Failure => Some(false),
        }
    }

    // returns None -> still running
    // return Some(p) -> p true success, p false failure
    pub fn proceed(&mut self, context: &mut B) -> Option<bool> {
        if let Some(bp) = self.current_tick.take() {
            self.tick_node(&bp, context)
        } else {
            let node = self.tree.clone();
            self.tick_node(&node, context)
        }
    }
}