use crate::tick::Tick;
use alloc::collections::VecDeque;
use alloc::vec::Vec;
use bevy_ecs::component::Component;
use bevy_ecs::reflect::{ReflectComponent, ReflectResource};
use bevy_ecs::resource::Resource;
use bevy_reflect::Reflect;
use core::fmt::Debug;
use core::iter::FilterMap;
use tracing::debug;
#[derive(Debug, PartialEq, Clone, Default, Reflect)]
pub enum HistoryState<R> {
#[default]
Removed,
Updated(R),
}
#[derive(Resource, Component, Debug, Reflect)]
#[reflect(Component, Resource)]
pub struct HistoryBuffer<R> {
pub(crate) buffer: VecDeque<(Tick, HistoryState<R>)>,
}
impl<R> Default for HistoryBuffer<R> {
fn default() -> Self {
Self {
buffer: VecDeque::new(),
}
}
}
impl<R> PartialEq for HistoryBuffer<R> {
fn eq(&self, other: &Self) -> bool {
let self_history: Vec<_> = self.buffer.iter().map(|(tick, _)| *tick).collect();
let other_history: Vec<_> = other.buffer.iter().map(|(tick, _)| *tick).collect();
self_history.eq(&other_history)
}
}
impl<R> HistoryBuffer<R> {
pub fn len(&self) -> usize {
self.buffer.len()
}
pub fn front(&self) -> Option<&(Tick, HistoryState<R>)> {
self.buffer.front()
}
pub fn back(&self) -> Option<&(Tick, HistoryState<R>)> {
self.buffer.back()
}
pub fn clear(&mut self) {
self.buffer.clear();
}
pub fn clear_until_tick(&mut self, tick: Tick) {
let partition = self
.buffer
.partition_point(|(buffer_tick, _)| buffer_tick <= &tick);
if partition == 0 {
return;
}
self.buffer.drain(0..partition);
}
pub fn add_update(&mut self, tick: Tick, value: R) {
self.add(tick, Some(value));
}
pub fn add_remove(&mut self, tick: Tick) {
self.add(tick, None);
}
pub fn add(&mut self, tick: Tick, value: Option<R>) {
if let Some(last_tick) = self.peek().map(|(tick, _)| tick) {
if *last_tick == tick {
debug!(
"Adding update to history buffer for tick: {:?} but it already had a value for that tick!",
tick
);
self.buffer.pop_back();
}
}
self.buffer.push_back((
tick,
match value {
Some(value) => HistoryState::Updated(value),
None => HistoryState::Removed,
},
));
}
pub fn peek(&self) -> Option<&(Tick, HistoryState<R>)> {
self.buffer.back()
}
pub fn update_ticks(&mut self, delta: i16) {
self.buffer.iter_mut().for_each(|(tick, _)| {
*tick = *tick + delta;
});
}
}
impl<'a, R> IntoIterator for &'a HistoryBuffer<R> {
type Item = (Tick, &'a R);
type IntoIter = FilterMap<
<&'a VecDeque<(Tick, HistoryState<R>)> as IntoIterator>::IntoIter,
fn(&(Tick, HistoryState<R>)) -> Option<(Tick, &R)>,
>;
fn into_iter(self) -> Self::IntoIter {
self.buffer.iter().filter_map(|(tick, state)| match state {
HistoryState::Updated(value) => Some((*tick, value)),
HistoryState::Removed => None,
})
}
}
impl<R> IntoIterator for HistoryBuffer<R> {
type Item = (Tick, R);
type IntoIter = FilterMap<
<VecDeque<(Tick, HistoryState<R>)> as IntoIterator>::IntoIter,
fn((Tick, HistoryState<R>)) -> Option<(Tick, R)>,
>;
fn into_iter(self) -> Self::IntoIter {
self.buffer
.into_iter()
.filter_map(|(tick, state)| match state {
HistoryState::Updated(value) => Some((tick, value)),
HistoryState::Removed => None,
})
}
}
impl<R: Clone> HistoryBuffer<R> {
pub fn pop_until_tick(&mut self, tick: Tick) -> Option<HistoryState<R>> {
let partition = self
.buffer
.partition_point(|(buffer_tick, _)| buffer_tick <= &tick);
if partition == 0 {
return None;
}
self.buffer.drain(0..(partition - 1));
let res = self.buffer.pop_front().map(|(_, state)| state);
match res.as_ref() {
None => {}
Some(HistoryState::Removed) => {
self.buffer.push_front((tick, HistoryState::Removed))
}
Some(r) => self.buffer.push_front((tick, r.clone())),
};
res
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[derive(Clone, PartialEq, Debug)]
struct TestValue(f32);
#[test]
fn test_add_remove_history() {
let mut history = HistoryBuffer::<TestValue>::default();
assert_eq!(history.pop_until_tick(Tick(0)), None);
history.add_update(Tick(1), TestValue(1.0));
history.add_update(Tick(2), TestValue(2.0));
assert_eq!(
history.pop_until_tick(Tick(2)),
Some(HistoryState::Updated(TestValue(2.0)))
);
assert_eq!(history.buffer.len(), 1);
assert_eq!(
history.buffer,
VecDeque::from(vec![(Tick(2), HistoryState::Updated(TestValue(2.0)))])
);
history.add_update(Tick(4), TestValue(4.0));
assert_eq!(
history.pop_until_tick(Tick(3)),
Some(HistoryState::Updated(TestValue(2.0)))
);
assert_eq!(history.buffer.len(), 2);
assert_eq!(
history.buffer,
VecDeque::from(vec![
(Tick(3), HistoryState::Updated(TestValue(2.0))),
(Tick(4), HistoryState::Updated(TestValue(4.0)))
])
);
assert_eq!(history.pop_until_tick(Tick(0)), None);
assert_eq!(history.buffer.len(), 2);
history.add_remove(Tick(5));
assert_eq!(history.buffer.len(), 3);
assert_eq!(history.peek(), Some(&(Tick(5), HistoryState::Removed)));
history.clear_until_tick(Tick(3));
assert_eq!(
history.buffer,
VecDeque::from(vec![
(Tick(4), HistoryState::Updated(TestValue(4.0))),
(Tick(5), HistoryState::Removed)
])
);
assert_eq!(
history.into_iter().collect::<Vec<_>>(),
vec![(Tick(4), TestValue(4.0))]
);
}
}