use std::fmt;

use crate::{Color, Constraint, Element, Filled, Line, Paint, Size, Style, cell::Cell};

/// A styled string that does not contain any `'\n'` and implements [`Element`] and [`Cell`].
#[derive(Clone, Default, Debug)]
pub struct Label(Paint<String>);

impl Label {
    /// Create a new label.
    pub fn new(s: &str) -> Self {
        Self(Paint::new(cleanup(s)))
    }

    /// Create a blank label.
    pub fn blank() -> Self {
        Self(Paint::default())
    }

    /// Is this label empty.
    pub fn is_blank(&self) -> bool {
        self.content().is_empty()
    }

    /// Get unstyled content.
    pub fn content(&self) -> &str {
        self.0.content()
    }

    /// Create a single space label
    pub fn space() -> Self {
        Self(Paint::new(" ".to_owned()))
    }

    /// Box the label.
    pub fn boxed(self) -> Box<dyn Element> {
        Box::new(self)
    }

    /// Color the label's foreground.
    pub fn fg(self, color: Color) -> Self {
        Self(self.0.fg(color))
    }

    /// Color the label's background.
    pub fn bg(self, color: Color) -> Self {
        Self(self.0.bg(color))
    }

    /// Style a label.
    pub fn style(self, style: Style) -> Self {
        Self(self.0.with_style(style))
    }

    /// Get inner paint object.
    pub fn paint(&self) -> &Paint<String> {
        &self.0
    }

    /// Return a filled cell from this label.
    pub fn filled(self, color: Color) -> Filled<Self> {
        Filled { item: self, color }
    }

    /// Wrap into a line.
    pub fn to_line(self) -> Line {
        Line::from(self)
    }
}

impl Element for Label {
    fn size(&self, _parent: Constraint) -> Size {
        Size::new(self.0.width(), 1)
    }

    fn render(&self, _parent: Constraint) -> Vec<Line> {
        vec![Line::new(self.clone())]
    }
}

impl fmt::Display for Label {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl Cell for Label {
    type Padded = Self;
    type Truncated = Self;

    fn background(&self) -> Color {
        self.paint().style.background
    }

    fn pad(&self, width: usize) -> Self::Padded {
        Self(self.0.pad(width))
    }

    fn truncate(&self, width: usize, delim: &str) -> Self::Truncated {
        Self(self.0.truncate(width, delim))
    }

    fn width(&self) -> usize {
        Cell::width(&self.0)
    }
}

impl<D: fmt::Display> From<Paint<D>> for Label {
    fn from(paint: Paint<D>) -> Self {
        Self(Paint {
            item: cleanup(paint.item.to_string().as_str()),
            style: paint.style,
        })
    }
}

impl From<String> for Label {
    fn from(value: String) -> Self {
        Self::new(value.as_str())
    }
}

impl From<&str> for Label {
    fn from(value: &str) -> Self {
        Self::new(value)
    }
}

/// Create a new label from a [`Paint`] object.
pub fn label(s: impl Into<Paint<String>>) -> Label {
    Label::from(s.into())
}

/// Cleanup the input string for display as a label.
fn cleanup(input: &str) -> String {
    input.chars().filter(|c| *c != '\n' && *c != '\r').collect()
}
