// Color models, Hex parsing, HSL conversions, and color blending routines.

///|
fn char_at(s : String, idx : Int) -> Char {
  s[idx].to_int().unsafe_to_char()
}

///|
pub struct RGBA {
  r : Double // 0.0 .. 1.0
  g : Double // 0.0 .. 1.0
  b : Double // 0.0 .. 1.0
  a : Double // 0.0 .. 1.0
} derive(Eq, Debug)

///|
pub impl Show for RGBA with fn output(self, logger) {
  logger.write_string(self.to_hex())
}

///|
pub fn RGBA::new(r : Double, g : Double, b : Double, a : Double) -> RGBA {
  let clamp = fn(v : Double) -> Double {
    if v < 0.0 {
      0.0
    } else if v > 1.0 {
      1.0
    } else {
      v
    }
  }
  { r: clamp(r), g: clamp(g), b: clamp(b), a: clamp(a) }
}

///|
pub fn RGBA::from_bytes(r : Int, g : Int, b : Int, a : Double) -> RGBA {
  RGBA::new(
    r.to_double() / 255.0,
    g.to_double() / 255.0,
    b.to_double() / 255.0,
    a,
  )
}

///|
pub fn RGBA::black() -> RGBA {
  RGBA::new(0.0, 0.0, 0.0, 1.0)
}

///|
pub fn RGBA::white() -> RGBA {
  RGBA::new(1.0, 1.0, 1.0, 1.0)
}

///|
pub fn RGBA::transparent() -> RGBA {
  RGBA::new(0.0, 0.0, 0.0, 0.0)
}

///|
pub fn RGBA::red() -> RGBA {
  RGBA::new(1.0, 0.0, 0.0, 1.0)
}

///|
pub fn RGBA::green() -> RGBA {
  RGBA::new(0.0, 1.0, 0.0, 1.0)
}

///|
pub fn RGBA::blue() -> RGBA {
  RGBA::new(0.0, 0.0, 1.0, 1.0)
}

///|
fn hex_digit_value(ch : Char) -> Int {
  if ch >= '0' && ch <= '9' {
    ch.to_int() - '0'.to_int()
  } else if ch >= 'a' && ch <= 'f' {
    ch.to_int() - 'a'.to_int() + 10
  } else if ch >= 'A' && ch <= 'F' {
    ch.to_int() - 'A'.to_int() + 10
  } else {
    0
  }
}

///|
pub fn RGBA::from_hex(hex_str : String) -> RGBA {
  let s = if hex_str.has_prefix("#") { hex_str[1:].to_owned() } else { hex_str }
  let len = s.length()
  if len == 6 {
    let r = hex_digit_value(char_at(s, 0)) * 16 + hex_digit_value(char_at(s, 1))
    let g = hex_digit_value(char_at(s, 2)) * 16 + hex_digit_value(char_at(s, 3))
    let b = hex_digit_value(char_at(s, 4)) * 16 + hex_digit_value(char_at(s, 5))
    RGBA::from_bytes(r, g, b, 1.0)
  } else if len == 8 {
    let r = hex_digit_value(char_at(s, 0)) * 16 + hex_digit_value(char_at(s, 1))
    let g = hex_digit_value(char_at(s, 2)) * 16 + hex_digit_value(char_at(s, 3))
    let b = hex_digit_value(char_at(s, 4)) * 16 + hex_digit_value(char_at(s, 5))
    let a = hex_digit_value(char_at(s, 6)) * 16 + hex_digit_value(char_at(s, 7))
    RGBA::from_bytes(r, g, b, a.to_double() / 255.0)
  } else if len == 3 {
    let r = hex_digit_value(char_at(s, 0)) * 17
    let g = hex_digit_value(char_at(s, 1)) * 17
    let b = hex_digit_value(char_at(s, 2)) * 17
    RGBA::from_bytes(r, g, b, 1.0)
  } else {
    RGBA::black()
  }
}

///|
fn byte_to_hex(val : Int) -> String {
  let digits = [
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
  ]
  let d1 = digits[val / 16 % 16].to_string()
  let d2 = digits[val % 16].to_string()
  d1 + d2
}

///|
pub fn RGBA::to_hex(self : RGBA) -> String {
  let r_int = (self.r * 255.0 + 0.5).to_int()
  let g_int = (self.g * 255.0 + 0.5).to_int()
  let b_int = (self.b * 255.0 + 0.5).to_int()
  let a_int = (self.a * 255.0 + 0.5).to_int()

  if a_int >= 255 {
    "#" + byte_to_hex(r_int) + byte_to_hex(g_int) + byte_to_hex(b_int)
  } else {
    "#" +
    byte_to_hex(r_int) +
    byte_to_hex(g_int) +
    byte_to_hex(b_int) +
    byte_to_hex(a_int)
  }
}

///|
pub fn RGBA::blend(self : RGBA, overlay : RGBA) -> RGBA {
  let out_a = overlay.a + self.a * (1.0 - overlay.a)
  if out_a < 0.000000000001 {
    RGBA::transparent()
  } else {
    let r = (overlay.r * overlay.a + self.r * self.a * (1.0 - overlay.a)) /
      out_a
    let g = (overlay.g * overlay.a + self.g * self.a * (1.0 - overlay.a)) /
      out_a
    let b = (overlay.b * overlay.a + self.b * self.a * (1.0 - overlay.a)) /
      out_a
    RGBA::new(r, g, b, out_a)
  }
}

///|
pub fn RGBA::lerp(self : RGBA, target : RGBA, t : Double) -> RGBA {
  RGBA::new(
    self.r + (target.r - self.r) * t,
    self.g + (target.g - self.g) * t,
    self.b + (target.b - self.b) * t,
    self.a + (target.a - self.a) * t,
  )
}