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

use crate::{
    icons::{Category, Icon},
    utils::make_ascii_titlecase,
    widgets::{icons::IconWidget, ExportDialog},
};

mod imp {
    use std::cell::OnceCell;

    use super::*;

    #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
    #[template(resource = "/org/gnome/design/SymbolicPreview/icons_group.ui")]
    #[properties(wrapper_type = super::IconsGroupWidget)]
    pub struct IconsGroupWidget {
        #[template_child]
        pub(super) flowbox: TemplateChild<gtk::FlowBox>,
        #[template_child]
        pub(super) name_label: TemplateChild<gtk::Label>,
        #[property(get, set, construct_only)]
        pub(super) category: OnceCell<Category>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for IconsGroupWidget {
        const NAME: &'static str = "IconsGroupWidget";
        type Type = super::IconsGroupWidget;
        type ParentType = gtk::ListBoxRow;

        fn class_init(klass: &mut Self::Class) {
            klass.bind_template();
            klass.bind_template_instance_callbacks();
            klass.set_css_name("icons-group");
        }

        fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
            obj.init_template();
        }
    }

    #[glib::derived_properties]
    impl ObjectImpl for IconsGroupWidget {
        fn constructed(&self) {
            self.parent_constructed();
            let obj = self.obj();
            let category = obj.category();
            let mut category_title = category.name();
            if category_title.len() <= 3 {
                category_title = category_title.to_uppercase();
            } else {
                make_ascii_titlecase(&mut category_title);
            };
            self.name_label.set_label(&category_title);

            let sorter = gtk::StringSorter::new(Some(Icon::this_expression("name")));
            let sort_model = gtk::SortListModel::new(Some(category.icons()), Some(sorter));
            self.flowbox.bind_model(Some(&sort_model), move |item| {
                let icon = item.clone().downcast::<Icon>().unwrap();
                IconWidget::new(icon).upcast()
            });
        }
    }
    impl WidgetImpl for IconsGroupWidget {}
    impl ListBoxRowImpl for IconsGroupWidget {}
}

glib::wrapper! {
    pub struct IconsGroupWidget(ObjectSubclass<imp::IconsGroupWidget>)
        @extends gtk::Widget, gtk::ListBoxRow;
}

#[gtk::template_callbacks]
impl IconsGroupWidget {
    pub fn new(category: &Category) -> Self {
        glib::Object::builder()
            .property("category", category)
            .build()
    }

    #[template_callback]
    fn on_child_activated(&self, child: &gtk::FlowBoxChild) {
        let icon = child.downcast_ref::<IconWidget>().unwrap().icon();
        let export_dialog = ExportDialog::new(icon);
        let window = self.root().and_downcast::<gtk::Window>().unwrap();

        export_dialog.set_transient_for(Some(&window));
        export_dialog.present();
    }
}
