///|
/// Return a stream dictionary with xref-stream-only keys removed.
pub fn pdf_reader_stream_dictionary_without_xref_keys(
  dictionary : @syntax.PdfObject,
) -> @syntax.PdfObject raise @core.PdfError {
  dictionary
  .remove_dict_entry(pdf_reader_type_name())
  .remove_dict_entry(pdf_reader_w_name())
  .remove_dict_entry(pdf_reader_index_name())
  .remove_dict_entry(pdf_length_name())
  .remove_dict_entry(pdf_reader_filter_name())
  .remove_dict_entry(pdf_reader_short_filter_name())
  .remove_dict_entry(pdf_reader_decodeparms_name())
  .remove_dict_entry(pdf_reader_short_decodeparms_name())
}

///|
/// Return whether a dictionary declares an xref stream.
pub fn pdf_reader_xref_stream_type_ok(dictionary : @syntax.PdfObject) -> Bool {
  match dictionary.lookup_immediate(pdf_reader_type_name()) {
    Some(PdfNameObject(name)) => name == pdf_reader_xref_name()
    Some(_) => false
    None => dictionary.lookup_immediate(pdf_reader_w_name()) != None
  }
}

///|
/// Return whether an object is a stream object with xref-stream metadata.
pub fn pdf_reader_object_is_xref_stream(object : @syntax.PdfObject) -> Bool {
  match object {
    PdfStreamObject(stream) => pdf_reader_xref_stream_type_ok(stream.dictionary)
    _ => false
  }
}

///|
/// Read an optional integer array from a direct dictionary entry.
pub fn pdf_xref_stream_int_array(
  dictionary : @syntax.PdfObject,
  key : @core.PdfName,
) -> Array[Int]? raise @core.PdfError {
  match dictionary.lookup_immediate(key) {
    Some(PdfArray(values)) => {
      let output : Array[Int] = Array(capacity=values.length())
      for value in values {
        match value {
          PdfInteger(number) => output.push(number)
          _ => raise XRefEntryExpected
        }
      }
      Some(output)
    }
    Some(_) => raise XRefEntryExpected
    None => None
  }
}

///|
/// Read and validate xref-stream `/W` field widths.
pub fn pdf_xref_stream_widths(
  dictionary : @syntax.PdfObject,
) -> (Int, Int, Int) raise @core.PdfError {
  match pdf_xref_stream_int_array(dictionary, pdf_reader_w_name()) {
    Some([w0, w1, w2]) if w0 >= 0 && w1 >= 0 && w2 >= 0 && w0 + w1 + w2 > 0 =>
      (w0, w1, w2)
    _ => raise XRefEntryExpected
  }
}

///|
/// Read and validate xref-stream `/Index` object-number ranges.
pub fn pdf_xref_stream_index_pairs(
  dictionary : @syntax.PdfObject,
) -> Array[(Int, Int)] raise @core.PdfError {
  match pdf_xref_stream_int_array(dictionary, pdf_reader_index_name()) {
    Some(values) => {
      if values.length() % 2 != 0 {
        raise XRefEntryExpected
      }
      let pairs : Array[(Int, Int)] = Array(capacity=values.length() / 2)
      for pair_index in 0..<(values.length() / 2) {
        let i = pair_index * 2
        let start = values[i]
        let length = values[i + 1]
        if start < 0 || length < 0 {
          raise XRefEntryExpected
        }
        pairs.push((start, length))
      }
      pairs
    }
    None =>
      match dictionary.lookup_immediate(pdf_reader_size_name()) {
        Some(PdfInteger(size)) if size >= 0 => [(0, size)]
        _ => raise XRefEntryExpected
      }
  }
}