/*
    Appellation: actors <module>
    Contrib: @FL03
*/
//! This module focuses on implementing various operators for the [VNode](crate::vnode::VNode).
//! Each operator is a specialized actor that can be used to perform specific tasks.
//!
#[doc(inline)]
pub use self::{agent::Agent, observer::Observer};

pub mod agent;
pub mod observer;

pub(crate) mod prelude {
    pub use super::agent::*;
    pub use super::observer::*;
    pub use super::{Operator, OperatorKind};
}

use crate::drivers::RawDriver;
use crate::mem::TopoLedger;
use crate::traits::Actor;
use rstmt::nrt::Triad;

use num_traits::{Float, FromPrimitive, NumAssign};

/// Enumerates the varios operators for the nodes, reducing the number of generics needed.
#[derive(Clone, Debug, Eq, PartialEq, strum::EnumDiscriminants, strum::EnumIs)]
#[strum_discriminants(
    name(OperatorKind),
    derive(
        Hash,
        Ord,
        PartialOrd,
        strum::AsRefStr,
        strum::Display,
        strum::EnumCount,
        strum::EnumIs,
        strum::EnumIter,
        strum::EnumString,
        strum::VariantArray,
        strum::VariantNames
    ),
    strum(serialize_all = "snake_case")
)]
pub enum Operator<T> {
    Agent(Agent<T>),
    Observer(Observer<T>),
}

impl<T> Operator<T> {
    /// Get the current active operator as Actor trait object
    pub fn active_operator<D>(&self) -> &dyn Actor<D, T>
    where
        D: RawDriver<Triad>,
        T: core::iter::Sum + Float + FromPrimitive + NumAssign,
    {
        match self {
            Operator::Observer(observer) => observer,
            Operator::Agent(agent) => agent,
        }
    }

    /// Get the current active operator as Actor trait object (mutable)
    pub fn active_operator_mut<D>(&mut self) -> &mut dyn Actor<D, T>
    where
        D: RawDriver<Triad>,
        T: core::iter::Sum + Float + FromPrimitive + NumAssign,
    {
        match self {
            Operator::Observer(observer) => observer,
            Operator::Agent(agent) => agent,
        }
    }
}

impl<D, T> Actor<D, T> for Operator<T>
where
    D: RawDriver<Triad>,
    T: core::iter::Sum + Float + FromPrimitive + NumAssign,
{
    fn kind(&self) -> &'static str {
        self.active_operator::<D>().kind()
    }

    fn initialize(&mut self) -> crate::Result<()> {
        self.active_operator_mut::<D>().initialize()
    }

    fn process_transform(
        &mut self,
        transform: crate::nrt::LPR,
        driver: &mut D,
        memory: &mut TopoLedger<T>,
    ) -> crate::Result<bool> {
        self.active_operator_mut::<D>()
            .process_transform(transform, driver, memory)
    }

    fn process_message(
        &mut self,
        source: rshyper::EdgeId,
        message: &[u8],
        memory: &mut TopoLedger<T>,
    ) -> crate::Result<()> {
        self.active_operator_mut::<D>()
            .process_message(source, message, memory)
    }

    fn on_activate(&mut self, driver: &D, memory: &mut TopoLedger<T>) -> crate::Result<()> {
        self.active_operator_mut::<D>().on_activate(driver, memory)
    }

    fn on_deactivate(&mut self, driver: &D, memory: &mut TopoLedger<T>) -> crate::Result<()> {
        self.active_operator_mut::<D>()
            .on_deactivate(driver, memory)
    }

    fn resource_requirements(&self) -> (usize, usize) {
        self.active_operator::<D>().resource_requirements()
    }

    fn allows_pattern_sharing(&self) -> bool {
        self.active_operator::<D>().allows_pattern_sharing()
    }

    fn contextualize(
        &self,
        driver: &D,
        memory: &TopoLedger<T>,
    ) -> crate::Result<crate::ActorContext<T>> {
        self.active_operator::<D>().contextualize(driver, memory)
    }
}

impl<T> Default for Operator<T>
where
    T: Default + FromPrimitive,
{
    fn default() -> Self {
        Operator::Agent(Agent::default())
    }
}

impl<T> core::hash::Hash for Operator<T> {
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        match self {
            Operator::Agent(agent) => agent.hash(state),
            Operator::Observer(observer) => observer.hash(state),
        }
    }
}
