use cssparser::Parser;
use std::fmt;

use crate::style::{
  CssSyntaxKind, CssToken, FromCss, MakeComputed, ParseResult, ToCss, properties::write_css_string,
  tw::TailwindPropertyParser,
};

#[derive(Debug, Clone, PartialEq)]
/// Represents a line clamp value.
#[non_exhaustive]
pub struct LineClamp {
  /// The number of lines to clamp.
  pub count: u32,
  /// The ellipsis character to use when the text is clamped.
  pub ellipsis: Option<String>,
}

impl MakeComputed for LineClamp {}

impl TailwindPropertyParser for LineClamp {
  fn parse_tw(token: &str) -> Option<Self> {
    if token.eq_ignore_ascii_case("none") {
      return Some(LineClamp {
        count: 0,
        ellipsis: None,
      });
    }
    let count = token.parse::<u32>().ok()?;
    if count == 0 {
      return None;
    }
    Some(LineClamp {
      count,
      ellipsis: None,
    })
  }
}

impl From<u32> for LineClamp {
  fn from(count: u32) -> Self {
    Self {
      count,
      ellipsis: None,
    }
  }
}

impl<'i> FromCss<'i> for LineClamp {
  fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
    if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
      return Ok(LineClamp {
        count: 0,
        ellipsis: None,
      });
    }
    let count = input.try_parse(Parser::expect_integer)?;
    let ellipsis = input.try_parse(Parser::expect_string_cloned).ok();

    Ok(LineClamp {
      count: count as u32,
      ellipsis: ellipsis.map(|s| s.to_string()),
    })
  }

  const VALID_TOKENS: &'static [CssToken] = &[
    CssToken::Keyword("none"),
    CssToken::Syntax(CssSyntaxKind::Integer),
    CssToken::Syntax(CssSyntaxKind::String),
  ];
}

impl ToCss for LineClamp {
  fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
    if self.count == 0 && self.ellipsis.is_none() {
      return dest.write_str("none");
    }
    match &self.ellipsis {
      Some(e) => {
        write!(dest, "{} ", self.count)?;
        write_css_string(dest, e)
      }
      None => write!(dest, "{}", self.count),
    }
  }
}
