use core::marker::PhantomData;
use core::cell::RefCell;
use core::ops::DerefMut;
use avr_oxide::devices::internal::StaticShareable;
use crate::alloc::boxed::Box;
use crate::devices::masterclock::{DelayCallback, DelayEvents};
use crate::event::{EventSink, EventSource, OxideEvent, OxideEventEnvelope};
use crate::hal::generic::timer::{RtcCalibration, RtcPrescaler, RtcSource, RtcTimerCalibration, TimerControl, TimerIsrCallback};
use crate::hal::generic::timer::TimerMode::Periodic;
use crate::panic_if_none;
use crate::private::delayq::{DelayQueue, SimpleDelayQueue};
use crate::util::OwnOrBorrowMut;
use crate::time::Duration;
use super::masterclock::TickCallback;
use super::masterclock::TickEvents;
pub struct WallClock<'wc,T,S>
where
T: 'static + TimerControl + RtcTimerCalibration,
S: EventSink
{
timer: OwnOrBorrowMut<'static,T>,
phantom: PhantomData<S>,
running_time: Duration,
running_time_overflow: bool,
delay_events: RefCell<SimpleDelayQueue<Duration,Box<dyn DelayCallback + 'wc>>>,
on_tick: RefCell<Option<Box<dyn TickCallback + 'wc>>>
}
impl<'wc,T,S> StaticShareable for WallClock<'wc,T,S>
where
T: 'static + TimerControl + RtcTimerCalibration,
S: EventSink
{}
impl<T,S> WallClock<'_,T,S>
where
T: 'static + TimerControl + RtcTimerCalibration,
S: EventSink
{
pub fn using<OT: Into<OwnOrBorrowMut<'static,T>>>(timer: OT) -> Self {
let mut timer : OwnOrBorrowMut<T> = timer.into();
timer.set_clock_calibration(RtcSource::Int1k,
RtcCalibration::Fast(0),
RtcPrescaler::Div4);
timer.set_mode(Periodic);
timer.set_interrupt_period(1024);
Self {
timer,
running_time: Duration::ZERO,
running_time_overflow: false,
phantom: PhantomData::default(),
on_tick: RefCell::new(None),
delay_events: RefCell::new(SimpleDelayQueue::new())
}
}
pub fn static_using<OT: Into<OwnOrBorrowMut<'static,T>>>(timer: OT) -> &'static mut Self {
let alloc = Box::new(Self::using(timer));
Box::leak(alloc)
}
pub fn with_timer(timer: &'static mut T) -> Self {
Self::using(timer)
}
pub fn static_with_timer(timer: &'static mut T) -> &'static mut Self {
Self::static_using(timer)
}
pub fn runtime(&self) -> Duration {
crate::hal::concurrency::interrupt::isolated(||{
self.running_time
})
}
pub fn runtime_overflowed(&self) -> bool {
crate::hal::concurrency::interrupt::isolated(||{
self.running_time_overflow
})
}
pub fn clear_runtime_overflow(&mut self) {
crate::hal::concurrency::interrupt::isolated(||{
self.running_time_overflow = false
})
}
fn increment_running_time(&mut self, secs: u16) {
crate::hal::concurrency::interrupt::isolated(||{
match self.running_time.checked_add(Duration::from_secs(secs as u32)) {
None => { self.running_time_overflow = true;
self.running_time = Duration::from_secs(secs as u32);
},
Some(new_running_time) => {
self.running_time = new_running_time;
}
}
})
}
}
impl<'wc, T, S> TickEvents<'wc> for WallClock<'wc, T, S>
where
T: 'static + TimerControl + RtcTimerCalibration,
S: EventSink
{
fn on_tick(&self, bf: Box<dyn TickCallback + 'wc>) {
self.on_tick.replace(Some(bf));
}
}
impl<'wc,T,S> DelayEvents<'wc> for WallClock<'wc,T,S>
where
T: 'static + TimerControl + RtcTimerCalibration,
S: EventSink
{
fn after_delay(&self, delay: Duration, bf: Box<dyn DelayCallback + 'wc>) {
self.delay_events.borrow_mut().insert_at(delay, bf);
}
}
impl<T,S> EventSource for WallClock<'_,T,S>
where
T: 'static + TimerControl + RtcTimerCalibration,
S: EventSink
{
#[optimize(speed)]
fn listen(&'static self) {
self.timer.start(TimerIsrCallback::WithData(|source, ticks, udata| {
unsafe {
let clock = &mut *(panic_if_none!(udata) as *mut WallClock<T,S>);
clock.increment_running_time(ticks);
}
S::event(OxideEventEnvelope::to(unsafe { &*(panic_if_none!(udata) as *const WallClock<T,S> as *const dyn EventSource) },
OxideEvent::ClockTick(source,ticks)));
true
}, self as *const dyn core::any::Any ));
}
#[optimize(speed)]
fn process_event(&self, evt: OxideEvent) {
match (self.on_tick.borrow_mut().deref_mut(), evt) {
(Some(f), OxideEvent::ClockTick(source, ticks)) => {
let time_passed = Duration::from_secs(ticks as u32);
self.delay_events.borrow_mut().decrement(time_passed);
while let Some(mut handler) = self.delay_events.borrow_mut().consume_next_ready() {
(*handler)(source)
}
(*f)(source,time_passed)
},
_ => {}
}
}
}