use core::time::Duration;
use crate::WorldError;
use crate::abi::{DaytimeInfo, Header, Status};
use crate::{Host, Load, Off};
const HOUR: f64 = 3_600_000_000_000.0;
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct TimeOfDay {
pub hours: f32,
pub is_day: bool,
pub day_starts: f32,
pub night_starts: f32,
pub day_time_duration: Duration,
pub night_time_duration: Duration,
pub speed: f32,
}
impl TimeOfDay {
fn from_info(info: &DaytimeInfo) -> Self {
let length = |ns: i64| Duration::from_nanos(u64::try_from(ns).unwrap_or(0));
Self {
hours: hours(info.time),
is_day: info.flags & DaytimeInfo::DAY != 0,
day_starts: hours(info.day_start),
night_starts: hours(info.day_end),
day_time_duration: length(info.day_length),
night_time_duration: length(info.night_length),
speed: info.speed,
}
}
}
#[expect(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
reason = "a time of day in hours needs no more than an f32 holds"
)]
fn hours(ns: i64) -> f32 {
(ns as f64 / HOUR) as f32
}
#[derive(Clone, Copy, Debug)]
pub struct Clock {
host: Host,
}
impl Clock {
#[must_use]
pub fn now(&self) -> Option<TimeOfDay> {
let mut info = DaytimeInfo {
header: Header::of::<DaytimeInfo>(),
time: 0,
day_start: 0,
day_end: 0,
day_length: 0,
night_length: 0,
speed: 0.0,
flags: 0,
};
let status = (self.host.api().time_of_day)(self.host.handle(), &raw mut info);
(status == Status::OK).then(|| TimeOfDay::from_info(&info))
}
pub fn set_hours(&self, hours: f32) -> Result<(), WorldError> {
if !hours.is_finite() {
return Err(WorldError::Refused);
}
#[expect(
clippy::cast_possible_truncation,
reason = "the hours are taken modulo a day first"
)]
let ns = (f64::from(hours).rem_euclid(24.0) * HOUR) as i64;
WorldError::from_status((self.host.api().set_time_of_day)(self.host.handle(), ns))
}
}
#[derive(Clone, Copy, Debug)]
pub struct ClockSpeed {
host: Host,
}
impl ClockSpeed {
pub const MAX: f32 = 1000.0;
pub fn set(&self, factor: f32) -> Result<(), WorldError> {
if !(0.0..=Self::MAX).contains(&factor) {
return Err(WorldError::Refused);
}
WorldError::from_status((self.host.api().set_time_speed)(self.host.handle(), factor))
}
}
impl Load<'_> {
#[must_use]
pub fn clock(&mut self) -> Clock {
Clock { host: self.host() }
}
pub fn clock_speed(&mut self) -> Result<ClockSpeed, Off> {
let host = self.host();
let status = (host.api().time_speed_control)(host.handle());
crate::load::ok(status, ClockSpeed { host }, || "clock speed".to_owned())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nanoseconds_read_as_hours() {
assert!((hours(18 * 3_600_000_000_000) - 18.0).abs() < 1e-6);
assert!((hours(5_400_000_000_000) - 1.5).abs() < 1e-6);
}
}