///|
/// True-color pixel format. Validate before reading any pixel.
pub(all) struct PixelFormat {
  bits : Int
  depth : Int
  big_endian : Bool
  red_max : Int
  green_max : Int
  blue_max : Int
  red_shift : Int
  green_shift : Int
  blue_shift : Int
} derive(Debug, Eq)

///|
/// Canonical wire format: little endian B,G,R,padding, represented as RGB integers.
pub fn rgb32() -> PixelFormat {
  {
    bits: 32,
    depth: 24,
    big_endian: false,
    red_max: 255,
    green_max: 255,
    blue_max: 255,
    red_shift: 16,
    green_shift: 8,
    blue_shift: 0,
  }
}

///|
pub fn PixelFormat::validate(self : PixelFormat) -> Unit raise RfbError {
  if !(self.bits == 8 || self.bits == 16 || self.bits == 32) ||
    self.depth <= 0 ||
    self.depth > self.bits {
    raise Invalid("pixel depth")
  }
  let maxima = [self.red_max, self.green_max, self.blue_max]
  let shifts = [self.red_shift, self.green_shift, self.blue_shift]
  let mut used : UInt = 0
  let mut significant = 0
  for i in 0..<3 {
    let max = maxima[i]
    if max <= 0 || max > 65535 || (max & (max + 1)) != 0 {
      raise Invalid("channel maximum")
    }
    let mut width = 0
    let mut v = max
    while v > 0 {
      width += 1
      v = v >> 1
    }
    if shifts[i] < 0 || shifts[i] + width > self.bits {
      raise Invalid("channel shift")
    }
    let mask = max.reinterpret_as_uint() << shifts[i]
    if (mask & used) != 0 {
      raise Invalid("overlapping channels")
    }
    used = used | mask
    significant += width
  }
  if significant > self.depth {
    raise Invalid("pixel depth smaller than channels")
  }
}

///|
fn read_format(r : Reader) -> PixelFormat raise RfbError {
  let bits = r.u8()
  let depth = r.u8()
  let endian = r.u8()
  let color = r.u8()
  if endian > 1 || color != 1 {
    raise Invalid("indexed color or endian flag")
  }
  let red_max = r.u16()
  let green_max = r.u16()
  let blue_max = r.u16()
  let red_shift = r.u8()
  let green_shift = r.u8()
  let blue_shift = r.u8()
  ignore(r.take(3))
  let f = {
    bits,
    depth,
    big_endian: endian == 1,
    red_max,
    green_max,
    blue_max,
    red_shift,
    green_shift,
    blue_shift,
  }
  f.validate()
  f
}

///|
fn write_format(f : PixelFormat) -> Array[Int] raise RfbError {
  f.validate()
  let b = [f.bits, f.depth, if f.big_endian { 1 } else { 0 }, 1]
  put16(b, f.red_max)
  put16(b, f.green_max)
  put16(b, f.blue_max)
  b.append([f.red_shift, f.green_shift, f.blue_shift, 0, 0, 0])
  b
}

///|
fn read_pixel(
  r : Reader,
  f : PixelFormat,
  compact : Bool,
) -> Int raise RfbError {
  // CPIXEL drops an unused extreme byte only for 32-bit true-color depth <= 24.
  let mask = (f.red_max.reinterpret_as_uint() << f.red_shift) |
    (f.green_max.reinterpret_as_uint() << f.green_shift) |
    (f.blue_max.reinterpret_as_uint() << f.blue_shift)
  let omit_high = compact &&
    f.bits == 32 &&
    f.depth <= 24 &&
    (mask & 0xff000000U) == 0
  let omit_low = compact &&
    f.bits == 32 &&
    f.depth <= 24 &&
    !omit_high &&
    (mask & 255U) == 0
  let mut v : UInt = 0
  for i in 0..<(f.bits / 8) {
    let shift = if f.big_endian { f.bits - 8 - i * 8 } else { i * 8 }
    if !(omit_high && shift == 24) && !(omit_low && shift == 0) {
      v = v | (r.u8().reinterpret_as_uint() << shift)
    }
  }
  let red = ((v >> f.red_shift) & f.red_max.reinterpret_as_uint()).reinterpret_as_int() *
    255 /
    f.red_max
  let green = ((v >> f.green_shift) & f.green_max.reinterpret_as_uint()).reinterpret_as_int() *
    255 /
    f.green_max
  let blue = ((v >> f.blue_shift) & f.blue_max.reinterpret_as_uint()).reinterpret_as_int() *
    255 /
    f.blue_max
  (red << 16) | (green << 8) | blue
}