///|
/// Stable read view over one immutable Segment set.
///
/// Searchers keep the exact Segment array and tombstones captured by their
/// IndexReader generation. Later commits cannot mutate this view.
pub struct Searcher {
  segments : ReadOnlyArray[SnapshotSegment]
  statistics : SearchStatistics
}

///|
/// Creates a Searcher for one in-memory Segment.
pub fn Searcher::new(segment : Segment) -> Searcher {
  Searcher::from_segments(ReadOnlyArray::from_array([live_snapshot(segment)]))
}

///|
pub fn Searcher::from_segments(
  segments : ReadOnlyArray[SnapshotSegment],
) -> Searcher {
  { segments, statistics: SearchStatistics::from_segments(segments) }
}

///|
pub fn Searcher::segment_count(self : Searcher) -> Int {
  self.segments.length()
}

///|
/// Returns the number of live documents visible in this Searcher snapshot.
pub fn Searcher::doc_count(self : Searcher) -> Int {
  self.statistics.doc_count()
}

///|
/// Builds one Weight from snapshot-wide statistics, runs a Scorer against
/// every Segment, then performs one global Top-K selection.
pub fn Searcher::search(
  self : Searcher,
  query : &Query,
  collector : TopKCollector,
) -> ReadOnlyArray[SearchHit] {
  let weight = query.weight(self.statistics)
  let heap = TopKHeap::new(collector.limit)
  for segment_ord in 0.. Array[SearchHit] {
  let hits : Array[SearchHit] = []
  let weight = query.weight(self.statistics)
  for segment_ord in 0.. Int {
  if collector.enabled {
    self.all_hits(query).length()
  } else {
    0
  }
}

///|
pub fn Searcher::top_docs(
  self : Searcher,
  query : &Query,
  collector : TopDocsCollector,
) -> ReadOnlyArray[SearchHit] {
  self.search(query, TopKCollector::new(collector.limit))
}

///|
pub fn Searcher::collect(
  self : Searcher,
  query : &Query,
  collector : MultiCollector,
) -> MultiCollectorResult {
  let weight = query.weight(self.statistics)
  let heap = TopKHeap::new(collector.top_docs_limit)
  let mut count = 0
  for segment_ord in 0.. FieldValue? {
  if values.length() == 0 {
    return None
  }
  let mut selected = values[0]
  for index in 1..
        if (order == Ascending && comparison < 0) ||
          (order == Descending && comparison > 0) {
          selected = values[index]
        }
      None => ()
    }
  }
  Some(selected)
}

///|
fn Searcher::compare_sorted_hits(
  self : Searcher,
  left : SearchHit,
  right : SearchHit,
  sort : Sort,
) -> Int {
  let left_segment = self.segments[left.address.segment_ord].segment
  let right_segment = self.segments[right.address.segment_ord].segment
  for field in sort.fields {
    let left_value = representative_sort_value(
      left_segment.fast_values(field.field_id, left.address.doc_id),
      field.order,
    )
    let right_value = representative_sort_value(
      right_segment.fast_values(field.field_id, right.address.doc_id),
      field.order,
    )
    let comparison = match (left_value, right_value) {
      (None, None) => 0
      (None, Some(_)) => if field.missing == MissingFirst { -1 } else { 1 }
      (Some(_), None) => if field.missing == MissingFirst { 1 } else { -1 }
      (Some(left_value), Some(right_value)) =>
        match compare_field_values(left_value, right_value) {
          Some(comparison) =>
            if field.order == Descending {
              -comparison
            } else {
              comparison
            }
          None => 0
        }
    }
    if comparison != 0 {
      return comparison
    }
  }
  if left.address.segment_ord != right.address.segment_ord {
    left.address.segment_ord.compare(right.address.segment_ord)
  } else {
    left.address.doc_id.value.compare(right.address.doc_id.value)
  }
}

///|
pub fn Searcher::search_sorted(
  self : Searcher,
  query : &Query,
  sort : Sort,
  limit : Int,
) -> ReadOnlyArray[SearchHit] {
  if limit <= 0 {
    return []
  }
  let hits = self.all_hits(query)
  hits.sort_by((left, right) => self.compare_sorted_hits(left, right, sort))
  let result : Array[SearchHit] = []
  let count = if hits.length() < limit { hits.length() } else { limit }
  for index in 0.. StoredDocument? {
  if address.segment_ord < 0 || address.segment_ord >= self.segments.length() {
    None
  } else {
    let snapshot = self.segments[address.segment_ord]
    if snapshot.is_deleted(address.doc_id) {
      None
    } else {
      snapshot.segment.doc(address.doc_id)
    }
  }
}