///|
/// SAC byte-order marker.
pub enum SacEndian {
  SacLittleEndian
  SacBigEndian
}

///|
/// Return the little-endian marker.
pub fn sac_little_endian() -> SacEndian {
  SacLittleEndian
}

///|
/// Return the big-endian marker.
pub fn sac_big_endian() -> SacEndian {
  SacBigEndian
}

///|
/// Parsed SAC header fields needed by downstream processing.
pub struct SacHeaderInfo {
  endian : SacEndian
  version : Int
  sample_count : Int
  delta : Double
  begin : Double
  station : String
  channel : String
  header_size : Int
}

///|
/// Detect SAC byte order from the NVHDR field.
pub fn detect_sac_endian(bytes : Bytes) -> Result[SacEndian, String] {
  if bytes.length() < 632 {
    return Err("SAC header requires at least 632 bytes")
  }
  let little = read_int32(bytes, 304, false)
  if little == 6 {
    return Ok(SacLittleEndian)
  }
  let big = read_int32(bytes, 304, true)
  if big == 6 {
    Ok(SacBigEndian)
  } else {
    Err("SAC NVHDR is not a supported version")
  }
}

///|
/// Extract a validated SAC header.
pub fn parse_sac_header(bytes : Bytes) -> Result[SacHeaderInfo, String] {
  let endian = match detect_sac_endian(bytes) {
    Ok(value) => value
    Err(message) => return Err(message)
  }
  let is_big = endian is SacBigEndian
  let version = read_int32(bytes, 304, is_big)
  let sample_count = read_int32(bytes, 316, is_big)
  if sample_count < 0 {
    return Err("SAC sample count cannot be negative")
  }
  let delta = read_float32(bytes, 0, is_big).to_double()
  let begin = read_float32(bytes, 20, is_big).to_double()
  Ok({
    endian,
    version,
    sample_count,
    delta,
    begin,
    station: read_string(bytes, 440, 8),
    channel: read_string(bytes, 608, 8),
    header_size: 632,
  })
}

///|
/// Return the physical size required by a SAC header and sample count.
pub fn sac_record_size(sample_count : Int) -> Int {
  if sample_count < 0 {
    632
  } else {
    632 + sample_count * 4
  }
}

///|
/// Validate that a byte array contains the complete SAC record.
pub fn validate_sac_record(bytes : Bytes) -> Result[Unit, String] {
  let header = match parse_sac_header(bytes) {
    Ok(value) => value
    Err(message) => return Err(message)
  }
  let expected = sac_record_size(header.sample_count)
  if bytes.length() < expected {
    Err("SAC record is shorter than its header declares")
  } else {
    Ok(())
  }
}

///|
/// Return the sample interval from a SAC record.
pub fn sac_delta(bytes : Bytes) -> Result[Double, String] {
  match parse_sac_header(bytes) {
    Ok(header) => Ok(header.delta)
    Err(message) => Err(message)
  }
}

///|
/// Return all SAC data samples as doubles.
pub fn sac_samples(bytes : Bytes) -> Result[Array[Double], String] {
  let header = match parse_sac_header(bytes) {
    Ok(value) => value
    Err(message) => return Err(message)
  }
  let expected = sac_record_size(header.sample_count)
  if bytes.length() < expected {
    return Err("SAC record is truncated")
  }
  let is_big = header.endian is SacBigEndian
  let result = Array::new(capacity=header.sample_count)
  for index = 0; index < header.sample_count; index = index + 1 {
    result.push(read_float32(bytes, 632 + index * 4, is_big).to_double())
  }
  Ok(result)
}

///|
/// Return a compact header-plus-data summary.
pub fn sac_summary(bytes : Bytes) -> Result[DescriptiveStats, String] {
  match sac_samples(bytes) {
    Ok(data) => Ok(describe_samples(data))
    Err(message) => Err(message)
  }
}

///|
/// Serialize a trace with a selected endian marker.
pub fn serialize_sac(trace : Trace, endian : SacEndian) -> Bytes {
  to_sac_bytes(trace, endian is SacBigEndian)
}