/*
    Appellation: plant <module>
    Contrib: @FL03
*/

mod impl_neural;
mod impl_plant;
mod impl_wolfplant;

use crate::engine::{self, ComputationalEngine, RawEngine};
use crate::traits::RawStore;
use eryon::State;
use rstmt::prelude::{Octave, Triad, Triads};

/// The term plant comes from control theory, denoting a fully-controlled dynamical system
/// that exists within some environment in-which both are part of a larger universe. This
/// definition aptly describes our efforts, as the plant is a fully-controlled entity modeled
/// topologically as a 2-simplex, representing a "slice" of the tonnetz.
///
/// The plant relies on a _headspace_ to define all possible combinations of state-symbols
/// pairs. The plant is platform agnostic and isolated, relying on its engine to define
/// execution patterns and its environment (i.e Virtual Nodes) to provide the necessary
/// resources to execute a given transaction.
///
/// The plant is a _stateful_ entity, meaning that it can be in one of many states at any
/// given time. The plant is also _symbolic_, meaning that it can represent a symbol from
/// its headspace at any given time.
///
/// Plants are abstracted via the [`Driver`](crate::drivers::Drivers) trait, allowing us to
/// define a wide range of plants with different execution patterns.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Plant<E = engine::NeuralEngine>
where
    E: RawEngine,
{
    // the engine defines how the plant should execute a given transaction
    pub(crate) engine: E,
    // the headspace of the instance defines all possible states and symbols
    pub(crate) headspace: Triad,
}

impl<E> Plant<E>
where
    E: RawEngine,
{
    pub fn from_headspace(headspace: Triad) -> Self
    where
        E: ComputationalEngine<usize, [usize; 3]>,
        E::Store: RawStore<Elem = usize>,
    {
        let engine = E::new(headspace.notes(), State(0));
        Plant { engine, headspace }
    }
    /// returns the alphabet of the plant's headspace
    pub const fn alphabet(&self) -> [usize; 3] {
        self.headspace().notes()
    }
    /// returns the class of the plant's headspace
    pub const fn class(&self) -> Triads {
        self.headspace().class()
    }
    /// returns an immutable reference to the engine of the plant; here, the engine is defined
    /// to be a Wolfram [2, 3] UTM
    pub const fn engine(&self) -> &E {
        &self.engine
    }
    /// returns a mutable reference to the plant's UTM
    #[inline]
    pub fn engine_mut(&mut self) -> &mut E {
        &mut self.engine
    }
    /// returns a copy to the plant's current modulus; the modulus is used to identify the
    /// _layer_ it is associated with
    pub const fn modulus(&self) -> Octave {
        self.headspace().octave()
    }
    /// returns a copy of the plant's current headspace; the headspace is a topological entity
    /// whose elements are considered state-symbol pairs ($Q\times\Sigma$).
    pub const fn headspace(&self) -> Triad {
        self.headspace
    }
    /// returns a mutable reference to the plant's headspace
    #[inline]
    pub fn headspace_mut(&mut self) -> &mut Triad {
        &mut self.headspace
    }
    /// set the engine of the plant
    pub fn set_engine(&mut self, engine: E) {
        self.engine = engine;
    }
    /// set the modulus of the plant
    pub fn set_modulus(&mut self, octave: Octave) {
        self.headspace_mut().set_octave(octave);
    }
    /// set the headspace of the plant
    pub fn set_headspace(&mut self, triad: Triad)
    where
        E: ComputationalEngine<usize, [usize; 3]>,
        E::Store: crate::RawStore<Elem = usize>,
    {
        self.headspace = triad;
        self.engine_mut().set_alphabet(triad.notes());
    }
    /// consumes this instance to create another with the given class
    pub fn with_class(self, class: Triads) -> Self {
        Plant {
            headspace: Triad::new(self.alphabet(), class),
            ..self
        }
    }
    /// consumes this instance to create another with the given utm
    pub fn with_engine<U>(self, engine: U) -> Plant<U>
    where
        U: RawEngine,
    {
        Plant {
            engine,
            headspace: self.headspace,
        }
    }
    /// consumes this instance to create another with the given triad
    pub fn with_headspace(self, triad: Triad) -> Self {
        Self {
            headspace: triad,
            ..self
        }
    }
    /// consumes this instance to create another with the given modulus
    pub fn with_modulus(self, modulus: Octave) -> Self {
        Self {
            headspace: self.headspace.with_octave(modulus),
            ..self
        }
    }
    /// checks if the given symbol is currently considered by the plant's headspace
    pub fn contains<Q>(&self, pitch: &Q) -> bool
    where
        Q: core::borrow::Borrow<usize>,
    {
        self.headspace.contains(pitch)
    }
    /// assert the validity of the plant's headspace
    pub fn is_valid(&self) -> bool {
        self.headspace.is_valid()
    }

    pub fn snapehot(&self) -> crate::SnapshotRef<'_, usize, [usize; 3], Vec<usize>>
    where
        E: crate::engine::ComputationalEngine<usize, [usize; 3], Store = Vec<usize>>,
    {
        self.engine().snapshot()
    }
}
