1use crate::models::{
4 session::AlgorithmType,
5 traits::Sorter,
6};
7use anyhow::{Result, anyhow};
8use crossterm::event::{KeyEvent, KeyCode, KeyModifiers};
9#[derive(Debug, Clone)]
11pub struct DisplayMode {
12 pub viewed_algorithm: AlgorithmType,
14 pub available_algorithms: Vec<AlgorithmType>,
16 pub cycle_index: usize,
18 needs_update: bool,
20}
21
22impl DisplayMode {
23 pub fn new() -> Self {
25 let available_algorithms = AlgorithmType::all();
26 Self {
27 viewed_algorithm: available_algorithms[0], available_algorithms,
29 cycle_index: 0,
30 needs_update: true,
31 }
32 }
33
34 pub fn with_algorithms(algorithms: Vec<AlgorithmType>) -> Result<Self> {
36 if algorithms.is_empty() {
37 return Err(anyhow!("Cannot create DisplayMode with empty algorithm list"));
38 }
39
40 Ok(Self {
41 viewed_algorithm: algorithms[0],
42 available_algorithms: algorithms,
43 cycle_index: 0,
44 needs_update: true,
45 })
46 }
47
48 pub fn handle_visualization_switch(&mut self, _key_event: KeyEvent) -> Result<()> {
50 self.cycle_to_next_algorithm();
51 Ok(())
52 }
53
54 pub fn cycle_to_next_algorithm(&mut self) {
56 if !self.available_algorithms.is_empty() {
57 self.cycle_index = (self.cycle_index + 1) % self.available_algorithms.len();
58 self.viewed_algorithm = self.available_algorithms[self.cycle_index];
59 self.needs_update = true;
60 }
61 }
62
63 pub fn set_viewed_algorithm(&mut self, algorithm_type: AlgorithmType) -> Result<()> {
65 if let Some(index) = self.available_algorithms.iter().position(|&alg| alg == algorithm_type) {
66 self.cycle_index = index;
67 self.viewed_algorithm = algorithm_type;
68 self.needs_update = true;
69 Ok(())
70 } else {
71 Err(anyhow!("Algorithm {:?} is not available in current race", algorithm_type))
72 }
73 }
74
75 pub fn set_viewed_algorithm_by_index(&mut self, index: usize) -> Result<()> {
77 if index < self.available_algorithms.len() {
78 self.cycle_index = index;
79 self.viewed_algorithm = self.available_algorithms[index];
80 self.needs_update = true;
81 Ok(())
82 } else {
83 Err(anyhow!("Algorithm index {} is out of bounds (max: {})", index, self.available_algorithms.len() - 1))
84 }
85 }
86
87 pub fn should_update_visualization(&self) -> bool {
89 self.needs_update
90 }
91
92 pub fn mark_visualization_updated(&mut self) {
94 self.needs_update = false;
95 }
96
97 pub fn get_current_array_data<'a>(&self, algorithms: &'a [Box<dyn Sorter>]) -> Option<&'a [i32]> {
99 for (i, algorithm) in algorithms.iter().enumerate() {
101 if let Some(expected_type) = AlgorithmType::from_index(i)
102 && expected_type == self.viewed_algorithm {
103 return Some(algorithm.get_array());
104 }
105 }
106 None
107 }
108
109 pub fn get_array_source_algorithm(&self) -> String {
111 self.viewed_algorithm.to_string()
112 }
113
114 pub fn get_highlighted_algorithm(&self) -> AlgorithmType {
116 self.viewed_algorithm
117 }
118
119 pub fn is_algorithm_highlighted(&self, algorithm_type: AlgorithmType) -> bool {
121 self.viewed_algorithm == algorithm_type
122 }
123
124 pub fn get_current_algorithm_index(&self) -> usize {
126 self.cycle_index
127 }
128
129 pub fn get_algorithm_count(&self) -> usize {
131 self.available_algorithms.len()
132 }
133
134 pub fn update_available_algorithms(&mut self, algorithms: Vec<AlgorithmType>) -> Result<()> {
136 if algorithms.is_empty() {
137 return Err(anyhow!("Cannot update to empty algorithm list"));
138 }
139
140 self.available_algorithms = algorithms;
141
142 if !self.available_algorithms.contains(&self.viewed_algorithm) {
144 self.viewed_algorithm = self.available_algorithms[0];
145 self.cycle_index = 0;
146 self.needs_update = true;
147 } else {
148 self.cycle_index = self.available_algorithms
150 .iter()
151 .position(|&alg| alg == self.viewed_algorithm)
152 .unwrap_or(0);
153 }
154
155 Ok(())
156 }
157
158 pub fn reset_to_first(&mut self) {
160 if !self.available_algorithms.is_empty() {
161 self.cycle_index = 0;
162 self.viewed_algorithm = self.available_algorithms[0];
163 self.needs_update = true;
164 }
165 }
166
167 pub fn get_algorithm_at_index(&self, index: usize) -> Option<AlgorithmType> {
169 self.available_algorithms.get(index).copied()
170 }
171
172 pub fn can_cycle_next(&self) -> bool {
174 self.available_algorithms.len() > 1
175 }
176
177 pub fn peek_next_algorithm(&self) -> Option<AlgorithmType> {
179 if self.available_algorithms.is_empty() {
180 None
181 } else {
182 let next_index = (self.cycle_index + 1) % self.available_algorithms.len();
183 self.available_algorithms.get(next_index).copied()
184 }
185 }
186
187 pub fn peek_previous_algorithm(&self) -> Option<AlgorithmType> {
189 if self.available_algorithms.is_empty() {
190 None
191 } else {
192 let prev_index = if self.cycle_index == 0 {
193 self.available_algorithms.len() - 1
194 } else {
195 self.cycle_index - 1
196 };
197 self.available_algorithms.get(prev_index).copied()
198 }
199 }
200
201 pub fn from_algorithms(algorithms: &[Box<dyn Sorter>]) -> Self {
203 let available_algorithms = (0..algorithms.len())
204 .filter_map(AlgorithmType::from_index)
205 .collect();
206
207 Self::with_algorithms(available_algorithms).unwrap_or_default()
208 }
209
210 pub fn process_key_event(&mut self, key_event: KeyEvent) -> Result<bool> {
212 match key_event {
213 KeyEvent {
214 code: KeyCode::Char('v'),
215 modifiers: KeyModifiers::NONE,
216 ..
217 } => {
218 self.handle_visualization_switch(key_event)?;
219 Ok(true) },
221 _ => Ok(false), }
223 }
224}
225
226impl Default for DisplayMode {
227 fn default() -> Self {
228 Self::new()
229 }
230}
231
232#[derive(Debug, Clone, PartialEq)]
234pub enum MemoryDisplayValue {
235 Bytes(usize),
237 NotAvailable,
239}
240
241impl MemoryDisplayValue {
242 pub fn as_string(&self) -> String {
244 match self {
245 MemoryDisplayValue::Bytes(bytes) => {
246 Self::format_bytes(*bytes)
247 },
248 MemoryDisplayValue::NotAvailable => "N/A".to_string(),
249 }
250 }
251
252 fn format_bytes(bytes: usize) -> String {
254 const UNITS: &[&str] = &["B", "KB", "MB", "GB"];
255 let mut size = bytes as f64;
256 let mut unit_index = 0;
257
258 while size >= 1024.0 && unit_index < UNITS.len() - 1 {
259 size /= 1024.0;
260 unit_index += 1;
261 }
262
263 if unit_index == 0 {
264 format!("{}B", bytes)
265 } else {
266 format!("{:.1}{}", size, UNITS[unit_index])
267 }
268 }
269}
270
271impl std::fmt::Display for MemoryDisplayValue {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 write!(f, "{}", self.as_string())
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280 use crossterm::event::KeyEventKind;
281
282 #[test]
283 fn test_display_mode_creation() {
284 let display = DisplayMode::new();
285
286 assert_eq!(display.viewed_algorithm, AlgorithmType::BubbleSort);
287 assert_eq!(display.available_algorithms.len(), 7);
288 assert_eq!(display.cycle_index, 0);
289 assert!(display.should_update_visualization());
290 }
291
292 #[test]
293 fn test_algorithm_cycling() {
294 let mut display = DisplayMode::new();
295
296 assert_eq!(display.viewed_algorithm, AlgorithmType::BubbleSort);
298
299 display.cycle_to_next_algorithm();
301 assert_eq!(display.viewed_algorithm, AlgorithmType::SelectionSort);
302 assert_eq!(display.cycle_index, 1);
303
304 for _ in 0..6 {
306 display.cycle_to_next_algorithm();
307 }
308 assert_eq!(display.viewed_algorithm, AlgorithmType::BubbleSort);
309 assert_eq!(display.cycle_index, 0);
310 }
311
312 #[test]
313 fn test_set_viewed_algorithm() {
314 let mut display = DisplayMode::new();
315
316 assert!(display.set_viewed_algorithm(AlgorithmType::QuickSort).is_ok());
318 assert_eq!(display.viewed_algorithm, AlgorithmType::QuickSort);
319 assert_eq!(display.cycle_index, 4);
320
321 let limited_algorithms = vec![AlgorithmType::BubbleSort, AlgorithmType::QuickSort];
323 let mut limited_display = DisplayMode::with_algorithms(limited_algorithms).unwrap();
324 assert!(limited_display.set_viewed_algorithm(AlgorithmType::ShellSort).is_err());
325 }
326
327 #[test]
328 fn test_algorithm_index_operations() {
329 let mut display = DisplayMode::new();
330
331 assert!(display.set_viewed_algorithm_by_index(3).is_ok());
333 assert_eq!(display.viewed_algorithm, AlgorithmType::MergeSort);
334
335 assert!(display.set_viewed_algorithm_by_index(10).is_err());
337
338 assert_eq!(display.get_algorithm_at_index(2), Some(AlgorithmType::InsertionSort));
340 assert_eq!(display.get_algorithm_at_index(10), None);
341 }
342
343 #[test]
344 fn test_peek_operations() {
345 let mut display = DisplayMode::new();
346 display.set_viewed_algorithm_by_index(2).unwrap(); assert_eq!(display.peek_next_algorithm(), Some(AlgorithmType::MergeSort));
349 assert_eq!(display.peek_previous_algorithm(), Some(AlgorithmType::SelectionSort));
350
351 display.set_viewed_algorithm_by_index(0).unwrap(); assert_eq!(display.peek_previous_algorithm(), Some(AlgorithmType::ShellSort));
354
355 display.set_viewed_algorithm_by_index(6).unwrap(); assert_eq!(display.peek_next_algorithm(), Some(AlgorithmType::BubbleSort));
357 }
358
359 #[test]
360 fn test_key_event_processing() {
361 let mut display = DisplayMode::new();
362
363 let v_key = KeyEvent {
364 code: KeyCode::Char('v'),
365 modifiers: KeyModifiers::NONE,
366 kind: KeyEventKind::Press,
367 state: crossterm::event::KeyEventState::empty(),
368 };
369
370 let initial_algorithm = display.viewed_algorithm;
371 let handled = display.process_key_event(v_key).unwrap();
372
373 assert!(handled);
374 assert_ne!(display.viewed_algorithm, initial_algorithm);
375
376 let other_key = KeyEvent {
378 code: KeyCode::Char('x'),
379 modifiers: KeyModifiers::NONE,
380 kind: KeyEventKind::Press,
381 state: crossterm::event::KeyEventState::empty(),
382 };
383
384 let handled = display.process_key_event(other_key).unwrap();
385 assert!(!handled);
386 }
387
388 #[test]
389 fn test_available_algorithms_update() {
390 let mut display = DisplayMode::new();
391
392 let limited = vec![AlgorithmType::BubbleSort, AlgorithmType::QuickSort, AlgorithmType::HeapSort];
394 assert!(display.update_available_algorithms(limited.clone()).is_ok());
395
396 assert_eq!(display.available_algorithms, limited);
397 assert_eq!(display.get_algorithm_count(), 3);
398
399 assert!(display.update_available_algorithms(vec![]).is_err());
401 }
402
403 #[test]
404 fn test_memory_display_value() {
405 let value_bytes = MemoryDisplayValue::Bytes(1536);
406 assert_eq!(value_bytes.to_string(), "1.5KB");
407
408 let value_na = MemoryDisplayValue::NotAvailable;
409 assert_eq!(value_na.to_string(), "N/A");
410
411 assert_eq!(MemoryDisplayValue::Bytes(512).to_string(), "512B");
413 assert_eq!(MemoryDisplayValue::Bytes(1024).to_string(), "1.0KB");
414 assert_eq!(MemoryDisplayValue::Bytes(1048576).to_string(), "1.0MB");
415 }
416
417 #[test]
418 fn test_algorithm_highlighting() {
419 let mut display = DisplayMode::new();
420 display.set_viewed_algorithm(AlgorithmType::QuickSort).unwrap();
421
422 assert!(display.is_algorithm_highlighted(AlgorithmType::QuickSort));
423 assert!(!display.is_algorithm_highlighted(AlgorithmType::BubbleSort));
424 assert_eq!(display.get_highlighted_algorithm(), AlgorithmType::QuickSort);
425 }
426
427 #[test]
428 fn test_reset_to_first() {
429 let mut display = DisplayMode::new();
430 display.cycle_to_next_algorithm(); display.cycle_to_next_algorithm();
432
433 assert_ne!(display.viewed_algorithm, AlgorithmType::BubbleSort);
434
435 display.reset_to_first();
436 assert_eq!(display.viewed_algorithm, AlgorithmType::BubbleSort);
437 assert_eq!(display.cycle_index, 0);
438 }
439}