use bevy::prelude::*;
use chrono::{DateTime, Local};
use std::collections::{HashMap, VecDeque};
use crate::capture::CropRegion;
use crate::config::ScreenshotConfig;
#[derive(Clone)]
pub struct CapturedScreenshot {
pub image: Image,
pub timestamp: DateTime<Local>,
pub description: String,
pub key: String,
pub frame_number: u32,
pub crop_region: Option<CropRegion>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImageValidation {
Valid,
ZeroDimensions { width: u32, height: u32 },
EmptyData,
DataSizeMismatch {
expected: usize,
actual: usize,
width: u32,
height: u32,
},
}
impl CapturedScreenshot {
pub fn validate(&self) -> ImageValidation {
let width = self.image.width();
let height = self.image.height();
if width == 0 || height == 0 {
return ImageValidation::ZeroDimensions { width, height };
}
let data = match &self.image.data {
Some(d) if !d.is_empty() => d,
_ => return ImageValidation::EmptyData,
};
let expected_size = (width * height * 4) as usize;
let actual_size = data.len();
if actual_size != expected_size {
return ImageValidation::DataSizeMismatch {
expected: expected_size,
actual: actual_size,
width,
height,
};
}
ImageValidation::Valid
}
pub fn is_valid(&self) -> bool {
self.validate() == ImageValidation::Valid
}
}
pub struct ScreenshotRingBuffer {
buffer: VecDeque<CapturedScreenshot>,
capacity: usize,
}
impl ScreenshotRingBuffer {
pub fn new(capacity: usize) -> Self {
Self {
buffer: VecDeque::with_capacity(capacity),
capacity,
}
}
pub fn push(&mut self, screenshot: CapturedScreenshot) -> Option<CapturedScreenshot> {
let evicted = if self.buffer.len() >= self.capacity {
self.buffer.pop_front()
} else {
None
};
self.buffer.push_back(screenshot);
evicted
}
pub fn len(&self) -> usize {
self.buffer.len()
}
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn iter(&self) -> impl Iterator<Item = &CapturedScreenshot> {
self.buffer.iter()
}
pub fn drain(&mut self) -> impl Iterator<Item = CapturedScreenshot> + '_ {
self.buffer.drain(..)
}
pub fn clear(&mut self) {
self.buffer.clear();
}
pub fn latest(&self) -> Option<&CapturedScreenshot> {
self.buffer.back()
}
}
#[derive(Resource)]
pub struct ScreenshotBufferManager {
buffers: HashMap<String, ScreenshotRingBuffer>,
config: ScreenshotConfig,
}
impl ScreenshotBufferManager {
pub fn new(config: ScreenshotConfig) -> Self {
Self {
buffers: HashMap::new(),
config,
}
}
pub fn get_or_create_buffer(&mut self, key: &str) -> &mut ScreenshotRingBuffer {
let capacity = self
.config
.keys
.get(key)
.and_then(|k| k.buffer_capacity)
.unwrap_or(self.config.buffer_capacity);
self.buffers
.entry(key.to_string())
.or_insert_with(|| ScreenshotRingBuffer::new(capacity))
}
pub fn push(&mut self, screenshot: CapturedScreenshot) -> Option<CapturedScreenshot> {
let key = screenshot.key.clone();
self.get_or_create_buffer(&key).push(screenshot)
}
pub fn config(&self) -> &ScreenshotConfig {
&self.config
}
pub fn get_buffer(&self, key: &str) -> Option<&ScreenshotRingBuffer> {
self.buffers.get(key)
}
pub fn get_buffer_mut(&mut self, key: &str) -> Option<&mut ScreenshotRingBuffer> {
self.buffers.get_mut(key)
}
pub fn keys(&self) -> impl Iterator<Item = &String> {
self.buffers.keys()
}
pub fn total_screenshots(&self) -> usize {
self.buffers.values().map(|b| b.len()).sum()
}
}