use std::sync::Arc;
use std::time::Duration;
use bacnet_types::enums::ColorOperationInProgress;
use crate::traits::{DeadlineWaker, MonotonicClock};
use crate::transition::{Progress, Run, Transition, TransitionKind, TransitionLevel};
#[derive(Clone)]
pub(super) struct Engine<V> {
run: Option<Run<V>>,
sample_step: f64,
finest_increment: Option<f64>,
monotonic_clock: Option<Arc<MonotonicClock>>,
deadline_waker: Option<Arc<DeadlineWaker>>,
logical_now: Duration,
}
impl<V: TransitionLevel> Engine<V> {
pub(super) fn new(sample_step: f64) -> Self {
Self {
run: None,
sample_step,
finest_increment: None,
monotonic_clock: None,
deadline_waker: None,
logical_now: Duration::ZERO,
}
}
pub(super) fn now(&self) -> Duration {
self.monotonic_clock
.as_ref()
.map_or(self.logical_now, |clock| clock())
}
pub(super) fn tracking(&self, present: V, now: Duration) -> V {
self.run
.map_or(present, |run| run.transition().value_at(now))
}
pub(super) fn in_progress(&self, now: Duration) -> ColorOperationInProgress {
match self.run {
Some(run) if !run.transition().is_finished(now) => match run.transition().kind() {
TransitionKind::Fade => ColorOperationInProgress::FADE_ACTIVE,
TransitionKind::Ramp => ColorOperationInProgress::RAMP_ACTIVE,
},
_ => ColorOperationInProgress::IDLE,
}
}
pub(super) fn start(&mut self, transition: Option<Transition<V>>) {
self.run = transition.map(|transition| Run::start(transition, self.step()));
if self.run.is_some() {
if let Some(waker) = &self.deadline_waker {
waker();
}
}
}
pub(super) fn halt(&mut self, now: Duration) -> Option<V> {
self.run.take().map(|run| run.transition().value_at(now))
}
pub(super) fn advance_to(&mut self, now: Duration) -> bool {
let step = self.step();
let Some(run) = &mut self.run else {
return false;
};
match run.advance(now, step) {
Progress::Finished => {
self.run = None;
true
}
Progress::Sampled => true,
Progress::Pending => false,
}
}
pub(super) fn advance_by(&mut self, elapsed: Duration) -> bool {
self.logical_now = self.logical_now.saturating_add(elapsed);
self.advance_to(self.logical_now)
}
pub(super) fn deadline(&self) -> Option<Duration> {
self.run.map(|run| run.deadline())
}
fn step(&self) -> f64 {
self.finest_increment
.map_or(self.sample_step, |finest| self.sample_step.min(finest))
}
pub(super) fn set_finest_increment(&mut self, finest: Option<f64>) {
self.finest_increment = finest;
}
pub(super) fn bind_clock(&mut self, clock: Option<Arc<MonotonicClock>>) {
self.monotonic_clock = clock;
}
pub(super) fn bind_waker(&mut self, waker: Option<Arc<DeadlineWaker>>) {
self.deadline_waker = waker;
}
pub(super) fn frozen(&self) -> Self {
Self {
run: self.run,
sample_step: self.sample_step,
finest_increment: self.finest_increment,
monotonic_clock: None,
deadline_waker: None,
logical_now: self.now(),
}
}
}