/// A color struct to handle the various ANSI color systems.
///
/// The color is recorded internally as 8-bit RGB.
///
/// Example:
/// ```
/// use ansiconv::ANSIColor;
///
/// let green = ANSIColor::new(0, 255, 0);
/// ```
///
/// The struct implements the `Display` trait, and thus can be used inside
/// formatted string. It will be convert into an HTML hexadecimal color prefix
/// with a sharp (`#`).
/// Example:
/// ```
/// use ansiconv::ANSIColor;
///
/// let blue = ANSIColor::new(0, 0, 255);
/// println!("Color: {blue}");
/// ```
pub struct ANSIColor {
  red: u8,
  green: u8,
  blue: u8,
}

impl std::fmt::Display for ANSIColor {
  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    write!(f, "#{:02x}{:02x}{:02x}", self.red, self.green, self.blue)
  }
}

impl ANSIColor {

  /// Construct an instance from 8-bit RGB values.
  ///
  /// Example:
  /// ```
  /// use ansiconv::ANSIColor;
  ///
  /// let red = ANSIColor::new(255, 0, 0);
  /// ```
  #[must_use]
  pub fn new(red: u8, green: u8, blue: u8) -> Self {
    Self { red, green, blue }
  }

  /// Construct an `ANSIColor` instance from either 3-bit 4-bit ANSI color
  /// index.
  ///
  /// Example
  /// ```
  /// use ansiconv::ANSIColor;
  ///
  /// let cyan = ANSIColor::from_ansi_3_4_bit_color(6);
  /// let bright_cyan = ANSIColor::from_ansi_3_4_bit_color(14);
  /// ```
  #[must_use]
  pub fn from_ansi_3_4_bit_color(c: u8) -> Self {
    match c {
       0 => Self::new(0    ,0    ,0   ), // black
       1 => Self::new(0x80 ,0    ,0   ), // red
       2 => Self::new(0    ,0x80 ,0   ), // green
       3 => Self::new(0x80 ,0x80 ,0   ), // yellow
       4 => Self::new(0    ,0    ,0x80), // blue
       5 => Self::new(0x80 ,0    ,0x80), // magenta
       6 => Self::new(0    ,0x80 ,0x80), // cyan
       7 => Self::new(0xc0 ,0xc0 ,0xc0), // white (light grey)
       8 => Self::new(0x80 ,0x80 ,0x80), // grey
       9 => Self::new(0xe4 ,0x1e ,0x1d), // bright red
      10 => Self::new(0xff ,0xff ,0x00), // bright green
      11 => Self::new(0    ,0xff ,0   ), // bright yellow
      12 => Self::new(0x1a ,0x8f ,0xff), // bright blue
      13 => Self::new(0xff ,0    ,0xff), // bright magenta
      14 => Self::new(0    ,0xff ,0xff), // bright cyan
       _ => Self::new(0xff ,0xff ,0xff), // bright white
    }
  }

  /// Construct an `ANSIColor` instance from a 8-bit ANSI color index.
  ///
  /// Example:
  /// ```
  /// use ansiconv::ANSIColor;
  ///
  /// let seagreen = ANSIColor::from_ansi_8_bit_color(50);
  /// ```
  #[must_use]
  pub fn from_ansi_8_bit_color(index: u8) -> Self {

    // ANSI 3-bit 4-bit color
    if index < 16 {
      Self::from_ansi_3_4_bit_color(index)
    }

    // Colors
    else if index < 232 {

      // Compute indices
      let ir = (index - 16) / 36;
      let ig = ((index - 16) % 36) / 6;
      let ib = (index - 16) % 6;

      // Compute color value from index
      let i2v = |i| { if i == 0 { 0 } else { 55 + i * 40 }};

      Self::new(i2v(ir), i2v(ig), i2v(ib))
    }

    // Grays
    else {
      let c = (index - 232) * 10 + 8;
      Self::new(c, c, c)
    }
  }

  /// Convert to hexadecimal value (HTML style).
  ///
  /// Example:
  /// ```
  /// use ansiconv::ANSIColor;
  ///
  /// let color = ANSIColor::new(10, 32, 100);
  /// let hexa_string = color.to_hex();
  /// ```
  #[must_use]
  pub fn to_hex(&self) -> String {
    format!("{:02x}{:02x}{:02x}", self.red, self.green, self.blue)
  }
}
