///|
/// Core structure representing a single-channel seismic trace.
pub struct Trace {
  station : String
  channel : String
  sampling_rate : Double
  start_time : Double
  data : Array[Double]
}

///|
/// Create a new Trace.
pub fn Trace::new(
  station : String,
  channel : String,
  sampling_rate : Double,
  start_time : Double,
  data : Array[Double],
) -> Trace {
  { station, channel, sampling_rate, start_time, data }
}

///|
/// Get the sampling interval in seconds (delta = 1.0 / sampling_rate).
pub fn Trace::delta(self : Trace) -> Double {
  if self.sampling_rate <= 0.0 {
    0.0
  } else {
    1.0 / self.sampling_rate
  }
}

///|
/// Get the number of data points in the trace.
pub fn Trace::npts(self : Trace) -> Int {
  self.data.length()
}

///|
/// Get the duration of the trace in seconds.
pub fn Trace::duration(self : Trace) -> Double {
  let n = self.npts()
  if n <= 1 {
    0.0
  } else {
    (n - 1).to_double() * self.delta()
  }
}

///|
/// Return the time array corresponding to each sample in the trace.
pub fn Trace::times(self : Trace) -> Array[Double] {
  let n = self.npts()
  let dt = self.delta()
  let t = Array::new(capacity=n)
  for i = 0; i < n; i = i + 1 {
    t.push(self.start_time + i.to_double() * dt)
  }
  t
}

///|
/// Deep clone the Trace.
pub fn Trace::clone(self : Trace) -> Trace {
  let cloned_data = Array::new(capacity=self.data.length())
  for v in self.data {
    cloned_data.push(v)
  }
  {
    station: self.station,
    channel: self.channel,
    sampling_rate: self.sampling_rate,
    start_time: self.start_time,
    data: cloned_data,
  }
}

///|
/// Calculate the mean value of the trace data.
pub fn Trace::mean(self : Trace) -> Double {
  let n = self.npts()
  if n == 0 {
    return 0.0
  }
  let mut sum = 0.0
  for v in self.data {
    sum = sum + v
  }
  sum / n.to_double()
}

///|
/// Calculate the standard deviation of the trace data.
pub fn Trace::std(self : Trace) -> Double {
  let n = self.npts()
  if n <= 1 {
    return 0.0
  }
  let mu = self.mean()
  let mut sum_sq = 0.0
  for v in self.data {
    let diff = v - mu
    sum_sq = sum_sq + diff * diff
  }
  (sum_sq / (n - 1).to_double()).sqrt()
}

///|
/// Remove the mean value from the trace data in-place.
pub fn Trace::demean(self : Trace) -> Unit {
  let mu = self.mean()
  let n = self.npts()
  for i = 0; i < n; i = i + 1 {
    self.data[i] = self.data[i] - mu
  }
}

///|
/// Remove linear trend from the trace data in-place using least squares regression.
pub fn Trace::detrend(self : Trace) -> Unit {
  let n = self.npts()
  if n <= 1 {
    return
  }

  // Calculate means of x (sample indices) and y (sample values)
  let mut sum_y = 0.0
  for v in self.data {
    sum_y = sum_y + v
  }
  let mean_y = sum_y / n.to_double()
  let mean_x = (n - 1).to_double() / 2.0

  // Calculate covariance S_xy and variance S_xx
  let mut s_xy = 0.0
  let mut s_xx = 0.0
  for i = 0; i < n; i = i + 1 {
    let dx = i.to_double() - mean_x
    let dy = self.data[i] - mean_y
    s_xy = s_xy + dx * dy
    s_xx = s_xx + dx * dx
  }

  if s_xx == 0.0 {
    return
  }

  let slope = s_xy / s_xx
  let intercept = mean_y - slope * mean_x

  // Subtract the trend: y_i = y_i - (slope * i + intercept)
  for i = 0; i < n; i = i + 1 {
    self.data[i] = self.data[i] - (slope * i.to_double() + intercept)
  }
}