///|
const MIN_LOOKAHEAD : Int32 = 258

// Block planning for the streaming encoder. Input lives in a fixed mirrored
// ring: the first half is indexed by absolute position modulo 64 KiB and the
// second half mirrors it for branch-free SIMD comparisons across the wrap.
// The parser and its 32 KiB matchfinder survive every block boundary.

///|
fn Deflater::pending_bytes(deflater : Deflater) -> Int32 {
  deflater.pending_end - deflater.parse_pos
}

///|
/// Append `input` to the ring with bulk copies into both mirrored halves,
/// split at the ring wrap.
fn Deflater::push_pending(deflater : Deflater, input : BytesView) -> Unit {
  let mut rest = input
  while rest.length() > 0 {
    let offset = deflater.pending_end & STREAM_RING_MASK
    let count = rest.length().min(STREAM_RING_SIZE - offset)
    let chunk = rest[:count]
    deflater.pending.blit_from_bytesview(offset, chunk)
    deflater.pending.blit_from_bytesview(offset + STREAM_RING_SIZE, chunk)
    deflater.pending_end = deflater.pending_end + count
    rest = rest[count:]
  }
}

///|
fn Deflater::write_stored_pending(
  deflater : Deflater,
  is_final~ : Bool,
) -> Unit {
  deflater.w.write_stored_window(
    deflater.pending,
    deflater.parse_pos,
    deflater.pending_end,
    is_final~,
  )
  deflater.parse_pos = deflater.pending_end
}

///|
// The final three bytes of an earlier accepted chunk could not yet form a
// 4-byte hash. Insert them once new lookahead makes them hashable; all older
// history is already present in the persistent chain.
fn Deflater::chain_ready_history(deflater : Deflater) -> Unit {
  let ready_end = deflater.parse_pos.min((deflater.pending_end - 3).max(0))
  while deflater.hashed_through < ready_end {
    deflater.finder.insert_window_pos(
      deflater.pending,
      deflater.hashed_through,
      deflater.pending_end,
    )
    deflater.hashed_through = deflater.hashed_through + 1
  }
}

///|
fn Deflater::record_chained_positions(
  deflater : Deflater,
  processed : Int32,
) -> Unit {
  let chained_end = processed.min((deflater.pending_end - 3).max(0))
  if chained_end > deflater.hashed_through {
    deflater.hashed_through = chained_end
  }
}

///|
// Unlike level 0, a heuristically stored block must remain available as
// history if a later block resumes normal matching. Keep both hash tables
// current without spending time on token search or Huffman construction.
fn Deflater::try_fast_store(
  deflater : Deflater,
  end : Int32,
  is_final~ : Bool,
) -> Bool {
  guard deflater.fast_store &&
    should_fast_store_window(deflater.pending, deflater.parse_pos, end) else {
    return false
  }
  deflater.chain_ready_history()
  deflater.w.write_stored_window(
    deflater.pending,
    deflater.parse_pos,
    end,
    is_final~,
  )
  deflater.finder.insert_window_positions(
    deflater.pending,
    deflater.parse_pos,
    end,
    deflater.pending_end,
  )
  deflater.parse_pos = end
  deflater.record_chained_positions(end)
  deflater.maybe_rebase()
  true
}

///|
/// Emit one non-final parsed block. `pending` already includes the lookahead
/// tail, and `tokenize_window` only commits `DEFLATE_BLOCK_SIZE` bytes except
/// for an ordinary 258-byte match overhang.
fn Deflater::emit_capped(deflater : Deflater) -> Unit {
  let target = (deflater.parse_pos + DEFLATE_BLOCK_SIZE).min(
    deflater.pending_end,
  )
  if deflater.try_fast_store(target, is_final=false) {
    return
  }
  deflater.tokens.clear()
  deflater.ll_freq.fill(0)
  deflater.d_freq.fill(0)
  deflater.chain_ready_history()
  let processed = tokenize_window(
    deflater.pending,
    deflater.finder,
    deflater.parse_pos,
    target,
    deflater.pending_end,
    deflater.cfg,
    deflater.tokens,
    deflater.ll_freq,
    deflater.d_freq,
  )
  deflater.w.emit_window_block(
    deflater.pending,
    deflater.parse_pos,
    processed,
    deflater.tokens,
    deflater.ll_freq,
    deflater.d_freq,
    is_final=false,
  )
  deflater.parse_pos = processed
  deflater.record_chained_positions(processed)
  deflater.maybe_rebase()
}

