use std::{
    any::Any,
    borrow::Borrow,
    cell::{Ref, RefCell},
    fmt,
    hash::Hash,
    rc::Rc,
};

use self::map::PrefixMap;
pub use self::map::PrefixNode;

mod key;
mod map;

pub use key::KeySeq;
use lysp::vm::{
    Value,
    value::{NativeObject, convert::AnyFunction},
};

#[derive(Debug)]
pub struct LyspKeyMap {
    map: RefCell<PrefixMap<KeySeq, AnyFunction>>,
}

impl LyspKeyMap {
    pub fn new_value() -> Value {
        let map: Rc<dyn NativeObject> = Rc::new(Self {
            map: RefCell::new(PrefixMap::new()),
        });
        Value::NativeValue(map.into())
    }

    pub fn set(&self, key: KeySeq, value: AnyFunction) {
        self.map.borrow_mut().insert(key, value);
    }

    pub fn get<N>(&self, key: &N) -> Option<Ref<'_, PrefixNode<KeySeq, AnyFunction>>>
    where
        KeySeq: Borrow<N>,
        N: Eq + Hash + ?Sized,
    {
        Ref::filter_map(self.map.borrow(), |map| map.get(key)).ok()
    }
}

impl fmt::Display for LyspKeyMap {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "#red/keymap<")?;
        let mut i = 0;
        for (seq, entry) in self.map.borrow().map.iter() {
            let PrefixNode::Leaf(leaf) = entry else {
                continue;
            };
            if i != 0 {
                write!(f, " ")?;
            }
            i += 1;
            write!(f, "({seq} . {leaf})")?;
        }
        write!(f, ">")
    }
}

impl NativeObject for LyspKeyMap {
    fn as_any(&self) -> &dyn Any {
        self
    }
}
