// APNG (the third edition's 4.9 and 11.3.6): acTL says the image is
// animated, each fcTL describes a frame, and fdAT carries the data of the
// frames after the default image, each with a sequence number that fcTL
// shares. A frame is reported at its first data chunk, so that everything
// before it is known, and ends at the next fcTL or IEND: text chunks may
// come between the fdAT chunks of one frame.

///|
/// What to do with a frame's rectangle once it has been shown, before the
/// next frame is drawn (dispose_op).
pub(all) enum Dispose {
  /// Leave the canvas as it is.
  Keep
  /// Clear the rectangle to fully transparent black.
  Background
  /// Put back what the rectangle held before the frame was drawn.
  Previous
} derive(Eq, @debug.Debug)

///|
/// `Dispose::equal` and `Dispose::not_equal`, the derived comparison.
pub extend Dispose with Eq::{equal, not_equal}

///|
/// `Dispose::to_repr`, the derived debug representation.
pub extend Dispose with @debug.Debug::{to_repr}

///|
/// How a frame's pixels are put on the canvas (blend_op).
pub(all) enum Blend {
  /// They replace the rectangle's, alpha included.
  Source
  /// They are composited over the rectangle's, by their alpha.
  Over
} derive(Eq, @debug.Debug)

///|
/// `Blend::equal` and `Blend::not_equal`, the derived comparison.
pub extend Blend with Eq::{equal, not_equal}

///|
/// `Blend::to_repr`, the derived debug representation.
pub extend Blend with @debug.Debug::{to_repr}

///|
/// A frame's place and how it is shown, from its fcTL, or the whole image
/// for a default image without one.
priv struct Control {
  mut x : Int32
  mut y : Int32
  mut width : Int32
  mut height : Int32
  mut delay_numerator : Int32
  mut delay_denominator : Int32
  mut dispose : Dispose
  mut blend : Blend
}

///|
fn Control::new() -> Control {
  {
    x: 0,
    y: 0,
    width: 0,
    height: 0,
    delay_numerator: 0,
    delay_denominator: 100,
    dispose: Keep,
    blend: Source,
  }
}

///|
/// The whole image `width` x `height`, shown at once.
fn Control::whole(self : Control, width : Int32, height : Int32) -> Unit {
  self.x = 0
  self.y = 0
  self.width = width
  self.height = height
  self.delay_numerator = 0
  self.delay_denominator = 100
  self.dispose = Keep
  self.blend = Source
}

///|
fn Control::copy(self : Control, from : Control) -> Unit {
  self.x = from.x
  self.y = from.y
  self.width = from.width
  self.height = from.height
  self.delay_numerator = from.delay_numerator
  self.delay_denominator = from.delay_denominator
  self.dispose = from.dispose
  self.blend = from.blend
}

///|
/// Reads fcTL's 26 bytes in `data` (its sequence number already checked)
/// for an image `width` x `height`, refusing values that describe no frame.
/// The values are those to apply: a delay's denominator of 0 is 100, and a
/// first frame's dispose op of PREVIOUS is BACKGROUND, as 11.3.6.2 says.
fn Control::read(
  self : Control,
  data : FixedArray[Byte],
  width : Int32,
  height : Int32,
  default_image~ : Bool,
  first~ : Bool,
) -> Unit raise Stop {
  let frame_width = u32(data, 4)
  let frame_height = u32(data, 8)
  let x = u32(data, 12)
  let y = u32(data, 16)
  let dispose = data[24].to_int()
  let blend = data[25].to_int()
  if @rules.check_frame(
      width.reinterpret_as_uint(),
      height.reinterpret_as_uint(),
      frame_width,
      frame_height,
      x,
      y,
      dispose,
      blend,
      first=default_image,
    )
    is Some(message) {
    raise Fail(Corrupt, "fcTL: \{message}")
  }
  self.x = x.reinterpret_as_int()
  self.y = y.reinterpret_as_int()
  self.width = frame_width.reinterpret_as_int()
  self.height = frame_height.reinterpret_as_int()
  self.delay_numerator = u16(data, 20)
  self.delay_denominator = if u16(data, 22) == 0 { 100 } else { u16(data, 22) }
  self.dispose = match dispose {
    0 => Keep
    1 => Background
    _ => if first { Background } else { Previous }
  }
  self.blend = if blend == 0 { Source } else { Over }
}

///|
/// Whether the image is an APNG: an acTL with at least one frame came
/// before IDAT, and the decoder reads animations (`animation=true`). Valid
/// from `Frame` until `reset`.
pub fn Decoder::animated(self : Decoder) -> Bool {
  self.animated
}

///|
/// The frames of the animation acTL announces (the default image among
/// them when it is part of it), and 0 for an image that is not animated.
/// The decoder checks the count at IEND. Valid from `Frame` until `reset`.
pub fn Decoder::frame_count(self : Decoder) -> UInt32 {
  self.frames
}

///|
/// How many times acTL says to play the animation, 0 for without end.
/// Valid from `Frame` until `reset`.
pub fn Decoder::plays(self : Decoder) -> UInt32 {
  self.plays
}

///|
/// Whether the current frame is one of the animation's: every frame of
/// fdAT chunks, and the default image when an fcTL comes before IDAT. A
/// default image that is not part of the animation, and the image of a PNG
/// that is not animated, is not. Valid from `Frame` until the next `Frame`
/// or `Done`.
pub fn Decoder::in_animation(self : Decoder) -> Bool {
  self.in_animation
}

///|
/// The numerator of the current frame's delay, in seconds: how long it is
/// shown. Valid from `Frame` until the next `Frame` or `Done`.
pub fn Decoder::delay_numerator(self : Decoder) -> Int32 {
  self.control.delay_numerator
}

///|
/// The denominator of the current frame's delay, 100 when fcTL says 0.
/// Valid from `Frame` until the next `Frame` or `Done`.
pub fn Decoder::delay_denominator(self : Decoder) -> Int32 {
  self.control.delay_denominator
}

///|
/// What to do with the current frame's rectangle after it is shown;
/// PREVIOUS on the first frame comes as `Background`. Valid from `Frame`
/// until the next `Frame` or `Done`.
pub fn Decoder::dispose(self : Decoder) -> Dispose {
  self.control.dispose
}

///|
/// How the current frame's pixels go on the canvas. Valid from `Frame`
/// until the next `Frame` or `Done`.
pub fn Decoder::blend(self : Decoder) -> Blend {
  self.control.blend
}