use crate::gobject_models::{GTag, GTags};
use crate::util::GtkUtil;
use glib::{Properties, clone, subclass::*};
use gtk4::{
    Accessible, Align, Box, Buildable, CompositeTemplate, ConstraintTarget, DrawingArea, Label, Widget, prelude::*,
    subclass::prelude::*,
};
use std::cell::RefCell;

mod imp {
    use super::*;

    #[derive(Debug, Default, CompositeTemplate, Properties)]
    #[properties(wrapper_type = super::ArticleRowTags)]
    #[template(file = "data/resources/ui_templates/article_list/row_tags.blp")]
    pub struct ArticleRowTags {
        #[template_child]
        pub full_names: TemplateChild<Box>,
        #[template_child]
        pub color_circles: TemplateChild<DrawingArea>,

        #[property(get, set = Self::set_tags)]
        pub tags: RefCell<GTags>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for ArticleRowTags {
        const NAME: &'static str = "ArticleRowTags";
        type ParentType = gtk4::Box;
        type Type = super::ArticleRowTags;

        fn class_init(klass: &mut Self::Class) {
            klass.bind_template();
        }

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

    #[glib::derived_properties]
    impl ObjectImpl for ArticleRowTags {
        fn constructed(&self) {
            self.color_circles.set_draw_func(clone!(
                #[weak(rename_to = imp)]
                self,
                move |_drawing_area, ctx, width, height| {
                    const SIZE: f64 = 16_f64;
                    const MARGIN: f64 = 2_f64;
                    const HALF_SIZE: f64 = SIZE / 2_f64;
                    const MIN_OFFSET: f64 = HALF_SIZE;
                    const MAX_OFFSET: f64 = SIZE + MARGIN;

                    let circle_count = imp.tags.borrow().len() as f64;
                    let offset = width as f64 / circle_count;
                    let offset = offset.min(MAX_OFFSET);
                    let offset = offset.max(MIN_OFFSET);

                    for (i, tag) in imp.tags.borrow().as_ref().iter().enumerate() {
                        let Some(color) = tag.color() else { continue };
                        let center_x = width as f64 - (HALF_SIZE + 2.0 * MARGIN) - (offset * i as f64);
                        let center_y = height as f64 / 2.0;
                        GtkUtil::draw_color_cirlce(ctx, &color, Some((center_x, center_y)));
                    }
                }
            ));

            self.parent_constructed();
        }
    }

    impl WidgetImpl for ArticleRowTags {}

    impl BoxImpl for ArticleRowTags {}

    impl ArticleRowTags {
        fn set_tags(&self, tags: GTags) {
            self.tags.replace(tags);
            self.update_labels();
            self.color_circles.queue_draw();
        }

        fn update_labels(&self) {
            while let Some(label) = self.full_names.first_child() {
                self.full_names.remove(&label);
            }

            for tag in self.tags.borrow().as_ref().iter() {
                let label = Self::create_tag_label(tag);
                self.full_names.append(&label);
            }
        }

        fn create_tag_label(tag: &GTag) -> Label {
            let css_selector = tag
                .tag_id()
                .as_ref()
                .as_str()
                .chars()
                .filter(|c| c.is_alphanumeric() && !c.is_whitespace())
                .collect::<String>();
            let css_selector = format!("tag-style-{css_selector}");
            let label = Label::new(Some(&tag.label()));
            label.set_margin_start(2);
            label.set_margin_end(2);
            label.set_valign(Align::Center);
            label.add_css_class(&css_selector);
            label.add_css_class("subtitle");
            label
        }
    }
}

glib::wrapper! {
    pub struct ArticleRowTags(ObjectSubclass<imp::ArticleRowTags>)
        @extends Widget, Box,
        @implements Accessible, Buildable, ConstraintTarget;
}

impl Default for ArticleRowTags {
    fn default() -> Self {
        glib::Object::new::<Self>()
    }
}

impl ArticleRowTags {
    pub fn new() -> Self {
        Self::default()
    }
}
