mod label_with_widgets;

use crate::elements::{Label, Margins};
use crate::widget_factory::image::ImageType;
use crate::widget_factory::WidgetBuilder;
use crate::{FontStyle, HtmlWidget, InlineWidgetType};
use gtk4::glib::Propagation;
use gtk4::{pango::WrapMode, prelude::*, Align, Justification, Widget};
pub use label_with_widgets::LabelWithWidgets;

impl WidgetBuilder {
    pub(super) fn create_label(&self, label: Label, allow_justification: bool) -> Widget {
        let css_classes = label
            .styles
            .iter()
            .map(AsRef::as_ref)
            .chain(std::iter::once("document"))
            .collect::<Vec<&str>>();

        let is_title = matches!(label.margins, Margins::Title);

        let justification = if !is_title && allow_justification {
            self.config.justification.into()
        } else {
            Justification::Left
        };

        let top_margin = match label.margins {
            Margins::None => 0,
            Margins::Default => 2,
            Margins::Title => 5,
        };
        let bottom_margin = match label.margins {
            Margins::None => 0,
            Margins::Default => (4.0 * self.config.paragraph_spacing) as i32,
            Margins::Title => (5.0 * self.config.paragraph_spacing) as i32,
        };

        let font_family = match self.config.font_style {
            FontStyle::SansSerif => String::new(),
            FontStyle::Serif => format!("face=\"{}\"", FontStyle::Serif.as_css_property()),
        };

        let text = format!(
            "<span line_height=\"{}\" size=\"{}pt\" {font_family}>{}</span>",
            self.config.line_height, self.config.font_size, label.text
        );

        if label.widgets.is_empty() {
            let widget = gtk4::Label::builder()
                .wrap(true)
                .wrap_mode(WrapMode::WordChar)
                .xalign(0.0)
                .valign(Align::Start)
                .use_markup(true)
                .justify(justification)
                .selectable(self.config.select_text)
                .css_classes(css_classes)
                .label(text)
                .margin_top(top_margin)
                .margin_bottom(bottom_margin)
                .build();

            widget.connect_activate_link(|widget, link| {
                let Some(html_widget) = HtmlWidget::find_parent(widget.upcast_ref()) else {
                    return Propagation::Proceed;
                };

                html_widget.emit_activate_link(link);
                Propagation::Stop
            });

            for id in label.ids {
                tracing::trace!(id, "instert label into id_map");
                self.id_map.borrow_mut().insert(id, widget.clone().upcast());
            }

            widget.upcast()
        } else {
            let widgets = label
                .widgets
                .into_iter()
                .map(|inline_widget| match inline_widget.widget_type {
                    InlineWidgetType::Image(image) => self.create_image(image, ImageType::Inline),
                    InlineWidgetType::Math(math) => self.render_math(math),
                })
                .collect();
            let label_with_widgets = LabelWithWidgets::new();
            label_with_widgets.set_use_markup(true);
            label_with_widgets.set_label_and_widgets(text, widgets);
            label_with_widgets.set_margin_top(top_margin);
            label_with_widgets.set_margin_bottom(bottom_margin);
            label_with_widgets.set_justify(justification);

            label_with_widgets
                .child()
                .set_selectable(self.config.select_text);

            label_with_widgets
                .child()
                .connect_activate_link(|widget, link| {
                    let Some(html_widget) = HtmlWidget::find_parent(widget.upcast_ref()) else {
                        return Propagation::Proceed;
                    };

                    html_widget.emit_activate_link(link);
                    Propagation::Stop
                });

            for id in label.ids {
                tracing::trace!(id, "instert label with widgets into id_map");
                self.id_map
                    .borrow_mut()
                    .insert(id, label_with_widgets.clone().upcast());
            }

            label_with_widgets.upcast()
        }
    }
}
