///|
/// Structured input failures; no NaN/Infinity is accepted at the public boundary.
pub(all) enum InputError {
  InvalidInstant
  InvalidDate
  InvalidLatitude
  InvalidLongitude
  InvalidHeight
  InvalidAngle
  InvalidSamplingStep
  InvalidSampleCount
  TooManyEvents
} derive(Eq, Debug)

///|
/// UTC Unix milliseconds, whole milliseconds, supported years 1900 through 2150.
pub struct Instant {
  priv ms : Double
} derive(Eq, Debug)

///|
pub fn instant(unix_ms : Double) -> Result[Instant, InputError] {
  if !(unix_ms >= -2208988800000.0 && unix_ms < 5711817600000.0) {
    return Err(InvalidInstant)
  }
  Ok({ ms: @math.trunc(unix_ms), })
}

///|
pub fn Instant::unix_ms(self : Instant) -> Double {
  self.ms
}

///|
/// Earth latitude north-positive and longitude east-positive in degrees.
pub struct Observer {
  priv lat : Double
  priv lng : Double
} derive(Eq, Debug)

///|
pub fn observer(
  latitude_deg : Double,
  longitude_deg : Double,
) -> Result[Observer, InputError] {
  if !(latitude_deg >= -90.0 && latitude_deg <= 90.0) {
    return Err(InvalidLatitude)
  }
  if !(longitude_deg >= -180.0 && longitude_deg <= 180.0) {
    return Err(InvalidLongitude)
  }
  Ok({ lat: latitude_deg, lng: longitude_deg, })
}

///|
pub fn Observer::latitude(self : Observer) -> Double {
  self.lat
}

///|
pub fn Observer::longitude(self : Observer) -> Double {
  self.lng
}

///|
/// Apparent altitude and azimuth in degrees. Azimuth: north=0, east=90.
pub(all) struct Position {
  azimuth : Double
  altitude : Double
} derive(Eq, Debug)