///|
pub(all) suberror TimeError {
  NegativeMonotonicTime(Int64)
  NegativeDuration(Int64)
  TimeOverflow
} derive(Debug, Eq)

///|
pub struct Duration(Int64) derive(Debug, Eq, Compare)

///|
pub fn Duration::milliseconds(value : Int64) -> Duration raise TimeError {
  if value < 0L {
    raise NegativeDuration(value)
  }
  Duration(value)
}

///|
pub fn Duration::zero() -> Duration {
  Duration(0L)
}

///|
pub fn Duration::as_milliseconds(self : Duration) -> Int64 {
  self.0
}

///|
pub struct Instant(Int64) derive(Debug, Eq, Compare)

///|
pub fn Instant::from_milliseconds(value : Int64) -> Instant raise TimeError {
  if value < 0L {
    raise NegativeMonotonicTime(value)
  }
  Instant(value)
}

///|
pub fn Instant::as_milliseconds(self : Instant) -> Int64 {
  self.0
}

///|
pub fn Instant::checked_add(
  self : Instant,
  duration : Duration,
) -> Instant raise TimeError {
  let max_int64 = 9223372036854775807L
  if self.0 > max_int64 - duration.0 {
    raise TimeOverflow
  }
  Instant(self.0 + duration.0)
}

///|
pub fn Instant::saturating_duration_since(
  self : Instant,
  earlier : Instant,
) -> Duration {
  if self.0 <= earlier.0 {
    Duration::zero()
  } else {
    Duration(self.0 - earlier.0)
  }
}

///|
pub struct WallTime(Int64) derive(Debug, Eq, Compare)

///|
pub fn WallTime::from_unix_nanoseconds(value : Int64) -> WallTime {
  WallTime(value)
}

///|
pub fn WallTime::as_unix_nanoseconds(self : WallTime) -> Int64 {
  self.0
}

///|
pub struct ClockSample {
  monotonic : Instant
  wall : WallTime
} derive(Debug, Eq)

///|
pub fn ClockSample::new(monotonic~ : Instant, wall~ : WallTime) -> ClockSample {
  { monotonic, wall, }
}

///|
pub fn ClockSample::monotonic(self : ClockSample) -> Instant {
  self.monotonic
}

///|
pub fn ClockSample::wall(self : ClockSample) -> WallTime {
  self.wall
}

///|
pub fn ClockSample::wall_at(
  self : ClockSample,
  instant : Instant,
) -> WallTime raise TimeError {
  let max_int64 = 9223372036854775807L
  let min_int64 = -9223372036854775807L - 1L
  let max_delta_ms = 9223372036854L
  let delta_ms = instant.0 - self.monotonic.0
  if delta_ms > max_delta_ms || delta_ms < -max_delta_ms {
    raise TimeOverflow
  }
  let delta_ns = delta_ms * 1000000L
  if delta_ns > 0L && self.wall.0 > max_int64 - delta_ns {
    raise TimeOverflow
  }
  if delta_ns < 0L && self.wall.0 < min_int64 - delta_ns {
    raise TimeOverflow
  }
  WallTime(self.wall.0 + delta_ns)
}

///|
pub struct ManualClock {
  mut monotonic : Instant
  mut wall : WallTime
}

///|
pub fn ManualClock::new(sample : ClockSample) -> ManualClock {
  { monotonic: sample.monotonic, wall: sample.wall, }
}

///|
pub fn ManualClock::sample(self : ManualClock) -> ClockSample {
  ClockSample::new(monotonic=self.monotonic, wall=self.wall)
}

///|
pub fn ManualClock::advance(
  self : ManualClock,
  duration : Duration,
) -> Unit raise TimeError {
  let next_monotonic = self.monotonic.checked_add(duration)
  let sample = ClockSample::new(monotonic=self.monotonic, wall=self.wall)
  let next_wall = sample.wall_at(next_monotonic)
  self.monotonic = next_monotonic
  self.wall = next_wall
}