///|
/// Selection policy for exporting a review-ready event catalog.
pub struct EventSelectionPolicy {
  minimum_magnitude : Double
  minimum_confidence : Double
  minimum_stations : Int
  minimum_separation_seconds : Double
  maximum_depth_km : Double
}

///|
/// Return a practical production selection policy.
pub fn default_event_selection_policy() -> EventSelectionPolicy {
  {
    minimum_magnitude: 0.0,
    minimum_confidence: 0.5,
    minimum_stations: 2,
    minimum_separation_seconds: 0.0,
    maximum_depth_km: 700.0,
  }
}

///|
/// Filter catalog entries by quality and physical constraints.
pub fn select_catalog_entries(
  catalog : EventCatalog,
  policy : EventSelectionPolicy,
) -> Array[CatalogEntry] {
  let selected = catalog.entries.filter(entry => {
    entry.magnitude >= policy.minimum_magnitude &&
    entry.confidence >= policy.minimum_confidence &&
    entry.station_count >= policy.minimum_stations &&
    entry.depth_km <= policy.maximum_depth_km
  })
  if policy.minimum_separation_seconds <= 0.0 {
    return selected
  }
  let sorted = selected.copy()
  sorted.sort_by((left, right) => {
    if left.origin_time < right.origin_time {
      -1
    } else if left.origin_time > right.origin_time {
      1
    } else {
      0
    }
  })
  let result : Array[CatalogEntry] = Array::new()
  for entry in sorted {
    let separated = result.is_empty() ||
      (entry.origin_time - result[result.length() - 1].origin_time).abs() >=
      policy.minimum_separation_seconds
    if separated {
      result.push(entry)
    }
  }
  result
}

///|
/// Return the largest event in a selected catalog.
pub fn strongest_selected_entry(entries : Array[CatalogEntry]) -> CatalogEntry? {
  if entries.is_empty() {
    return None
  }
  let mut best = entries[0]
  for entry in entries {
    if entry.magnitude > best.magnitude {
      best = entry
    }
  }
  Some(best)
}

///|
/// Return an event density in entries per day.
pub fn selected_event_rate(entries : Array[CatalogEntry]) -> Double {
  if entries.length() < 2 {
    0.0
  } else {
    let times = entries.map(entry => entry.origin_time)
    let duration = maximum_sample(times) - minimum_sample(times)
    if duration <= 0.0 {
      0.0
    } else {
      entries.length().to_double() * 86400.0 / duration
    }
  }
}

///|
/// Format a selection policy.
pub fn event_selection_policy_text(policy : EventSelectionPolicy) -> String {
  "min_magnitude=" +
  policy.minimum_magnitude.to_string() +
  " min_confidence=" +
  policy.minimum_confidence.to_string() +
  " min_stations=" +
  policy.minimum_stations.to_string() +
  " min_separation=" +
  policy.minimum_separation_seconds.to_string() +
  " max_depth=" +
  policy.maximum_depth_km.to_string()
}