// Frames and blocks (RFC 8878 sections 3.1 and 3.1.1.2): the units of the
// state machine outside compressed blocks.

///|
/// The magic number that starts every frame, or a skippable one.
fn Decoder::magic(self : Decoder) -> Unit raise Stop {
  self.fill(4)
  let magic = self.hdr_le(0, 4).to_uint()
  if magic == FRAME_MAGIC {
    self.hdr_len = 0
    self.state = FrameHeader
  } else if (magic & 0xFFFFFFF0) == 0x184D2A50 {
    self.state = SkippableSize
  } else if self.frames == 0 {
    raise corrupt("not a zstd stream")
  } else {
    raise corrupt("data after the last frame that starts no frame")
  }
}

///|
fn Decoder::skippable_size(self : Decoder) -> Unit raise Stop {
  self.fill(8)
  self.remaining = self.hdr_le(4, 4).reinterpret_as_int64()
  self.hdr_len = 0
  self.state = SkippableData
}

///|
/// Skips a skippable frame's data without keeping it.
fn Decoder::skippable_data(self : Decoder) -> Unit raise Stop {
  let take = self.remaining.min((self.view.length() - self.in_pos).to_int64())
  self.in_pos += take.to_int()
  self.remaining -= take
  if self.remaining > 0 {
    raise NeedInput
  }
  self.frames += 1
  self.state = Magic
}

///|
/// The frame header: what the frame holds and the window it needs.
fn Decoder::frame_header(self : Decoder) -> Unit raise Stop {
  self.fill(1)
  let descriptor = self.hdr[0].to_int()
  let fcs_flag = descriptor >> 6
  let single_segment = (descriptor & 0x20) != 0
  if (descriptor & 0x08) != 0 {
    raise corrupt("the frame header's reserved bit is set")
  }
  let did_size = match descriptor & 3 {
    0 => 0
    1 => 1
    2 => 2
    _ => 4
  }
  let fcs_size = match fcs_flag {
    0 => if single_segment { 1 } else { 0 }
    1 => 2
    2 => 4
    _ => 8
  }
  let window_size = if single_segment { 0 } else { 1 }
  self.fill(1 + window_size + did_size + fcs_size)
  let mut at = 1
  // Computed in 64 bits: a descriptor may ask for up to 2^41 + 7 * 2^38.
  let mut window : UInt64 = 0
  if !single_segment {
    let b = self.hdr[at].to_int()
    let base = 1UL << (10 + (b >> 3))
    window = base + (base >> 3) * (b & 7).to_uint64()
    at += 1
  }
  if did_size > 0 && self.hdr_le(at, did_size) != 0 {
    raise Fail(
      DictionaryUnsupported,
      "the frame needs dictionary \{self.hdr_le(at, did_size)}",
    )
  }
  at += did_size
  self.content_size = if fcs_size > 0 {
    let size = self.hdr_le(at, fcs_size)
    Some(if fcs_size == 2 { size + 256 } else { size })
  } else {
    None
  }
  if single_segment {
    window = self.content_size.unwrap_or(0)
  }
  let limit = 1UL << self.max_window_bits
  if window > limit {
    raise Fail(
      WindowTooLarge,
      "the frame needs a \{window}-byte window, over the \{limit}-byte limit",
    )
  }
  self.window = window.to_int()
  self.block_max = self.window.min(MAX_BLOCK_SIZE)
  self.has_checksum = (descriptor & 0x04) != 0
  self.frame_out = 0
  self.xxh.reset()
  self.huffman_valid = false
  self.ll_table = None
  self.of_table = None
  self.ml_table = None
  self.rep[0] = 1
  self.rep[1] = 4
  self.rep[2] = 8
  self.new_frame = true
  self.hdr_len = 0
  self.state = BlockHeader
}

///|
/// Sets the window up for a new frame once the last frame's output is all
/// handed out; false until then.
fn Decoder::start_window(self : Decoder) -> Bool {
  if self.pending > 0 {
    self.drain()
    if self.pending > 0 {
      return false
    }
  }
  if self.ring.length() > self.window {
    self.ring = FixedArray::make(0, b'\x00')
  }
  self.ring_max = self.window
  self.wpos = 0
  self.filled = 0
  self.new_frame = false
  true
}

