use crate::core::entity::EntityMap;
use crate::core::tasks::CancelToken;
use std::time::{Duration, Instant};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TimerId(u64);
type TimerJob = Box<dyn FnOnce(&mut EntityMap)>;
struct Entry {
deadline: Instant,
id: TimerId,
job: TimerJob,
cancel: Option<CancelToken>,
}
impl Entry {
fn cancelled(&self) -> bool {
self.cancel.as_ref().is_some_and(CancelToken::is_cancelled)
}
}
#[derive(Default)]
pub struct TimerQueue {
entries: Vec<Entry>,
next_id: u64,
animation_frame: bool,
}
impl TimerQueue {
pub fn schedule(&mut self, delay: Duration, job: impl FnOnce(&mut EntityMap) + 'static) -> TimerId {
self.insert(delay, None, Box::new(job))
}
pub(crate) fn schedule_cancellable(
&mut self,
delay: Duration,
cancel: CancelToken,
job: impl FnOnce(&mut EntityMap) + 'static,
) -> TimerId {
self.insert(delay, Some(cancel), Box::new(job))
}
fn insert(&mut self, delay: Duration, cancel: Option<CancelToken>, job: TimerJob) -> TimerId {
self.next_id += 1;
let id = TimerId(self.next_id);
self.entries.push(Entry { deadline: Instant::now() + delay, id, job, cancel });
id
}
pub fn cancel(&mut self, id: TimerId) {
self.entries.retain(|entry| entry.id != id);
}
pub fn next_deadline(&self) -> Option<Instant> {
self.entries.iter().filter(|entry| !entry.cancelled()).map(|entry| entry.deadline).min()
}
pub fn take_due(&mut self, now: Instant) -> Vec<TimerJob> {
let mut due = Vec::new();
let mut index = 0;
while index < self.entries.len() {
if self.entries[index].cancelled() {
self.entries.swap_remove(index);
} else if self.entries[index].deadline <= now {
due.push(self.entries.swap_remove(index).job);
} else {
index += 1;
}
}
due
}
pub fn request_animation_frame(&mut self) {
self.animation_frame = true;
}
pub fn take_animation_frame(&mut self) -> bool {
std::mem::take(&mut self.animation_frame)
}
}