/*
    Appellation: snapshot <module>
    Contrib: @FL03
*/
use crate::engine::ComputationalEngine;
use crate::traits::{Alphabet, RawStore};
use eryon::{RawState, State};
use scsys::time::Timestamp;
/// a type alias for an owned [`SnapshotBase`]
pub type Snapshot<Q, S, M> = SnapshotBase<Q, S, M>;
/// a type alias for a borrowed [`SnapshotBase`]
pub type SnapshotRef<'a, Q, S, M> = SnapshotBase<&'a Q, &'a S, &'a M>;

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SnapshotBase<Q, S, M>
where
    Q: RawState,
{
    pub(crate) alphabet: S,
    pub(crate) position: usize,
    pub(crate) state: State<Q>,
    pub(crate) tape: M,
    pub(crate) timestamp: Timestamp,
}

impl<Q, S, M> SnapshotBase<Q, S, M>
where
    Q: RawState,
{
    pub fn restore_engine<E>(self, engine: &mut E)
    where
        E: ComputationalEngine<Q, S, Store = M>,
        M: RawStore<Elem = S::Elem> + IntoIterator<Item = S::Elem>,
        S: Alphabet,
    {
        engine.set_alphabet(self.alphabet);
        engine.set_position(self.position);
        engine.set_state(self.state);
        engine.set_tape(self.tape);
    }
    /// returns a reference to the captured alphabet
    pub const fn alphabet(&self) -> &S {
        &self.alphabet
    }
    /// returns a copy to the position of the head at this particular time
    pub const fn position(&self) -> usize {
        self.position
    }
    /// returns an immutable reference to the captured state
    pub const fn state(&self) -> &State<Q> {
        &self.state
    }
    /// returns a reference to the machine's tape
    pub const fn tape(&self) -> &M {
        &self.tape
    }
    /// returns a copy of the timestamp for the snapshot
    pub const fn timestamp(&self) -> Timestamp {
        self.timestamp
    }
}

impl<'a, Q, S, M> SnapshotRef<'a, Q, S, M>
where
    Q: RawState,
{
    pub fn from_engine<E>(engine: &'a E) -> Self
    where
        E: ComputationalEngine<Q, S, Store = M>,
        M: RawStore<Elem = S::Elem>,
        S: Alphabet,
    {
        Self {
            alphabet: engine.alphabet(),
            position: engine.position(),
            state: engine.state(),
            tape: engine.tape(),
            timestamp: Timestamp::now(),
        }
    }
    /// clones the snapshot's data into a new snapshot
    pub fn cloned(&self) -> Snapshot<Q, S, M>
    where
        M: Clone,
        Q: Clone,
        S: Clone,
    {
        SnapshotBase {
            alphabet: self.alphabet.clone(),
            position: self.position,
            state: self.state.cloned(),
            tape: self.tape.clone(),
            timestamp: self.timestamp,
        }
    }
    /// copies the snapshot's data into a new snapshot
    pub fn copied(&self) -> Snapshot<Q, S, M>
    where
        M: Copy,
        Q: Copy,
        S: Copy,
    {
        SnapshotBase {
            alphabet: *self.alphabet,
            position: self.position(),
            state: self.state.copied(),
            tape: *self.tape,
            timestamp: self.timestamp(),
        }
    }
}

impl<'a, Q, S, M, U> From<&'a U> for SnapshotBase<&'a Q, &'a S, &'a M>
where
    Q: RawState,
    S: Alphabet,
    M: RawStore<Elem = S::Elem>,
    U: ComputationalEngine<Q, S, Store = M>,
{
    fn from(engine: &'a U) -> Self {
        Self {
            alphabet: engine.alphabet(),
            position: engine.position(),
            state: engine.state(),
            tape: engine.tape(),
            timestamp: Timestamp::now(),
        }
    }
}

impl<Q, S, M> core::fmt::Display for SnapshotBase<Q, S, M>
where
    Q: RawState,
    S: core::fmt::Display,
    M: core::fmt::Display,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(
            f,
            "{{\n\talphabet: {},\n\tposition: {},\n\tstate: {},\n\ttape: {},\n\ttimestamp: {}\n}}",
            self.alphabet, self.position, self.state, self.tape, self.timestamp
        )
    }
}
