///| Row-group statistics are exposed separately from stream positions. This API

///|
/// does not promise row-group seek or selective decompression within a stripe.
pub(all) struct RowGroupInfo {
  row_start : Int32
  row_count : Int32
  positions : Array[UInt64]
  statistics : Statistics
} derive(Debug, Eq)

///|
pub extend RowGroupInfo with @debug.Debug::{to_repr}

///|
pub extend RowGroupInfo with Eq::{equal, not_equal}

///|
pub fn OrcFile::row_groups(
  self : OrcFile,
  stripe_index : Int32,
  column : String,
) -> Array[RowGroupInfo] raise OrcError {
  let stripe = self.stripe(stripe_index)
  let index = self.schema.index(column)
  let stride = host(number(parse_proto(self.footer, self.limits), 8, 0UL))
  if stride == 0 {
    return []
  }
  guard self.stream(stripe, index + 1, 6) is Some(raw) else { return [] }
  let entries = blobs(parse_proto(raw, self.limits), 1)
  let expected = stripe.info.rows / stride +
    (if stripe.info.rows % stride == 0 { 0 } else { 1 })
  if entries.length() != expected {
    raise Invalid("row-index group count", stripe_index)
  }
  let groups : Array[RowGroupInfo] = []
  for i = 0; i < entries.length(); i = i + 1 {
    let fields = parse_proto(entries[i], self.limits)
    let positions : Array[UInt64] = []
    for field in fields {
      if field.id == 1 {
        match field.wire {
          Var(n) => positions.push(n)
          Data(raw) => {
            let c = input(raw)
            while c.pos < c.end {
              positions.push(c.varint())
            }
          }
          _ => raise Invalid("row-index position wire type", 0)
        }
      }
    }
    let start = mul(i, stride)
    let count = imin(stride, stripe.info.rows - start)
    let statistics = decode_statistics(
      blob(fields, 2, b""),
      self.schema.columns[index].kind,
      count,
      self.limits,
    )
    groups.push({ row_start: start, row_count: count, positions, statistics, })
  }
  groups
}