
use super::masking::TransitionMask;
use super::FSMState;
use std::collections::HashMap;
use std::rc::Rc;
use std::{cell::RefCell, rc::Weak};

type StateRef = Weak<RefCell<FSMState>>;

#[derive(Debug, Clone)]
pub struct FSMTranistion {
    array: [StateRef; 128],
}

impl FSMTranistion {
    pub fn new_from_mask(mask: TransitionMask, pos: StateRef, default: StateRef) -> Self {
        let mut array: [StateRef; 128] = std::array::from_fn(|_| Weak::clone(&default));
        for i in 0..128 {
            if mask.get_bit(i) {
                array[i] = Weak::clone(&pos);
            }
        }

        FSMTranistion { array }
    }

    pub fn get_next(&self, c: u8) -> StateRef {
        self.array[c as usize].clone()
    }

    pub fn new(default: StateRef) -> Self {
        let array: [StateRef; 128] = std::array::from_fn(|_| Weak::clone(&default));

        FSMTranistion { array }
    }

    pub fn apply(&mut self, mask: TransitionMask, state: StateRef) {
        for i in 0..128 {
            if mask.get_bit(i) {
                self.array[i] = Weak::clone(&state);
            }
        }
    }

    pub fn to_targets_vec(&self) -> Vec<(StateRef, TransitionMask)> {
        let mut target_maps: HashMap<String, (StateRef, TransitionMask)> = HashMap::new();

        self.array.iter().enumerate().for_each(|(index, target)| {
            match target.upgrade() {
                None => {}
                Some(t) => {
                    let s_borrowd = t.borrow();
                    let key = s_borrowd.to_graph_label();
                    match target_maps.get_mut(&key) {
                        Some((_, mask)) => {
                            mask.set_bit(index);
                        }
                        None => {
                            let mut mask = TransitionMask::new();
                            mask.set_bit(index);
                            target_maps.insert(key, (Rc::downgrade(&t), mask));
                        }
                    };
                }
            };
        });

        let mut res: Vec<(StateRef, TransitionMask)> = Vec::new();

        target_maps
            .iter()
            .for_each(|(_, (target, mask))| match target.upgrade() {
                Some(t) => {
                    res.push((Rc::downgrade(&t), mask.clone()));
                }
                None => {}
            });
        res
    }
}
