use super::instructions::{Head, Instruction};
use super::{Alphabet, State, Symbolic};
use contained_core::{states::Stateful, Include, Insert};
use serde::{Deserialize, Serialize};
use std::{
    mem::replace,
    ops::{Index, IndexMut},
};
pub trait Contract<S: Symbolic>:
    Clone
    + IndexMut<usize, Output = Instruction<S>>
    + Include<Instruction<S>>
    + Insert<usize, Instruction<S>>
{
    fn alphabet(&self) -> Box<dyn Alphabet<S>>;
    fn final_state(&self) -> State;
    fn get(&self, head: Head<S>) -> Option<&Instruction<S>> {
        if head.state() > self.final_state() {
            return None;
        }
        self.instructions()
            .iter()
            .find(|inst: &&Instruction<S>| inst.head() == head)
    }
    fn insert(&mut self, inst: Instruction<S>) -> Option<Instruction<S>> {
        if inst.head().state() == State::Invalid {
            return None;
        }
        if self.final_state() < inst.head().state() || self.final_state() < inst.tail().state() {
            return None;
        }
        if !self.alphabet().is_viable(&inst.head().symbol())
            || !self.alphabet().is_viable(&inst.tail().symbol())
        {
            return None;
        }
        match self
            .instructions()
            .iter()
            .position(|cand: &Instruction<S>| cand.head() == inst.head())
        {
            Some(index) => Some(replace(&mut self.instructions_mut()[index], inst)),
            None => {
                self.instructions_mut().push(inst.clone());
                Some(inst)
            }
        }
    }
    fn instructions(&self) -> &Vec<Instruction<S>>;
    fn instructions_mut(&mut self) -> &mut Vec<Instruction<S>>;
}
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Program<S: Symbolic> {
    pub alphabet: Vec<S>,
    instructions: Vec<Instruction<S>>,
    final_state: State,
}
impl<S: Symbolic> Program<S> {
    pub fn new(alphabet: impl IntoIterator<Item = S>, final_state: State) -> Self {
        let alphabet = Vec::from_iter(alphabet);
        let s: i64 = final_state.into();
        let capacity = alphabet.len() * s as usize;
        let instructions = Vec::with_capacity(capacity);
        Self {
            alphabet,
            instructions,
            final_state,
        }
    }
    pub fn alphabet(&self) -> &Vec<S> {
        &self.alphabet
    }
    pub fn instructions(&self) -> &Vec<Instruction<S>> {
        &self.instructions
    }
    pub fn final_state(&self) -> &State {
        &self.final_state
    }
    pub fn get(&self, head: Head<S>) -> Option<&Instruction<S>> {
        if head.state() > *self.final_state() {
            return None;
        }
        self.instructions()
            .iter()
            .find(|inst: &&Instruction<S>| inst.head() == head)
    }
    pub fn insert(&mut self, inst: Instruction<S>) -> Option<Instruction<S>> {
        if inst.head().state() == State::Invalid {
            return None;
        }
        if *self.final_state() < inst.head().state() || *self.final_state() < inst.tail().state() {
            return None;
        }
        if !self.alphabet().is_viable(&inst.head().symbol())
            || !self.alphabet().is_viable(&inst.tail().symbol())
        {
            return None;
        }
        match self
            .instructions()
            .iter()
            .position(|cand: &Instruction<S>| cand.head() == inst.head())
        {
            Some(index) => Some(replace(&mut self.instructions[index], inst)),
            None => {
                self.instructions.push(inst.clone());
                Some(inst)
            }
        }
    }
    fn check_instruction(&self, inst: &Instruction<S>) -> bool {
        if inst.head().state() == State::Invalid {
            return false;
        }
        if *self.final_state() < inst.head().state() || *self.final_state() < inst.tail().state() {
            return false;
        }
        if !self.alphabet().is_viable(&inst.head().symbol())
            || !self.alphabet().is_viable(&inst.tail().symbol())
        {
            return false;
        }
        true
    }
}
impl<S: Symbolic> Alphabet<S> for Program<S> {
    fn is_viable(&self, symbol: &S) -> bool {
        self.alphabet.is_viable(symbol)
    }
    fn default_symbol(&self) -> S {
        self.alphabet.default_symbol()
    }
}
impl<S: Symbolic> Extend<Instruction<S>> for Program<S> {
    fn extend<T: IntoIterator<Item = Instruction<S>>>(&mut self, iter: T) {
        for i in iter {
            self.insert(i);
        }
    }
}
impl<S: Symbolic> Include<Instruction<S>> for Program<S> {
    fn include(&mut self, inst: Instruction<S>) {
        if self.check_instruction(&inst) {
            match self
                .instructions()
                .iter()
                .position(|cand: &Instruction<S>| cand.head() == inst.head())
            {
                Some(index) => {
                    let _ = std::mem::replace(&mut self.instructions[index], inst);
                }
                None => {
                    self.instructions.push(inst.clone());
                }
            }
        }
    }
}
impl<S: Symbolic> Index<usize> for Program<S> {
    type Output = Instruction<S>;
    fn index(&self, index: usize) -> &Self::Output {
        &self.instructions[index]
    }
}
impl<S: Symbolic> IndexMut<usize> for Program<S> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.instructions[index]
    }
}
#[cfg(test)]
mod test {
    use super::*;
    use crate::instructions::Move;
    #[test]
    fn test_program() {
        let inst = Instruction::from((State::valid(), "a", State::valid(), "b", Move::Right));
        let mut program = Program::new(vec!["a", "b", "c"], State::invalid());
        assert!(program.insert(inst.clone()).is_some());
        assert!(program.get(inst.head().clone()).is_some())
    }
}