/*
    Appellation: engines <module>
    Contrib: @FL03
*/
//! This module works to implement various engines compatible with the [Plant](crate::plant::Plant).
//!
//! For the moment, the module contains two engines:
//!
//! - [NeuralEngine]: A neural Turing machine
//! - [WolframUTM]: A Wolfram [2, 3] universal Turing machine
//!
//! While the Wolfram [2, 3] UTM is used for testing and validation purposes, the NeuralTM is
//! used in production applications. All engines are required to implement a set of traits that
//!
//!  The Wolfram UTM is primarily used for testing and validation purposes, while the NeuralTM
//! is used for more practical applications. That being said, the architecture of the NeuralTM
//! is consistent with that of the WolframUTM as we rely on a 2-simplex to define the so-called
//! _headspace_ of the engines.
#[doc(inline)]
pub use self::{neural::NeuralEngine, wolfram::WolframUTM};

pub mod neural;
pub mod wolfram;

pub(crate) mod prelude {
    pub use super::neural::prelude::*;
    pub use super::wolfram::*;
    pub use super::{ComputationalEngine, RawEngine};
}

use crate::traits::{Alphabet, RawStore};
use eryon::{Head, RawState, State};

pub trait RawEngine: Send + Sync + core::fmt::Debug {
    type Store: RawStore;

    private!();
}

pub trait Engine: RawEngine
where
    Self: Clone + Default,
{
    private!();
}
/// A compute-oriented engine abstracted for use within the [Plant](crate::plant::Plant).
pub trait ComputationalEngine<Q, S>: RawEngine
where
    Q: RawState,
    S: Alphabet,
    Self: Sized,
    Self::Store: RawStore<Elem = S::Elem>,
{
    fn new(alphabet: S, initial_state: State<Q>) -> Self;
    /// returns an immutable reference to the alphabet
    fn alphabet(&self) -> &S;
    /// returns a mutable reference to the alphabet
    fn alphabet_mut(&mut self) -> &mut S;
    /// returns an instance of the head with immutable references to the state and symbol
    fn head(&self) -> Head<&Q, &S::Elem>;
    /// returns an instance of the head with mutable references to the state and symbol
    fn head_mut(&mut self) -> Head<&mut Q, &mut S::Elem>;
    /// returns a copy of the machine's current position
    fn position(&self) -> usize;
    /// returns a mutable reference to the machine's current position
    fn position_mut(&mut self) -> &mut usize;
    /// returns an instance of the state with an immutable inner value
    fn state(&self) -> State<&Q>;
    /// returns an instance of the state with a mutable inner value
    fn state_mut(&mut self) -> State<&mut Q>;
    /// returns an immutable reference to the tape
    fn tape(&self) -> &Self::Store;
    /// returns a mutable reference to the tape
    fn tape_mut(&mut self) -> &mut Self::Store;
    /// preform a single step of the turing machine
    fn step(&mut self) -> Result<(), crate::ActorError>;
    /// sets the alphabet of the turing machine
    fn set_alphabet(&mut self, alphabet: S);
    /// sets the position of the head
    fn set_position(&mut self, position: usize);
    /// sets the state of the head
    fn set_state(&mut self, state: State<Q>);
    /// sets the tape of the turing machine
    fn set_tape<I>(&mut self, tape: I)
    where
        I: IntoIterator<Item = S::Elem>;
    /// restores the engine from a snapshot
    fn restore_from_snapshot(&mut self, snapshot: crate::Snapshot<Q, S, Self::Store>)
    where
        Self::Store: IntoIterator<Item = S::Elem>,
    {
        snapshot.restore_engine(self);
    }
    /// returns a snapshot of the engine
    fn snapshot(&self) -> crate::SnapshotRef<'_, Q, S, Self::Store> {
        crate::SnapshotRef::from(self)
    }
}
