use ratatui::{
    buffer::Buffer,
    layout::Rect,
    style::{Color, Style},
    text::Line,
    widgets::Widget,
};

mod backend;

#[derive(Default)]
pub struct PrettyGauge<'a> {
    prefix: Option<Line<'a>>,
    suffix: Option<Line<'a>>,
    inner_label: Option<Line<'a>>,
    filled_color: Option<Color>,
    unfilled_color: Option<Color>,
    ratio: f64,
}

impl<'a> PrettyGauge<'a> {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn prefix<S: Into<Line<'a>>>(self, text: S) -> Self {
        Self {
            prefix: Some(text.into()),
            ..self
        }
    }

    pub fn suffix<S: Into<Line<'a>>>(self, text: S) -> Self {
        Self {
            suffix: Some(text.into()),
            ..self
        }
    }

    pub fn inner_label<S: Into<Line<'a>>>(self, text: S) -> Self {
        Self {
            inner_label: Some(text.into()),
            ..self
        }
    }

    pub fn filled_color(self, color: Color) -> Self {
        Self {
            filled_color: Some(color),
            ..self
        }
    }

    pub fn unfilled_color(self, color: Color) -> Self {
        Self {
            unfilled_color: Some(color),
            ..self
        }
    }

    pub fn ratio(self, ratio: f64) -> Self {
        let ratio = ratio.clamp(0.0, 1.0);
        Self { ratio, ..self }
    }
}

impl Widget for PrettyGauge<'_> {
    fn render(self, area: Rect, buf: &mut Buffer)
    where
        Self: Sized,
    {
        let default_prefix_label = Line::from("[");
        let default_suffix_label = Line::from("]");
        let default_inner_label = Line::from(format!("{:3.0}%", self.ratio * 100.0));
        let inner_label = self.inner_label.as_ref().unwrap_or(&default_inner_label);
        let prefix_label = self.prefix.as_ref().unwrap_or(&default_prefix_label);
        let suffix_label = self.suffix.as_ref().unwrap_or(&default_suffix_label);
        let filled_color = self.filled_color.unwrap_or(Color::White);
        let unfilled_color = self.unfilled_color.unwrap_or(Color::Black);

        if (area.width as usize) < prefix_label.width() + suffix_label.width() {
            return;
        }

        let gauge_x = area.x + prefix_label.width() as u16;
        let gauge_width = area.width - (prefix_label.width() + suffix_label.width()) as u16;
        buf.set_line(area.x, area.y, prefix_label, area.width);
        buf.set_line(
            area.x + area.width - suffix_label.width() as u16,
            area.y,
            suffix_label,
            suffix_label.width() as u16,
        );

        if (inner_label.width() as u16) <= gauge_width {
            let inner_label_x = gauge_x + (gauge_width / 2) - (inner_label.width() / 2) as u16;
            buf.set_line(
                inner_label_x,
                area.y,
                inner_label,
                inner_label.width() as u16,
            );
        }

        buf.set_style(area, Style::default());

        for i in 0..gauge_width {
            let x = gauge_x + i;
            let v = i as f64 / gauge_width as f64;
            let (fg, bg) = if v <= self.ratio {
                (unfilled_color, filled_color)
            } else {
                (filled_color, unfilled_color)
            };
            buf[(x, area.y)].set_bg(bg).set_fg(fg);
        }
    }
}
