///|
/// Extract a single channel as an `h * w` byte array.
///
/// - `ch`: channel index (0=R, 1=G, 2=B, 3=A).
pub fn Image::channel(self : Image, ch : Int) -> Array[Byte] {
  Array::makei(self.h * self.w, fn(i) { self.data[i * 4 + ch] })
}

///|
/// Red channel.
pub fn Image::channel_r(self : Image) -> Array[Byte] {
  self.channel(0)
}

///|
/// Green channel.
pub fn Image::channel_g(self : Image) -> Array[Byte] {
  self.channel(1)
}

///|
/// Blue channel.
pub fn Image::channel_b(self : Image) -> Array[Byte] {
  self.channel(2)
}

///|
/// Alpha channel.
pub fn Image::channel_a(self : Image) -> Array[Byte] {
  self.channel(3)
}

///|
/// Return a copy of the image with one channel replaced.
///
/// - `ch`: channel index (0=R, 1=G, 2=B, 3=A).
/// - `data`: per-pixel values; length must equal `h * w`.
pub fn Image::set_channel(self : Image, ch : Int, data : Array[Byte]) -> Image {
  let out = self.data.copy()
  let n = self.h * self.w
  for i = 0; i < n; i = i + 1 {
    out[i * 4 + ch] = data[i]
  }
  { data: out, h: self.h, w: self.w }
}

///|
/// Split into four channel arrays `(r, g, b, a)`.
pub fn Image::split_channels(
  self : Image,
) -> (Array[Byte], Array[Byte], Array[Byte], Array[Byte]) {
  (self.channel(0), self.channel(1), self.channel(2), self.channel(3))
}

///|
/// Merge channel arrays into a `1 × n` image (single row). Alpha defaults
/// to fully opaque (255) when `None`. Raises `ImageError` on length mismatch.
pub fn merge_channels(
  r : Array[Byte],
  g : Array[Byte],
  b : Array[Byte],
  a : Array[Byte]?,
) -> Image raise ImageError {
  let n = r.length()
  if g.length() != n || b.length() != n {
    raise ImageError("merge_channels: channel length mismatch")
  }
  match a {
    Some(av) =>
      if av.length() != n {
        raise ImageError("merge_channels: alpha length mismatch")
      }
    None => ()
  }
  let data = Array::make(n * 4, (0 : Byte))
  for i = 0; i < n; i = i + 1 {
    data[i * 4] = r[i]
    data[i * 4 + 1] = g[i]
    data[i * 4 + 2] = b[i]
    data[i * 4 + 3] = match a {
      Some(av) => av[i]
      None => 255
    }
  }
  { data, h: 1, w: n }
}