///|
/// Production track-window utilities for temporal filtering and keyframe selection.
pub(all) struct TrackWindowSummary {
  observations : Int
  views : Int
  tracks : Int
  mean_motion : Double
  maximum_motion : Double
  temporal_span : Int
} derive(Debug, Eq)

///|
pub fn track_window_summary(
  observations : ArrayView[TrackObservation],
) -> TrackWindowSummary {
  let ids = track_view_ids(observations)
  let tracks = track_analytics(observations)
  let mut minimum = 0
  let mut maximum = 0
  let mut initialized = false
  for observation in observations {
    if !initialized {
      minimum = observation.view_id
      maximum = observation.view_id
      initialized = true
    } else {
      minimum = minimum.min(observation.view_id)
      maximum = maximum.max(observation.view_id)
    }
  }
  {
    observations: observations.length(),
    views: ids.length(),
    tracks: tracks.track_count,
    mean_motion: track_mean_motion(observations),
    maximum_motion: track_max_motion(observations),
    temporal_span: if initialized {
      maximum - minimum
    } else {
      0
    },
  }
}

///|
pub fn track_window_sort_by_view(
  observations : ArrayView[TrackObservation],
) -> Array[TrackObservation] {
  let result = observations.to_owned()
  result.sort_by(fn(left, right) {
    if left.view_id == right.view_id {
      left.point_id.compare(right.point_id)
    } else {
      left.view_id.compare(right.view_id)
    }
  })
  result
}

///|
pub fn track_window_sort_by_point(
  observations : ArrayView[TrackObservation],
) -> Array[TrackObservation] {
  let result = observations.to_owned()
  result.sort_by(fn(left, right) {
    if left.point_id == right.point_id {
      left.view_id.compare(right.view_id)
    } else {
      left.point_id.compare(right.point_id)
    }
  })
  result
}

///|
pub fn track_window_limit_views(
  observations : ArrayView[TrackObservation],
  first_view : Int,
  last_view : Int,
) -> Array[TrackObservation] {
  let result : Array[TrackObservation] = []
  for observation in observations {
    if observation.view_id >= first_view && observation.view_id <= last_view {
      result.push(observation)
    }
  }
  result
}

///|
pub fn track_window_limit_points(
  observations : ArrayView[TrackObservation],
  first_point : Int,
  last_point : Int,
) -> Array[TrackObservation] {
  let result : Array[TrackObservation] = []
  for observation in observations {
    if observation.point_id >= first_point && observation.point_id <= last_point {
      result.push(observation)
    }
  }
  result
}

///|
pub fn track_window_group_by_view(
  observations : ArrayView[TrackObservation],
  view_id : Int,
) -> Array[TrackObservation] {
  let result : Array[TrackObservation] = []
  for observation in observations {
    if observation.view_id == view_id {
      result.push(observation)
    }
  }
  result
}

///|
pub fn track_window_group_by_point(
  observations : ArrayView[TrackObservation],
  point_id : Int,
) -> Array[TrackObservation] {
  track_observations_for(observations, point_id)
}

///|
pub fn track_window_view_counts(
  observations : ArrayView[TrackObservation],
) -> Array[Int] {
  let views = track_view_ids(observations)
  let result : Array[Int] = []
  for view in views {
    result.push(track_window_group_by_view(observations, view).length())
  }
  result
}

///|
pub fn track_window_point_counts(
  observations : ArrayView[TrackObservation],
) -> Array[Int] {
  let ids : Array[Int] = []
  let result : Array[Int] = []
  for observation in observations {
    if !ids.contains(observation.point_id) {
      ids.push(observation.point_id)
    }
  }
  for id in ids {
    result.push(track_window_group_by_point(observations, id).length())
  }
  result
}

///|
pub fn track_window_view_balance(
  observations : ArrayView[TrackObservation],
) -> Double {
  let counts = track_window_view_counts(observations)
  if counts.length() == 0 {
    return 0.0
  }
  let stats = @core.streaming_summary(
    counts.map(fn(value) { value.to_double() }),
  )
  if stats.mean <= 0.0 {
    0.0
  } else {
    1.0 / (1.0 + stats.variance.sqrt() / stats.mean)
  }
}

///|
pub fn track_window_consistent_motion(
  observations : ArrayView[TrackObservation],
  tolerance : Double,
) -> Bool {
  let sorted = track_window_sort_by_point(observations)
  let mut previous_point = -1
  let mut previous_view = -1
  let mut previous_pixel = @core.Point2::new(x=0.0, y=0.0)
  let mut initialized = false
  for observation in sorted {
    if initialized &&
      observation.point_id == previous_point &&
      observation.view_id == previous_view + 1 {
      if observation.pixel.minus(previous_pixel).norm() > tolerance.max(0.0) {
        return false
      }
    }
    previous_point = observation.point_id
    previous_view = observation.view_id
    previous_pixel = observation.pixel
    initialized = true
  }
  true
}

