use std::cell::{Cell, RefCell};
use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap};
use std::rc::Rc;
use std::time::{Duration, Instant};
use crate::{EventLoop, Result};
pub type TimerId = usize;
pub struct TimerState {
timer_id: TimerId,
duration: Duration,
event_loop: EventLoop,
handler: RefCell<Box<dyn FnMut()>>,
}
impl TimerState {
fn handle_timer(&self) -> Option<()> {
self.handler.borrow_mut()();
Some(())
}
pub fn repeat<F>(
event_loop: &EventLoop,
duration: Duration,
handler: F,
) -> Result<Rc<TimerState>>
where
F: FnMut() + 'static,
{
let now = Instant::now();
let timer_id = event_loop.state.timers.next_id.get();
event_loop.state.timers.next_id.set(timer_id + 1);
let state = Rc::new(TimerState {
timer_id,
duration,
event_loop: event_loop.clone(),
handler: RefCell::new(Box::new(handler)),
});
event_loop.state.timers.timers.borrow_mut().insert(timer_id, Rc::clone(&state));
event_loop.state.timers.queue.borrow_mut().push(QueueEntry {
time: now + duration,
timer_id,
});
Ok(state)
}
pub fn cancel(&self) {
let timers = &self.event_loop.state.timers;
timers.timers.borrow_mut().remove(&self.timer_id);
}
}
#[derive(Clone)]
struct QueueEntry {
time: Instant,
timer_id: TimerId,
}
impl PartialEq for QueueEntry {
fn eq(&self, other: &Self) -> bool {
self.time == other.time
}
}
impl Eq for QueueEntry {}
impl PartialOrd for QueueEntry {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.time.cmp(&other.time).reverse())
}
}
impl Ord for QueueEntry {
fn cmp(&self, other: &Self) -> Ordering {
self.time.cmp(&other.time).reverse()
}
}
pub struct Timers {
next_id: Cell<TimerId>,
timers: RefCell<HashMap<usize, Rc<TimerState>>>,
queue: RefCell<BinaryHeap<QueueEntry>>,
}
impl Timers {
pub fn new() -> Timers {
Timers {
next_id: Cell::new(0),
timers: RefCell::new(HashMap::new()),
queue: RefCell::new(BinaryHeap::new()),
}
}
pub fn next_time(&self) -> Option<Instant> {
self.queue.borrow().peek().map(|e| e.time)
}
pub fn poll(&self) {
let now = Instant::now();
while self.next_time().map_or(false, |t| t < now) {
let next = self.queue.borrow_mut().pop().unwrap();
let timer_state = self.timers.borrow().get(&next.timer_id).cloned();
if let Some(timer_state) = timer_state {
timer_state.handle_timer();
let next_time = (next.time + timer_state.duration).max(now);
self.queue.borrow_mut().push(QueueEntry {
time: next_time,
timer_id: next.timer_id,
})
}
}
}
}