use std::fmt::Debug;

use gpui::{
    point, px, App, Bounds, FontWeight, Hsla, Pixels, Point, SharedString, TextAlign, TextRun,
    Window,
};

use super::origin_point;

pub const TEXT_SIZE: f32 = 10.;
pub const TEXT_GAP: f32 = 2.;
pub const TEXT_HEIGHT: f32 = TEXT_SIZE + TEXT_GAP;

pub struct Text {
    pub text: SharedString,
    pub origin: Point<Pixels>,
    pub color: Hsla,
    pub font_size: Pixels,
    pub font_weight: FontWeight,
    pub align: TextAlign,
}

impl Text {
    pub fn new<T>(text: impl Into<SharedString>, origin: Point<T>, color: Hsla) -> Self
    where
        T: Default + Clone + Copy + Debug + PartialEq + Into<Pixels>,
    {
        let origin = point(origin.x.into(), origin.y.into());

        Self {
            text: text.into(),
            origin,
            color,
            font_size: TEXT_SIZE.into(),
            font_weight: FontWeight::NORMAL,
            align: TextAlign::Left,
        }
    }

    /// Set the font size of the Text.
    pub fn font_size(mut self, font_size: impl Into<Pixels>) -> Self {
        self.font_size = font_size.into();
        self
    }

    /// Set the font weight of the Text.
    pub fn font_weight(mut self, font_weight: FontWeight) -> Self {
        self.font_weight = font_weight;
        self
    }

    /// Set the alignment of the Text.
    pub fn align(mut self, align: TextAlign) -> Self {
        self.align = align;
        self
    }
}

impl<I> From<I> for PlotLabel
where
    I: Iterator<Item = Text>,
{
    fn from(items: I) -> Self {
        Self::new(items.collect())
    }
}

#[derive(Default)]
pub struct PlotLabel(Vec<Text>);

impl PlotLabel {
    pub fn new(items: Vec<Text>) -> Self {
        Self(items)
    }

    /// Paint the Label.
    pub fn paint(&self, bounds: &Bounds<Pixels>, window: &mut Window, cx: &mut App) {
        for Text {
            text,
            origin,
            color,
            font_size,
            font_weight,
            align,
        } in self.0.iter()
        {
            let origin = origin_point(origin.x, origin.y, bounds.origin);

            let text_run = TextRun {
                len: text.len(),
                font: window.text_style().highlight(*font_weight).font(),
                color: *color,
                background_color: None,
                underline: None,
                strikethrough: None,
            };

            if let Ok(text) =
                window
                    .text_system()
                    .shape_text(text.clone(), *font_size, &[text_run], None, None)
            {
                for line in text {
                    let origin = match align {
                        TextAlign::Left => origin,
                        TextAlign::Right => origin - point(line.size(*font_size).width, px(0.)),
                        _ => origin - point(line.size(*font_size).width / 2., px(0.)),
                    };

                    let _ = line.paint(origin, *font_size, *align, None, window, cx);
                }
            }
        }
    }
}