///|
pub fn track_window_interpolate(
  a : TrackObservation,
  b : TrackObservation,
  view_id : Int,
) -> TrackObservation {
  let denominator = (b.view_id - a.view_id).to_double()
  let amount = if denominator == 0.0 {
    0.0
  } else {
    (view_id - a.view_id).to_double() / denominator
  }
  TrackObservation::new(
    point_id=a.point_id,
    view_id~,
    pixel=@core.Point2::new(
      x=a.pixel.x + (b.pixel.x - a.pixel.x) * amount,
      y=a.pixel.y + (b.pixel.y - a.pixel.y) * amount,
    ),
  )
}

///|
pub fn track_window_interpolate_gap(
  observations : ArrayView[TrackObservation],
  point_id : Int,
  start_view : Int,
  end_view : Int,
) -> Array[TrackObservation] {
  let group = track_window_sort_by_view(
    track_window_group_by_point(observations, point_id),
  )
  let result : Array[TrackObservation] = []
  if group.length() == 0 {
    return result
  }
  let mut left = group[0]
  for right in group[1:] {
    let begin = left.view_id.max(start_view)
    let end = right.view_id.min(end_view)
    if right.view_id - left.view_id > 1 {
      for view in (begin + 1).. Unit {
  for observation in observations {
    let mut found = false
    for existing in target {
      if existing.point_id == observation.point_id &&
        existing.view_id == observation.view_id {
        found = true
      }
    }
    if !found {
      target.push(observation)
    }
  }
}

///|
pub fn track_window_deduplicate(
  observations : ArrayView[TrackObservation],
) -> Array[TrackObservation] {
  let result : Array[TrackObservation] = []
  track_window_append_unique(result, observations)
  result
}

///|
pub fn track_window_retain_best(
  observations : ArrayView[TrackObservation],
  maximum_per_point : Int,
) -> Array[TrackObservation] {
  let result : Array[TrackObservation] = []
  let ids : Array[Int] = []
  for observation in observations {
    if !ids.contains(observation.point_id) {
      ids.push(observation.point_id)
    }
  }
  for id in ids {
    let group = track_window_sort_by_view(
      track_window_group_by_point(observations, id),
    )
    let limit = maximum_per_point.max(0).min(group.length())
    for i in 0.. Double {
  let summary = track_window_summary(observations)
  let support = if minimum_tracks <= 0 {
    1.0
  } else {
    (summary.tracks.to_double() / minimum_tracks.to_double()).min(1.0)
  }
  support *
  track_window_view_balance(observations) *
  (1.0 / (1.0 + summary.mean_motion))
}

///|
pub fn track_window_select_keyframes(
  observations : ArrayView[TrackObservation],
  minimum_tracks : Int,
  threshold : Double,
) -> Array[Int] {
  let views = track_view_ids(observations)
  let result : Array[Int] = []
  for view in views {
    if track_window_keyframe_score(
        track_window_group_by_view(observations, view),
        minimum_tracks,
      ) >=
      threshold {
      result.push(view)
    }
  }
  result
}

///|
pub fn track_window_temporal_gaps(
  observations : ArrayView[TrackObservation],
) -> Array[Int] {
  let views = track_view_ids(observations)
  let sorted = views
  sorted.sort()
  let result : Array[Int] = []
  for i in 0..<(sorted.length() - 1).max(0) {
    let gap = sorted[i + 1] - sorted[i] - 1
    if gap > 0 {
      result.push(gap)
    }
  }
  result
}

///|
pub fn track_window_gap_penalty(
  observations : ArrayView[TrackObservation],
) -> Double {
  let gaps = track_window_temporal_gaps(observations)
  let mut total = 0
  for gap in gaps {
    total += gap
  }
  total.to_double()
}

///|
pub fn track_window_reliability(
  observations : ArrayView[TrackObservation],
  minimum_views : Int,
  maximum_motion : Double,
) -> Double {
  let summary = track_window_summary(observations)
  let support = if minimum_views <= 0 {
    1.0
  } else {
    (summary.views.to_double() / minimum_views.to_double()).min(1.0)
  }
  let motion = if maximum_motion <= 0.0 {
    0.0
  } else {
    (1.0 - summary.mean_motion / maximum_motion).max(0.0).min(1.0)
  }
  support * motion * (1.0 / (1.0 + track_window_gap_penalty(observations)))
}