///|
pub struct ClockSample {
  segment : Int
  relative_ticks : Int64
  delta_ticks : Int64
  wrapped : Bool
} derive(Eq, Debug)

///|
/// Relative PCR clock in 27 MHz ticks. No arrival-time or network jitter interpretation.
pub struct PcrClock {
  priv mut previous : Int64?
  priv mut relative : Int64
  priv mut segment : Int
}

///|
pub fn PcrClock::new() -> PcrClock {
  { previous: None, relative: 0L, segment: 0, }
}

///|
pub fn PcrClock::reset(self : PcrClock) -> Unit {
  self.previous = None
  self.relative = 0L
  if self.segment < 2147483647 {
    self.segment = self.segment + 1
  }
}

///|
/// Uses nearest signed modular difference. Gaps of half a wrap period or more are ambiguous.
/// Negative deltas start a fresh segment and are returned as clock_backward rather than hidden.
pub fn PcrClock::observe(
  self : PcrClock,
  ticks : Int64,
  discontinuity? : Bool = false,
) -> Result[ClockSample, Diagnostic] {
  let modulus = 2576980377600L
  if ticks < 0L || ticks >= modulus {
    return Err(fault("pcr_range", 0L, None))
  }
  if discontinuity {
    self.reset()
  }
  match self.previous {
    None => {
      self.previous = Some(ticks)
      Ok({
        segment: self.segment,
        relative_ticks: 0L,
        delta_ticks: 0L,
        wrapped: false,
      })
    }
    Some(prev) => {
      let raw = ticks - prev
      if raw == modulus / 2L || raw == 0L - modulus / 2L {
        self.reset()
        self.previous = Some(ticks)
        return Err(fault("clock_ambiguous", 0L, None))
      }
      let delta = if raw < 0L - modulus / 2L {
        raw + modulus
      } else if raw > modulus / 2L {
        raw - modulus
      } else {
        raw
      }
      if delta < 0L {
        self.reset()
        self.previous = Some(ticks)
        return Err(fault("clock_backward", 0L, None))
      }
      if self.relative > 9223372036854775807L - delta {
        self.reset()
        self.previous = Some(ticks)
        return Err(fault("clock_overflow", 0L, None))
      }
      self.relative = self.relative + delta
      self.previous = Some(ticks)
      Ok({
        segment: self.segment,
        relative_ticks: self.relative,
        delta_ticks: delta,
        wrapped: raw < 0L,
      })
    }
  }
}