1use eframe::egui::{self, Context};
2use ferrite_cache::{CacheHandle, CacheResult};
3use ferrite_logging::metrics::PerformanceMetrics;
4use image::{DynamicImage, GenericImageView};
5use std::{path::PathBuf, sync::Arc};
6use tracing::{info, info_span, instrument, warn};
7
8use crate::error::{ImageError, Result};
9
10pub struct ImageManager {
11 pub current_image: Option<Arc<DynamicImage>>,
12 pub texture: Option<egui::TextureHandle>,
13 pub current_path: Option<PathBuf>,
14 pub cache_manager: Arc<CacheHandle>,
15}
16
17impl ImageManager {
18 #[instrument(skip_all)]
19 pub fn new(cache_manager: Arc<CacheHandle>) -> Self {
20 info!("Initializing ImageManager with cache");
21 Self {
22 current_image: None,
23 texture: None,
24 current_path: None,
25 cache_manager,
26 }
27 }
28
29 pub fn set_path(&mut self, path: PathBuf) {
30 info!("Setting new image path: {}", path.display());
31 self.current_path = Some(path);
32 }
33
34 #[instrument(skip(self, path), fields(path = ?path))]
35 pub fn load_image(&mut self, path: PathBuf) -> Result<()> {
36 let metrics = PerformanceMetrics::new("image_loading", true);
37
38 let result = info_span!("image_loading_process").in_scope(|| {
39 let get_image: CacheResult<Arc<DynamicImage>> =
40 self.cache_manager.get_image(path.clone());
41
42 if let Ok(image_data) = get_image {
43 let dimensions = image_data.dimensions();
44 info!("Setting new image and clearing texture");
45
46 self.texture = None;
47
48 self.current_image = Some(image_data);
49 self.current_path = Some(path);
50
51 info!(
52 "Successfully loaded image from cache: dimensions={}x{}",
53 dimensions.0, dimensions.1
54 );
55 Ok(())
56 } else {
57 Err(ImageError::CacheError(get_image.unwrap_err()))
58 }
59 });
60
61 let duration = metrics.finish();
62 info!("Image loading completed in {} µs", duration.as_micros());
63
64 result
65 }
66
67 pub fn preload_image(&self, path: PathBuf) {
68 if let Err(e) = self.cache_manager.cache_image(path) {
69 warn!("Failed to preload image: {}", e);
70 }
71 }
72
73 pub fn get_current_dimensions(&self) -> Option<(u32, u32)> {
74 self.current_image
75 .as_ref()
76 .map(|img| img.dimensions())
77 }
78
79 #[instrument(skip(self, ctx))]
80 pub fn show_performance_window(&self, ctx: &Context) {
81 egui::Window::new("Performance Metrics").show(ctx, |ui| {
82 ui.heading("Image Information");
83
84 if let Some(ref img) = self.current_image {
85 let dims = img.dimensions();
86 ui.label(format!(
87 "Current image dimensions: {}x{}",
88 dims.0, dims.1
89 ));
90 }
91
92 if let Some(path) = &self.current_path {
93 ui.label(format!(
94 "Current image: {:?}",
95 path.file_name().unwrap_or_default()
96 ));
97 }
98 });
99 }
100
101 #[instrument(skip(self))]
105 pub fn delete_current_file(
106 &mut self,
107 ) -> crate::error::Result<Option<PathBuf>> {
108 use crate::operations::FileOperations;
109
110 if let Some(current_path) = self.current_path.take() {
111 self.current_image = None;
113 self.texture = None;
114
115 FileOperations::delete_file(¤t_path).map_err(|e| {
117 crate::ImageError::Other(format!("Delete failed: {}", e))
118 })?;
119
120 info!("Successfully deleted file: {}", current_path.display());
121 Ok(Some(current_path))
122 } else {
123 warn!("No current file to delete");
124 Ok(None)
125 }
126 }
127}