use preserves::value::Set;

use crate::gen::schema::ModulePath;
use crate::gen::schema::Ref;

pub struct WalkState<T> {
    pub context: T,
    pub module_path: ModulePath,
    seen: Set<Ref>,
}

impl<T> WalkState<T> {
    pub fn new(context: T, module_path: ModulePath) -> Self {
        WalkState {
            context,
            module_path,
            seen: Set::new(),
        }
    }

    pub fn cycle_check<
        E,
        F: Fn(&T, &Ref) -> Option<E>,
        R,
        Ks: FnOnce(&mut Self, Option<E>) -> R,
        Kf: FnOnce() -> R
    >(
        &mut self,
        r: &Ref,
        step: F,
        ks: Ks,
        kf: Kf,
    ) -> R {
        let r = r.qualify(&self.module_path);
        if self.seen.contains(&r) {
            kf()
        } else {
            self.seen.insert(r.clone());
            let maybe_e = step(&self.context, &r);
            let saved = std::mem::replace(&mut self.module_path, r.module);
            let result = ks(self, maybe_e);
            self.module_path = saved;
            result
        }
    }
}
