use glib::{Object, Properties, prelude::*, subclass};
use gtk4::{
    Accessible, Box, Buildable, CompositeTemplate, ConstraintTarget, IconSize, Image, Stack, Widget,
    subclass::prelude::*,
};
use html2gtk::Justification as JustificationEnum;
use libadwaita::{Bin, ViewStack, ViewSwitcher, prelude::*, subclass::prelude::*};
use std::cell::Cell;

mod imp {
    use super::*;

    #[derive(Debug, Default, CompositeTemplate, Properties)]
    #[properties(wrapper_type = super::Justification)]
    #[template(file = "data/resources/ui_templates/article_view/justification.blp")]
    pub struct Justification {
        #[template_child]
        pub switcher: TemplateChild<ViewSwitcher>,
        #[template_child]
        pub stack: TemplateChild<ViewStack>,

        #[property(get, set, builder(JustificationEnum::Left))]
        pub justification: Cell<JustificationEnum>,
    }

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

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

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

    #[glib::derived_properties]
    impl ObjectImpl for Justification {
        fn constructed(&self) {
            let mut child = self.switcher.first_child();

            while let Some(widget) = child {
                Self::increase_icon_size(&widget);
                child = widget.next_sibling();
            }

            self.obj()
                .bind_property("justification", &*self.stack, "visible_child_name")
                .transform_to(|_binding, justification: JustificationEnum| {
                    Some(match justification {
                        JustificationEnum::Fill => "fill",
                        JustificationEnum::Left => "left",
                    })
                })
                .transform_from(|_binding, justification: String| {
                    Some(match justification.as_str() {
                        "fill" => JustificationEnum::Fill,
                        _ => JustificationEnum::Left,
                    })
                })
                .sync_create()
                .bidirectional()
                .build();

            self.parent_constructed();
        }
    }

    impl WidgetImpl for Justification {}

    impl BinImpl for Justification {}

    impl Justification {
        fn increase_icon_size(widget: &Widget) {
            if let Some(stack) = widget.first_child().and_downcast_ref::<Stack>()
                && let Some(vertical_box) = stack
                    .first_child()
                    .and_then(|child| child.next_sibling())
                    .and_downcast_ref::<Box>()
                && let Some(image) = vertical_box
                    .first_child()
                    .and_then(|indicator_bin| indicator_bin.first_child())
                    .and_then(|gizmo| gizmo.next_sibling())
                    .and_then(|gizmo| gizmo.next_sibling())
                    .and_downcast_ref::<Image>()
            {
                image.set_icon_size(IconSize::Large);
            }
        }
    }
}

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

impl Default for Justification {
    fn default() -> Self {
        Object::new()
    }
}
