// Port of vnc-rs codec/trle.rs palette and run decoding, corrected to RFC 6143
// TRLE (16x16, no length prefix, palette reuse), unlike upstream's 64x64 routine.

///|
fn packed_tile(
  r : Reader,
  palette : Array[Int],
  w : Int,
  h : Int,
) -> Array[Int] raise RfbError {
  let n = palette.length()
  if n < 2 || n > 16 {
    raise Invalid("packed palette size")
  }
  let bits = if n == 2 { 1 } else if n <= 4 { 2 } else { 4 }
  let mask = (1 << bits) - 1
  let out = []
  for _ in 0..> shift) & mask
      if index >= n {
        raise Invalid("palette index")
      }
      out.push(palette[index])
      shift -= bits
    }
  }
  out
}

///|
fn trle_pixels(
  r : Reader,
  f : PixelFormat,
  w : Int,
  h : Int,
) -> Array[Int] raise RfbError {
  let out = Array::make(w * h, 0)
  let mut palette : Array[Int] = []
  let mut ty = 0
  while ty < h {
    let th = if h - ty < 16 { h - ty } else { 16 }
    let mut tx = 0
    while tx < w {
      let tw = if w - tx < 16 { w - tx } else { 16 }
      let mode = r.u8()
      let tile = if mode == 0 {
        Array::makei(tw * th, _ => read_pixel(r, f, true))
      } else if mode == 1 {
        Array::make(tw * th, read_pixel(r, f, true))
      } else if mode >= 2 && mode <= 16 {
        palette = Array::makei(mode, _ => read_pixel(r, f, true))
        packed_tile(r, palette, tw, th)
      } else if mode == 127 {
        packed_tile(r, palette, tw, th)
      } else if mode == 128 {
        rle_tile(r, f, [], tw * th)
      } else if mode == 129 {
        if palette.length() < 2 {
          raise Invalid("missing reused palette")
        }
        rle_tile(r, f, palette, tw * th)
      } else if mode >= 130 {
        palette = Array::makei(mode - 128, _ => read_pixel(r, f, true))
        rle_tile(r, f, palette, tw * th)
      } else {
        raise Invalid("reserved TRLE subencoding")
      }
      for y in 0.. Array[Int] raise RfbError {
  format.validate()
  check_dimensions(width, height, 4194304)
  let r = Reader::new(data)
  let out = trle_pixels(r, format, width, height)
  if r.pos != data.length() {
    raise Invalid("trailing TRLE bytes")
  }
  out
}

///|
fn run_length(r : Reader, remaining : Int) -> Int raise RfbError {
  let mut length = 1
  while true {
    let b = r.u8()
    if b > remaining - length {
      raise Invalid("TRLE run exceeds tile")
    }
    length += b
    if b != 255 {
      break
    }
  }
  length
}

///|
fn rle_tile(
  r : Reader,
  f : PixelFormat,
  palette : Array[Int],
  count : Int,
) -> Array[Int] raise RfbError {
  let out = []
  while out.length() < count {
    let (color, length) = if palette.is_empty() {
      let color = read_pixel(r, f, true)
      (color, run_length(r, count - out.length()))
    } else {
      let control = r.u8()
      let index = control & 127
      if index >= palette.length() {
        raise Invalid("palette index")
      }
      let length = if (control & 128) != 0 {
        run_length(r, count - out.length())
      } else {
        1
      }
      (palette[index], length)
    }
    for _ in 0..