use glib::subclass::prelude::*;
use gtk::{gio, glib, prelude::*};

use super::Icon;

mod imp {
    use std::{
        cell::{Cell, OnceCell, RefCell},
        collections::VecDeque,
    };

    use super::*;

    #[derive(Default, glib::Properties)]
    #[properties(wrapper_type = super::Category)]
    pub struct Category {
        #[property(get, set, construct_only)]
        pub name: OnceCell<String>,
        #[property(get, set, construct_only)]
        pub icons: OnceCell<gio::ListStore>,
        #[property(get, set)]
        pub is_rendered: Cell<bool>,
        pub queue: RefCell<VecDeque<Icon>>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for Category {
        const NAME: &'static str = "Category";
        type Type = super::Category;
    }

    #[glib::derived_properties]
    impl ObjectImpl for Category {}
}

glib::wrapper! {
    pub struct Category(ObjectSubclass<imp::Category>);
}

unsafe impl Send for Category {}
unsafe impl Sync for Category {}

impl Category {
    pub fn new(name: &str) -> Self {
        glib::Object::builder()
            .property("name", name)
            .property("icons", gio::ListStore::new::<Icon>())
            .build()
    }

    pub fn splice(&self, icons: Vec<Icon>) {
        self.imp().queue.replace(icons.into());
    }

    pub async fn queue_rendering(&self) {
        {
            let icons = self.imp().queue.borrow().clone();
            let Some(icon) = icons.get(0) else {
                return;
            };
            let handle = icon.imp().handle.get().unwrap().clone();
            let renderer: rsvg::CairoRenderer = rsvg::CairoRenderer::new(&handle);
            for icon in icons.iter() {
                match icon.render(&renderer, None).await {
                    Ok(_) => {
                        self.append(icon);
                    }
                    Err(err) => {
                        tracing::error!("Failed to render icon {err}");
                    }
                }
            }
        };
        self.imp().queue.borrow_mut().clear();
        self.set_is_rendered(true);
    }

    pub fn append(&self, icon: &Icon) {
        self.icons().append(icon);
    }
}
