///|
/// A channel waveform assembled from time-ordered records.
pub(all) struct Trace {
  sid : String
  start : @time.Time
  end : @time.Time
  sample_rate : Double
  samples : Array[Double]
  records : Array[@record.Record]
} derive(Eq, Debug)

///|
pub fn Trace::sid(self : Trace) -> String {
  self.sid
}

///|
pub fn Trace::start_time(self : Trace) -> @time.Time {
  self.start
}

///|
pub fn Trace::end_time(self : Trace) -> @time.Time {
  self.end
}

///|
pub fn Trace::sample_rate(self : Trace) -> Double {
  self.sample_rate
}

///|
pub fn Trace::samples(self : Trace) -> Array[Double] {
  self.samples
}

///|
pub fn Trace::records(self : Trace) -> Array[@record.Record] {
  self.records
}

///|
pub fn Trace::sample_count(self : Trace) -> Int {
  self.samples.length()
}

///|
pub fn Trace::duration_secs(self : Trace) -> Double {
  self.end.diff_secs(self.start)
}

///|
/// Group numeric records by SID and order each group chronologically.
pub fn build_traces(records : Array[@record.Record]) -> Array[Trace] {
  let sids : Array[String] = []
  for record in records {
    if !sids.contains(record.sid()) {
      sids.push(record.sid())
    }
  }
  sort_strings(sids)
  let out : Array[Trace] = []
  for sid in sids {
    let group : Array[@record.Record] = []
    for record in records {
      if record.sid() == sid {
        group.push(record)
      }
    }
    sort_records(group)
    match build_trace(sid, group) {
      Some(trace) => out.push(trace)
      None => ()
    }
  }
  out
}

///|
fn build_trace(sid : String, group : Array[@record.Record]) -> Trace? {
  if group.is_empty() {
    return None
  }
  let samples : Array[Double] = []
  let start = group[0].start_time()
  let mut end = start
  let mut rate = 0.0
  for record in group {
    if rate == 0.0 && record.header().rate_hz() > 0.0 {
      rate = record.header().rate_hz()
    }
    for value in record.samples() {
      samples.push(value)
    }
    match record.end_time() {
      Some(record_end) =>
        if record_end.to_epoch_ns() > end.to_epoch_ns() {
          end = record_end
        }
      None => ()
    }
  }
  Some({ sid, start, end, sample_rate: rate, samples, records: group })
}

///|
fn sort_records(records : Array[@record.Record]) -> Unit {
  let mut i = 1
  while i < records.length() {
    let current = records[i]
    let mut j = i
    while j > 0 &&
          records[j - 1].start_time().to_epoch_ns() >
          current.start_time().to_epoch_ns() {
      records[j] = records[j - 1]
      j = j - 1
    }
    records[j] = current
    i = i + 1
  }
}

///|
fn sort_strings(values : Array[String]) -> Unit {
  let mut i = 1
  while i < values.length() {
    let current = values[i]
    let mut j = i
    while j > 0 && values[j - 1].lexical_compare(current) > 0 {
      values[j] = values[j - 1]
      j = j - 1
    }
    values[j] = current
    i = i + 1
  }
}