///|
/// A named SAC analysis section over a trace.
pub struct SacAnalysisSection {
  name : String
  trace : Trace
  begin_time : Double
  end_time : Double
  sample_count : Int
  rms : Double
  peak : Double
}

///|
/// Pick markers commonly stored in SAC event headers.
pub struct SacPickMarkers {
  origin : Double?
  p_arrival : Double?
  s_arrival : Double?
  coda_end : Double?
  user_markers : Array[Double]
}

///|
/// SAC-oriented metadata validation result.
pub struct SacMetadataDiagnostics {
  valid : Bool
  sample_rate : Double
  duration : Double
  station : String
  channel : String
  warnings : Array[String]
}

///|
/// Cut a trace into a named section and calculate section measures.
pub fn make_sac_section(
  name : String,
  trace : Trace,
  start_time : Double,
  end_time : Double,
) -> SacAnalysisSection {
  let section = clip_trace_time(trace, start_time, end_time)
  {
    name,
    trace: section,
    begin_time: section.start_time,
    end_time: section.start_time + section.duration(),
    sample_count: section.npts(),
    rms: rms_samples(section.data),
    peak: absolute_peak(section.data),
  }
}

///|
/// Split a trace into fixed physical-duration SAC sections.
pub fn split_sac_sections(
  trace : Trace,
  duration_seconds : Double,
  step_seconds : Double,
  prefix : String,
) -> Array[SacAnalysisSection] {
  let result = Array::new()
  if duration_seconds <= 0.0 ||
    step_seconds <= 0.0 ||
    trace.sampling_rate <= 0.0 {
    return result
  }
  let mut start = trace.start_time
  let end = trace.start_time + trace.duration()
  let mut sequence = 0
  while start < end {
    let finish = start + duration_seconds
    if finish > end {
      break
    }
    result.push(
      make_sac_section(prefix + sequence.to_string(), trace, start, finish),
    )
    sequence += 1
    start += step_seconds
  }
  result
}

///|
/// Merge contiguous SAC sections while checking metadata.
pub fn merge_sac_sections(
  sections : Array[SacAnalysisSection],
) -> Result[Trace, String] {
  if sections.is_empty() {
    return Err("cannot merge empty SAC sections")
  }
  let sorted = sections.copy()
  sorted.sort_by((left, right) => {
    if left.begin_time < right.begin_time {
      -1
    } else if left.begin_time > right.begin_time {
      1
    } else {
      0
    }
  })
  let mut trace = sorted[0].trace.clone()
  for index = 1; index < sorted.length(); index = index + 1 {
    let section = sorted[index]
    match concatenate_traces(trace, section.trace) {
      Ok(value) => trace = value
      Err(message) => return Err(message)
    }
  }
  Ok(trace)
}

///|
/// Validate trace values before SAC serialization.
pub fn validate_sac_metadata(trace : Trace) -> SacMetadataDiagnostics {
  let warnings : Array[String] = Array::new()
  if trace.station == "" {
    warnings.push("station is empty")
  }
  if trace.channel == "" {
    warnings.push("channel is empty")
  }
  if trace.sampling_rate <= 0.0 {
    warnings.push("sample rate is not positive")
  }
  if trace.npts() == 0 {
    warnings.push("trace has no samples")
  }
  {
    valid: warnings.is_empty(),
    sample_rate: trace.sampling_rate,
    duration: trace.duration(),
    station: trace.station,
    channel: trace.channel,
    warnings,
  }
}

///|
/// Create empty SAC markers.
pub fn empty_sac_pick_markers() -> SacPickMarkers {
  {
    origin: None,
    p_arrival: None,
    s_arrival: None,
    coda_end: None,
    user_markers: [],
  }
}

///|
/// Add a user marker while retaining immutable marker semantics.
pub fn add_sac_user_marker(
  markers : SacPickMarkers,
  marker : Double,
) -> SacPickMarkers {
  let user_markers = markers.user_markers.copy()
  user_markers.push(marker)
  {
    origin: markers.origin,
    p_arrival: markers.p_arrival,
    s_arrival: markers.s_arrival,
    coda_end: markers.coda_end,
    user_markers,
  }
}

///|
/// Compute P/S separation when both markers exist.
pub fn sac_phase_separation(markers : SacPickMarkers) -> Double? {
  match (markers.p_arrival, markers.s_arrival) {
    (Some(p), Some(s)) => Some(s - p)
    _ => None
  }
}

///|
/// Return a section CSV table for QC dashboards.
pub fn sac_sections_to_csv(sections : Array[SacAnalysisSection]) -> String {
  let mut output = "name,begin,end,samples,rms,peak\n"
  for section in sections {
    output = output +
      section.name +
      "," +
      section.begin_time.to_string() +
      "," +
      section.end_time.to_string() +
      "," +
      section.sample_count.to_string() +
      "," +
      section.rms.to_string() +
      "," +
      section.peak.to_string() +
      "\n"
  }
  output
}

///|
/// Format SAC metadata diagnostics.
pub fn sac_metadata_text(diagnostics : SacMetadataDiagnostics) -> String {
  "station=" +
  diagnostics.station +
  " channel=" +
  diagnostics.channel +
  " valid=" +
  diagnostics.valid.to_string() +
  " rate=" +
  diagnostics.sample_rate.to_string() +
  " duration=" +
  diagnostics.duration.to_string() +
  " warnings=" +
  diagnostics.warnings.length().to_string()
}