///|
fn Decoder::block_header(self : Decoder) -> Status? raise Stop {
  if self.new_frame && !self.start_window() {
    return Some(NeedMoreOutput)
  }
  self.fill(3)
  let header = self.hdr_le(0, 3).to_int()
  self.hdr_len = 0
  self.last_block = (header & 1) != 0
  let size = header >> 3
  self.block_size = size
  self.block_out = 0
  match (header >> 1) & 3 {
    0 => {
      if size > self.block_max {
        raise corrupt("a raw block larger than the block limit")
      }
      self.remaining = size.to_int64()
      self.state = RawBlock
    }
    1 => {
      if size > self.block_max {
        raise corrupt("an RLE block larger than the block limit")
      }
      self.state = RleByte
    }
    2 => {
      if size > self.block_max {
        raise corrupt("a compressed block larger than the block limit")
      }
      if size == 0 {
        raise corrupt("an empty compressed block")
      }
      if self.block.length() < size {
        self.block = FixedArray::make(size, b'\x00')
      }
      self.block_len = 0
      self.state = CompressedBlock
    }
    _ => raise corrupt("a block of the reserved type")
  }
  None
}

///|
fn Decoder::raw_block(self : Decoder) -> Status? raise Stop {
  while self.remaining > 0 {
    let available = self.view.length() - self.in_pos
    if available == 0 {
      raise NeedInput
    }
    let n = self.room(self.remaining.to_int().min(available))
    if n == 0 {
      return Some(NeedMoreOutput)
    }
    self.copy_input(n)
    self.remaining -= n.to_int64()
  }
  self.end_block()
  None
}

///|
fn Decoder::rle_byte(self : Decoder) -> Unit raise Stop {
  self.fill(1)
  self.rle_byte = self.hdr[0]
  self.hdr_len = 0
  self.remaining = self.block_size.to_int64()
  self.state = RleRun
}

///|
fn Decoder::rle_run(self : Decoder) -> Status? raise Stop {
  while self.remaining > 0 {
    let n = self.room(self.remaining.to_int())
    if n == 0 {
      return Some(NeedMoreOutput)
    }
    self.put_run(self.rle_byte, n)
    self.remaining -= n.to_int64()
  }
  self.end_block()
  None
}

///|
/// Gathers a compressed block, then decodes it once earlier output is all
/// handed out.
fn Decoder::compressed_block(self : Decoder) -> Status? raise Stop {
  let take = (self.block_size - self.block_len).min(
    self.view.length() - self.in_pos,
  )
  if take > 0 {
    self.block.blit_from_bytesview(
      self.block_len,
      self.view[self.in_pos:self.in_pos + take],
    )
    self.block_len += take
    self.in_pos += take
  }
  if self.block_len < self.block_size {
    raise NeedInput
  }
  if self.pending > 0 {
    self.drain()
    if self.pending > 0 {
      return Some(NeedMoreOutput)
    }
  }
  self.decode_block()
  self.end_block()
  None
}

///|
fn Decoder::end_block(self : Decoder) -> Unit raise Stop {
  if self.content_size is Some(size) && self.frame_out > size {
    raise corrupt("the frame holds more than its declared content size")
  }
  if !self.last_block {
    self.state = BlockHeader
  } else if self.has_checksum {
    self.state = Checksum
  } else {
    self.end_frame()
  }
}

///|
fn Decoder::checksum(self : Decoder) -> Unit raise Stop {
  self.fill(4)
  let stored = self.hdr_le(0, 4).to_uint()
  self.hdr_len = 0
  if stored != self.xxh.digest().to_uint() {
    raise Fail(ChecksumMismatch, "the frame's content checksum does not match")
  }
  self.end_frame()
}

///|
fn Decoder::end_frame(self : Decoder) -> Unit raise Stop {
  if self.content_size is Some(size) && self.frame_out != size {
    raise corrupt("the frame holds less than its declared content size")
  }
  self.frames += 1
  self.state = Magic
}