// The sequences section of a compressed block (RFC 8878 section
// 3.1.1.3.2) and sequence execution (section 3.1.1.4). Sequences are
// executed as they are decoded, straight into the window.

///|
/// Decodes the compressed block in `block[0:block_size]` into the window.
/// No output is pending, so the block's at most `block_max` bytes fit.
fn Decoder::decode_block(self : Decoder) -> Unit raise Stop {
  let size = self.block_size
  let start = self.decode_literals(size)
  if start >= size {
    raise corrupt("a compressed block without its sequences section")
  }
  let block = self.block
  let b0 = block[start].to_int()
  let (count, header_end) = if b0 < 128 {
    (b0, start + 1)
  } else if b0 < 255 {
    if start + 2 > size {
      raise corrupt("a sequences header past its block")
    }
    (((b0 - 128) << 8) + block[start + 1].to_int(), start + 2)
  } else {
    if start + 3 > size {
      raise corrupt("a sequences header past its block")
    }
    (
      block[start + 1].to_int() + (block[start + 2].to_int() << 8) + 0x7F00,
      start + 3,
    )
  }
  let mut at = header_end
  if count == 0 {
    if at != size {
      raise corrupt("bytes after a sequences section without sequences")
    }
    self.put_literals(0, self.literals_len)
    return
  }
  if at >= size {
    raise corrupt("a sequences header past its block")
  }
  let modes = block[at].to_int()
  at += 1
  if (modes & 3) != 0 {
    raise corrupt("the sequences header's reserved bits are set")
  }
  let ll = self.sequence_table(
    (modes >> 6) & 3,
    self.ll_table,
    self.ll_store,
    ll_predefined,
    at,
    9,
    MAX_LL_CODE,
    "literals lengths",
  )
  self.ll_table = Some(ll.0)
  at += ll.1
  let of = self.sequence_table(
    (modes >> 4) & 3,
    self.of_table,
    self.of_store,
    of_predefined,
    at,
    8,
    MAX_OF_SYMBOL,
    "offsets",
  )
  self.of_table = Some(of.0)
  at += of.1
  let ml = self.sequence_table(
    (modes >> 2) & 3,
    self.ml_table,
    self.ml_store,
    ml_predefined,
    at,
    9,
    MAX_ML_CODE,
    "match lengths",
  )
  self.ml_table = Some(ml.0)
  at += ml.1
  self.execute(ll.0, of.0, ml.0, at, count)
}

///|
/// The table a mode selects, and the bytes its description used.
fn Decoder::sequence_table(
  self : Decoder,
  mode : Int32,
  previous : Fse?,
  store : Fse,
  predefined : Fse,
  at : Int32,
  max_log : Int32,
  max_symbol : Int32,
  name : String,
) -> (Fse, Int32) raise Stop {
  match mode {
    0 => (predefined, 0)
    1 => {
      if at >= self.block_size {
        raise corrupt("an RLE \{name} code past its block")
      }
      let symbol = self.block[at]
      if symbol.to_int() > max_symbol {
        raise corrupt("an RLE \{name} code out of range")
      }
      store.set_rle(symbol)
      (store, 1)
    }
    2 => {
      let used = store.read(
        self.block,
        at,
        self.block_size,
        max_log,
        max_symbol,
        self.scratch,
      )
      (store, used)
    }
    _ =>
      match previous {
        Some(table) => (table, 0)
        None => raise corrupt("repeated \{name} codes before any table")
      }
  }
}

///|
/// Copies `n` literals from `at`, within the block limit.
fn Decoder::put_literals(
  self : Decoder,
  at : Int32,
  n : Int32,
) -> Unit raise Stop {
  if self.block_out + n > self.block_max {
    raise corrupt("a block that decodes past the block limit")
  }
  self.reserve_window(n)
  self.put_array(self.literals, at, n)
}

///|
/// Makes room for `n` bytes of a compressed block. The block decodes to at
/// most `block_max` bytes, no more than the window, and no output was
/// pending when it started, so the room is always there.
fn Decoder::reserve_window(self : Decoder, n : Int32) -> Unit raise Stop {
  guard self.ensure_space(n) else {
    raise corrupt("a block that does not fit the window")
  }
}

///|
/// Decodes `count` sequences from the bitstream `block[at:block_size]` and
/// executes each as it comes, then copies the remaining literals.
fn Decoder::execute(
  self : Decoder,
  ll : Fse,
  of : Fse,
  ml : Fse,
  at : Int32,
  count : Int32,
) -> Unit raise Stop {
  let bits = BackwardBits::new(self.block, at, self.block_size)
  let mut ll_state = bits.read(ll.log)
  let mut of_state = bits.read(of.log)
  let mut ml_state = bits.read(ml.log)
  let rep = self.rep
  let mut lit = 0
  for i in 0.. MAX_OF_CODE {
      raise corrupt("an offset code above \{MAX_OF_CODE}")
    }
    let offset_value = (1 << of_code) + bits.read_long(of_code)
    let match_len = ml_base[ml_code] + bits.read(ml_bits[ml_code])
    let lit_len = ll_base[ll_code] + bits.read(ll_bits[ll_code])
    if i < count - 1 {
      ll_state = ll.next(ll_state, bits)
      ml_state = ml.next(ml_state, bits)
      of_state = of.next(of_state, bits)
    }
    // The offset, and the repeat offsets after it (RFC 8878 section
    // 3.1.2.5).
    let offset = if offset_value > 3 {
      let offset = offset_value - 3
      rep[2] = rep[1]
      rep[1] = rep[0]
      rep[0] = offset
      offset
    } else {
      let index = offset_value - 1 + (if lit_len == 0 { 1 } else { 0 })
      if index == 0 {
        rep[0]
      } else {
        let offset = if index < 3 { rep[index] } else { rep[0] - 1 }
        if index > 1 {
          rep[2] = rep[1]
        }
        rep[1] = rep[0]
        rep[0] = offset
        offset
      }
    }
    if lit_len > self.literals_len - lit {
      raise corrupt("a sequence with more literals than the block holds")
    }
    self.put_literals(lit, lit_len)
    lit += lit_len
    if offset == 0 || offset > self.filled {
      raise corrupt("an offset beyond the decoded data")
    }
    if self.block_out + match_len > self.block_max {
      raise corrupt("a block that decodes past the block limit")
    }
    self.reserve_window(match_len)
    self.copy_back(offset, match_len)
  }
  if bits.pos != 0 {
    raise corrupt("a sequences bitstream that is not used up exactly")
  }
  self.put_literals(lit, self.literals_len - lit)
}