///|
let stored_field_block_size : Int = 32

///|
pub(all) struct StoredFieldBlock {
  first_doc : Int
  doc_count : Int
  uncompressed_length : Int
  compressed : Bytes
} derive(Eq, @debug.Debug)

///|
pub struct StoredFieldsReader {
  blocks : ReadOnlyArray[StoredFieldBlock]
  document_count : Int
}

///|
fn write_stored_value(buffer : @buffer.Buffer, value : FieldValue) -> Unit {
  match value {
    @core.Text(value) => {
      buffer.write_byte(b'\x00')
      write_string(buffer, value)
    }
    @core.Keyword(value) => {
      buffer.write_byte(b'\x01')
      write_string(buffer, value)
    }
    @core.I64(value) => {
      buffer.write_byte(b'\x02')
      write_int64(buffer, value)
    }
    @core.U64(value) => {
      buffer.write_byte(b'\x03')
      write_uint64(buffer, value)
    }
    @core.F64(value) => {
      buffer.write_byte(b'\x04')
      write_double(buffer, value)
    }
    @core.Bool(value) => {
      buffer.write_byte(b'\x05')
      write_bool(buffer, value)
    }
    @core.Date(value) => {
      buffer.write_byte(b'\x06')
      write_int64(buffer, value)
    }
    @core.Bytes(value) => {
      buffer.write_byte(b'\x07')
      buffer.write_int_be(value.length())
      buffer.write_bytes(value[:])
    }
  }
}

///|
fn read_stored_value(
  decoder : SegmentDecoder,
) -> FieldValue raise PersistenceError {
  match decoder.read_byte("stored value type") {
    b'\x00' => @core.Text(decoder.read_string("stored text value"))
    b'\x01' => @core.Keyword(decoder.read_string("stored keyword value"))
    b'\x02' => @core.I64(decoder.read_int64("stored i64 value"))
    b'\x03' => @core.U64(decoder.read_uint64("stored u64 value"))
    b'\x04' => @core.F64(decoder.read_double("stored f64 value"))
    b'\x05' => @core.Bool(decoder.read_bool("stored bool value"))
    b'\x06' => @core.Date(decoder.read_int64("stored date value"))
    b'\x07' => @core.Bytes(decoder.read_bytes("stored bytes value"))
    _ => raise PersistenceError::InvalidFormat("invalid stored value type")
  }
}

///|
fn encode_stored_documents(documents : Array[StoredDocument]) -> Bytes {
  let buffer = @buffer.Buffer()
  buffer.write_int_be(documents.length())
  for document in documents {
    buffer.write_int_be(document.field_ids.length())
    for index in 0.. Bytes {
  let output = @buffer.Buffer(size_hint=input.length())
  let mut cursor = 0
  while cursor < input.length() {
    let mut run_length = 1
    while cursor + run_length < input.length() &&
          input[cursor + run_length] == input[cursor] &&
          run_length < 128 {
      run_length += 1
    }
    if run_length >= 4 {
      output.write_byte((127 + run_length).to_byte())
      output.write_byte(input[cursor])
      cursor += run_length
      continue
    }
    let literal_start = cursor
    cursor += run_length
    while cursor < input.length() && cursor - literal_start < 128 {
      let mut next_run = 1
      while cursor + next_run < input.length() &&
            input[cursor + next_run] == input[cursor] &&
            next_run < 128 {
        next_run += 1
      }
      if next_run >= 4 {
        break
      }
      cursor += next_run
    }
    let literal_length = cursor - literal_start
    output.write_byte((literal_length - 1).to_byte())
    output.write_bytes(input[literal_start:cursor])
  }
  output.to_bytes()
}

///|
fn rle_decompress(
  input : Bytes,
  expected_length : Int,
) -> Bytes raise PersistenceError {
  let output = @buffer.Buffer(size_hint=expected_length)
  let mut cursor = 0
  while cursor < input.length() {
    let control = input[cursor].to_int()
    cursor += 1
    if control < 128 {
      let length = control + 1
      guard cursor + length <= input.length() else {
        raise PersistenceError::InvalidFormat("truncated stored literal")
      }
      output.write_bytes(input[cursor:cursor + length])
      cursor += length
    } else {
      let length = control - 127
      guard cursor < input.length() else {
        raise PersistenceError::InvalidFormat("truncated stored run")
      }
      let value = input[cursor]
      cursor += 1
      for _ in 0.. ReadOnlyArray[StoredDocument] raise PersistenceError {
  let decoder = SegmentDecoder::new(bytes)
  let count = decoder.read_count("stored block document count")
  guard count == expected_count else {
    raise PersistenceError::InvalidFormat(
      "stored block document count mismatch",
    )
  }
  let documents : Array[StoredDocument] = []
  for _ in 0.. ReadOnlyArray[StoredDocument] raise PersistenceError {
  decode_stored_documents(
    rle_decompress(block.compressed, block.uncompressed_length),
    block.doc_count,
  )
}

///|
fn build_stored_field_blocks(
  documents : Array[StoredDocument],
) -> ReadOnlyArray[StoredFieldBlock] {
  let blocks : Array[StoredFieldBlock] = []
  let mut start = 0
  while start < documents.length() {
    let end = if start + stored_field_block_size < documents.length() {
      start + stored_field_block_size
    } else {
      documents.length()
    }
    let block_documents : Array[StoredDocument] = []
    for index in start.. Unit raise PersistenceError {
  let mut next_doc = 0
  for block in blocks {
    guard block.first_doc == next_doc && block.doc_count > 0 else {
      raise PersistenceError::InvalidFormat("invalid stored block range")
    }
    ignore(stored_block_documents(block))
    next_doc += block.doc_count
  }
  guard next_doc == document_count else {
    raise PersistenceError::InvalidFormat("stored block coverage mismatch")
  }
}

///|
fn StoredFieldsReader::new(
  blocks : ReadOnlyArray[StoredFieldBlock],
  document_count : Int,
) -> StoredFieldsReader {
  { blocks, document_count }
}

///|
pub fn StoredFieldsReader::doc(
  self : StoredFieldsReader,
  doc_id : DocId,
) -> StoredDocument? {
  if doc_id.value < 0 || doc_id.value >= self.document_count {
    return None
  }
  let block_index = doc_id.value / stored_field_block_size
  guard block_index >= 0 && block_index < self.blocks.length() else {
    return None
  }
  let block = self.blocks[block_index]
  let documents = stored_block_documents(block) catch {
    error => abort(error.to_string())
  }
  Some(documents[doc_id.value - block.first_doc])
}

///|
pub fn StoredFieldsReader::compressed_bytes(self : StoredFieldsReader) -> Int {
  let mut total = 0
  for block in self.blocks {
    total += block.compressed.length()
  }
  total
}

///|
pub fn StoredFieldsReader::block_count(self : StoredFieldsReader) -> Int {
  self.blocks.length()
}