use std::{cell::RefCell, collections::HashSet, rc::{Rc, Weak}};

use crate::configotron::fsm::{FSMState, FSMStateRef, FSM};

pub mod numbers;

pub fn elimate_unused_states(fsm: &mut FSM) {
    let start = fsm.start.as_ref().unwrap();
    let mut used: HashSet<String> = HashSet::new();

    pub fn explore_states(s: Weak<RefCell<FSMState>>, used: &mut HashSet<String>) -> Option<()> {

        let s_rc = s.upgrade()?;
        let s_borrow = s_rc.borrow();
        let t = s_borrow.transition.as_ref()?;
        let name = s_borrow.name.clone();

        if !used.contains(&name) {
            used.insert(name);

            for i in 0..(128 as u8) {
                explore_states(t.get_next(i), used);
            }
        }
        Some(())        
    }

    explore_states(Rc::downgrade(start), &mut used);

    let mut new_state_array: Vec<FSMStateRef> = Vec::new();

    fsm.states.drain(..).for_each(|s| {
        let s_name = s.borrow().name.clone();
        if used.contains(&s_name) {
            new_state_array.push(s);
        }
    });

    fsm.states = new_state_array;
}


#[cfg(test)]
mod test {
    use std::rc::Rc;

    use graphviz_rust::cmd::Format;

    use crate::configotron::fsm::{builder::elimate_unused_states, masking::{MaskPresets, TransitionMask}, new_state_ref, test::debug_file_dump, transition::FSMTranistion, FSMState, FSMStateAction, FSM};

    #[test]
    fn state_elimitnation() {
        let z0 = new_state_ref(FSMState::new(
            "z0".to_string(), FSMStateAction::Terminal,
        ));
        let z1 = new_state_ref(FSMState::new(
            "z1".to_string(), FSMStateAction::Terminal,
        ));
        let z2 = new_state_ref(FSMState::new(
            "z2".to_string(), FSMStateAction::Terminal,
        ));
        let z3 = new_state_ref(FSMState::new(
            "z3".to_string(), FSMStateAction::Terminal,
        ));
        let z4 = new_state_ref(FSMState::new(
            "z4".to_string(), FSMStateAction::Terminal,
        ));
        let z5 = new_state_ref(FSMState::new(
            "z5".to_string(), FSMStateAction::Terminal,
        ));

        let start = z0.clone();

        /*
         * Links:
         * (z0) -> (z1)
         * (z1) -> (z2), (z3)
         * (z2) -> (z2)
         * (z3) -> (z1) 
         * X (z4) -> (z3)
         * X (z5) -> (z5) 
         */

        z0.borrow_mut().set_transitions({
            let t = FSMTranistion::new(Rc::downgrade(&z1));
            t
        });
        z1.borrow_mut().set_transitions({
            let mut t = FSMTranistion::new(Rc::downgrade(&z2));
            t.apply(TransitionMask::from(MaskPresets::LowerCase), Rc::downgrade(&z3));
            t
        });
        z2.borrow_mut().set_transitions({
            let t = FSMTranistion::new(Rc::downgrade(&z2));
            t
        });
        z3.borrow_mut().set_transitions({
            let t = FSMTranistion::new(Rc::downgrade(&z1));
            t
        });
        z4.borrow_mut().set_transitions({
            let t = FSMTranistion::new(Rc::downgrade(&z3));
            t
        });
        z5.borrow_mut().set_transitions({
            let t = FSMTranistion::new(Rc::downgrade(&z5));
            t
        });


        let state_prev = vec![z0.clone(), z1.clone(), z2.clone(), z3.clone(), z4.clone(), z5.clone()];
        let state_after = vec![z0.clone(), z1.clone(), z2.clone(), z3.clone()];

        let mut fsm = FSM {
            states: state_prev,
            name: "test_fsm".to_string(),
            start: Some(start)
        };

        debug_file_dump(fsm.render_graph(Format::Svg).unwrap(), "/tmp/state_elimitiation_before.svg".to_string());

        elimate_unused_states(&mut fsm);

        debug_file_dump(fsm.render_graph(Format::Svg).unwrap(), "/tmp/state_elimitiation_after.svg".to_string());


        assert_eq!(fsm.states.len(), state_after.len());

        for (i, j) in fsm.states.iter().zip(state_after) {
            assert_eq!(i.borrow().name, j.borrow().name)
        }


    }
}