use crate::{Lights, LightsSetter, color};
use std::time::Duration;
use embassy_time::{Instant, Timer};
#[derive(PartialEq, Eq, Debug)]
pub enum LoopControlFlow {
Continue,
Break,
}
pub trait Updatable: Send {
fn update(&mut self, delta_time: Duration) -> LoopControlFlow;
fn run(&mut self) -> impl Future<Output = ()>
where
Self: Sized,
{
run_with_opts(self, 30, false)
}
}
pub async fn run(updatable: &mut dyn Updatable, frames_per_second: u64) {
run_with_opts(updatable, frames_per_second, false).await;
}
#[expect(
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::cast_possible_truncation,
reason = "`frames_per_second` is unsigned, and reasonably should not be large enough to cause any problems with the above lints."
)]
pub async fn run_with_opts(
updatable: &mut dyn Updatable,
frames_per_second: u64,
clear_lights_each_frame: bool,
) {
let delay = embassy_time::Duration::from_millis((1000.0 / frames_per_second as f64) as u64);
let mut update_start = Instant::now();
'update_loop: loop {
let now = Instant::now();
let elapsed = now.duration_since(update_start);
if elapsed <= delay {
let sleep_time = delay - elapsed;
Timer::after(sleep_time).await;
}
let _flush_guard = LightsSetter::get().pause_auto_flush();
if clear_lights_each_frame {
LightsSetter::get().set_color(Lights::all(), color::OFF);
}
let now = Instant::now();
let delta_time = now.duration_since(update_start);
update_start = now;
if updatable.update(delta_time.into()) == LoopControlFlow::Break {
break 'update_loop;
}
}
}
pub struct InSeries {
updatables: Vec<Box<dyn Updatable>>,
}
impl InSeries {
#[must_use]
pub fn new(mut updatables: Vec<Box<dyn Updatable>>) -> InSeries {
updatables.reverse();
InSeries { updatables }
}
}
impl Updatable for InSeries {
fn update(&mut self, delta_time: Duration) -> LoopControlFlow {
let Some(updatable) = &mut self.updatables.last_mut() else {
return LoopControlFlow::Break;
};
let res = updatable.update(delta_time);
match res {
LoopControlFlow::Continue => (),
LoopControlFlow::Break => {
self.updatables.pop();
}
}
LoopControlFlow::Continue
}
}
pub struct InParallel {
updatables: Vec<Box<dyn Updatable>>,
}
impl InParallel {
#[must_use]
pub fn new(updatables: Vec<Box<dyn Updatable>>) -> InParallel {
InParallel { updatables }
}
}
impl Updatable for InParallel {
fn update(&mut self, delta_time: Duration) -> LoopControlFlow {
self.updatables
.retain_mut(|up| up.update(delta_time) == LoopControlFlow::Continue);
if self.updatables.is_empty() {
LoopControlFlow::Break
} else {
LoopControlFlow::Continue
}
}
}
pub struct DoNothingFor {
remaining: Duration,
}
impl DoNothingFor {
#[must_use]
pub fn new(time: Duration) -> DoNothingFor {
DoNothingFor { remaining: time }
}
}
impl Updatable for DoNothingFor {
fn update(&mut self, delta_time: Duration) -> LoopControlFlow {
self.remaining = self.remaining.checked_sub(delta_time).unwrap_or_default();
if self.remaining == Duration::from_millis(0) {
LoopControlFlow::Break
} else {
LoopControlFlow::Continue
}
}
}