use crate::{
    WINGsP::{WMScreen, WMCreateScreen},
    font::{Font, FontName},
};

use std::{
    collections::{HashMap, hash_map::Entry},
    ffi::c_void,
    ptr::NonNull,
    rc::Rc,
};

impl WMScreen {
    pub fn new(display: &NonNull<x11::xlib::Display>) -> Option<NonNull<Self>> {
        NonNull::new(unsafe { WMCreateScreen(display.as_ptr(), x11::xlib::XDefaultScreen(display.as_ptr())) })
    }

    fn font_cache_mut(&mut self) -> &mut HashMap<FontName, Rc<Font>> {
        if self.fontCache.is_null() {
            self.fontCache = (Box::leak(Box::new(HashMap::<FontName, Rc<Font>>::new()))
                as *mut HashMap<_, _>)
                .cast::<c_void>();
        }
        unsafe { &mut *self.fontCache.cast::<HashMap<FontName, Rc<Font>>>() }
    }

    pub fn font_cache_get_or_else(
        &mut self,
        name: FontName,
        f: impl FnOnce(&FontName) -> Option<Font>,
    ) -> Option<Rc<Font>> {
        match self.font_cache_mut().entry(name) {
            Entry::Occupied(o) => Some(o.get().clone()),
            Entry::Vacant(v) => {
                if let Some(font) = f(v.key()) {
                    let font = Rc::new(font);
                    v.insert(font.clone());
                    Some(font)
                } else {
                    None
                }
            }
        }
    }

    /// Removes `font` from the font cache if it is not in use anywhere else
    /// (i.e., the only live references are `font` and an entry in the cache).
    pub fn font_cache_flush(&mut self, font: &Rc<Font>) {
        if Rc::strong_count(font) > 2 {
            return;
        }
        self.font_cache_mut().remove(&font.name());
    }
}

#[cfg(test)]
mod test {
    use crate::WINGsP::WMScreen;

    use std::mem::MaybeUninit;

    #[test]
    fn font_cache_init() {
        let mut screen: WMScreen = unsafe { MaybeUninit::zeroed().assume_init() };
        let cache = screen.font_cache_mut();
        assert!(cache.is_empty());
    }
}