///|
/// Level 0 bypasses parsing entirely and writes buffered bytes straight out of
/// the ring. With no matches, neither matchfinder nor history needs updates.
fn Deflater::emit_stored_all(deflater : Deflater, is_final~ : Bool) -> Unit {
  deflater.write_stored_pending(is_final~)
  deflater.parse_pos = 0
  deflater.pending_end = 0
}

///|
fn Deflater::emit_one(deflater : Deflater) -> Unit {
  guard deflater.level != 0 else {
    deflater.emit_stored_all(is_final=false)
    return
  }
  deflater.emit_capped()
}

///|
/// Tokenize all remaining input into one trailing block. The end bound removes
/// the non-final lookahead restriction while preserving the same parse policy.
fn Deflater::emit_trailing_block(
  deflater : Deflater,
  is_final~ : Bool,
) -> Int32 {
  let end = deflater.pending_end
  if deflater.try_fast_store(end, is_final~) {
    return end
  }
  deflater.tokens.clear()
  deflater.ll_freq.fill(0)
  deflater.d_freq.fill(0)
  deflater.chain_ready_history()
  let processed = tokenize_window(
    deflater.pending,
    deflater.finder,
    deflater.parse_pos,
    deflater.pending_end,
    deflater.pending_end,
    deflater.cfg,
    deflater.tokens,
    deflater.ll_freq,
    deflater.d_freq,
  )
  deflater.w.emit_window_block(
    deflater.pending,
    deflater.parse_pos,
    processed,
    deflater.tokens,
    deflater.ll_freq,
    deflater.d_freq,
    is_final~,
  )
  deflater.parse_pos = processed
  deflater.record_chained_positions(processed)
  deflater.maybe_rebase()
  processed
}

///|
/// Keep absolute positions comfortably below backend integer limits. This is
/// amortized over 1 GiB of source and preserves every live 32 KiB chain link.
fn Deflater::maybe_rebase(deflater : Deflater) -> Unit {
  let rebase_at = 1 << 30
  guard deflater.parse_pos >= rebase_at else { return }
  let offset = (deflater.parse_pos / STREAM_RING_SIZE - 1) * STREAM_RING_SIZE
  deflater.finder.rebase(offset)
  deflater.parse_pos = deflater.parse_pos - offset
  deflater.pending_end = deflater.pending_end - offset
  deflater.hashed_through = deflater.hashed_through - offset
}

///|
fn Deflater::emit_final(deflater : Deflater) -> Unit {
  if deflater.level == 0 {
    deflater.emit_stored_all(is_final=true)
  } else {
    while deflater.pending_bytes() > DEFLATE_BLOCK_SIZE {
      deflater.emit_capped()
    }
    ignore(deflater.emit_trailing_block(is_final=true))
  }
  deflater.w.flush()
  deflater.emitted_final = true
}

///|
fn Deflater::flush_stored(deflater : Deflater) -> Unit {
  if deflater.pending_bytes() > 0 {
    deflater.emit_stored_all(is_final=false)
  }
}

///|
fn Deflater::flush_capped(deflater : Deflater) -> Unit {
  while deflater.pending_bytes() > DEFLATE_BLOCK_SIZE {
    deflater.emit_capped()
  }
  if deflater.pending_bytes() > 0 {
    ignore(deflater.emit_trailing_block(is_final=false))
  }
}

///|
/// Sync flush (zlib `Z_SYNC_FLUSH`): drain all buffered bytes, then append an
/// empty byte-aligned stored block. The persistent matchfinder retains the
/// preceding window for the stream's next input.
fn Deflater::emit_flush(deflater : Deflater) -> Unit {
  if deflater.level == 0 {
    deflater.flush_stored()
  } else {
    deflater.flush_capped()
  }
  deflater.w.write_stored(b"", 0, 0, is_final=false)
}