1use crate::buffer::{AppMode, BufferAPI};
2use serde_json::Value;
3use std::collections::HashMap;
4use std::time::Instant;
5
6#[derive(Debug, Clone)]
7pub struct ModeState {
8 pub mode: AppMode,
9 pub context: StateContext,
10 pub timestamp: Instant,
11}
12
13#[derive(Debug, Clone, Default)]
14pub struct StateContext {
15 pub input_text: String,
16 pub cursor_position: usize,
17 pub scroll_offset: (usize, usize),
18
19 pub selected_row: Option<usize>,
20 pub selected_column: usize,
21
22 pub search_pattern: Option<String>,
23 pub filter_pattern: Option<String>,
24 pub fuzzy_filter_pattern: Option<String>,
25 pub column_search_pattern: Option<String>,
26
27 pub table_scroll: (usize, usize),
28 pub column_widths: Vec<u16>,
29
30 pub custom_data: HashMap<String, Value>,
31}
32
33pub struct StateManager {
34 mode_stack: Vec<ModeState>,
35 current_context: StateContext,
36 max_stack_size: usize,
37}
38
39impl Default for StateManager {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl StateManager {
46 #[must_use]
47 pub fn new() -> Self {
48 Self {
49 mode_stack: Vec::new(),
50 current_context: StateContext::default(),
51 max_stack_size: 10,
52 }
53 }
54
55 pub fn push_mode(&mut self, new_mode: AppMode, buffer: &mut dyn BufferAPI) {
56 let current_state = ModeState {
57 mode: buffer.get_mode(),
58 context: self.capture_context(buffer),
59 timestamp: Instant::now(),
60 };
61
62 self.mode_stack.push(current_state);
63 if self.mode_stack.len() > self.max_stack_size {
64 self.mode_stack.remove(0);
65 }
66
67 self.transition_to_mode(new_mode, buffer);
68 }
69
70 pub fn pop_mode(&mut self, buffer: &mut dyn BufferAPI) -> bool {
71 if let Some(previous_state) = self.mode_stack.pop() {
72 self.restore_context(&previous_state.context, buffer);
73 buffer.set_mode(previous_state.mode);
74 true
75 } else {
76 buffer.set_mode(AppMode::Command);
77 false
78 }
79 }
80
81 #[must_use]
82 pub fn peek_previous_mode(&self) -> Option<AppMode> {
83 self.mode_stack.last().map(|state| state.mode.clone())
84 }
85
86 pub fn save_current_state(&mut self, buffer: &dyn BufferAPI) {
87 self.current_context = self.capture_context(buffer);
88 }
89
90 pub fn restore_current_state(&self, buffer: &mut dyn BufferAPI) {
91 self.restore_context(&self.current_context, buffer);
92 }
93
94 #[must_use]
95 pub fn get_stack_depth(&self) -> usize {
96 self.mode_stack.len()
97 }
98
99 pub fn clear_stack(&mut self) {
100 self.mode_stack.clear();
101 }
102
103 fn capture_context(&self, buffer: &dyn BufferAPI) -> StateContext {
104 StateContext {
105 input_text: buffer.get_input_text(),
106 cursor_position: buffer.get_input_cursor_position(),
107 scroll_offset: buffer.get_scroll_offset(),
108 selected_row: buffer.get_selected_row(),
109 selected_column: buffer.get_current_column(),
110 search_pattern: if buffer.get_search_pattern().is_empty() {
111 None
112 } else {
113 Some(buffer.get_search_pattern())
114 },
115 filter_pattern: if buffer.get_filter_pattern().is_empty() {
116 None
117 } else {
118 Some(buffer.get_filter_pattern())
119 },
120 fuzzy_filter_pattern: if buffer.get_fuzzy_filter_pattern().is_empty() {
121 None
122 } else {
123 Some(buffer.get_fuzzy_filter_pattern())
124 },
125 column_search_pattern: {
126 None
128 },
129 table_scroll: buffer.get_scroll_offset(),
130 column_widths: Vec::new(), custom_data: HashMap::new(),
132 }
133 }
134
135 fn restore_context(&self, context: &StateContext, buffer: &mut dyn BufferAPI) {
136 buffer.set_input_text(context.input_text.clone());
137 buffer.set_input_cursor_position(context.cursor_position);
138 buffer.set_scroll_offset(context.scroll_offset);
139
140 if let Some(row) = context.selected_row {
141 buffer.set_selected_row(Some(row));
142 }
143 buffer.set_current_column(context.selected_column);
144
145 if let Some(pattern) = &context.search_pattern {
146 buffer.set_search_pattern(pattern.clone());
147 }
148 if let Some(pattern) = &context.filter_pattern {
149 buffer.set_filter_pattern(pattern.clone());
150 }
151 if let Some(pattern) = &context.fuzzy_filter_pattern {
152 buffer.set_fuzzy_filter_pattern(pattern.clone());
153 }
154 if let Some(pattern) = &context.column_search_pattern {
155 }
158 }
159
160 fn transition_to_mode(&self, mode: AppMode, buffer: &mut dyn BufferAPI) {
161 buffer.set_mode(mode.clone());
162
163 match mode {
164 AppMode::Search => {
165 buffer.set_search_pattern(String::new());
166 }
167 AppMode::Filter => {
168 buffer.set_filter_pattern(String::new());
169 }
170 AppMode::FuzzyFilter => {
171 buffer.set_fuzzy_filter_pattern(String::new());
172 buffer.set_fuzzy_filter_active(false);
173 }
174 AppMode::ColumnSearch => {
175 }
178 _ => {}
179 }
180 }
181
182 #[must_use]
183 pub fn format_debug_info(&self) -> String {
184 let mut info = String::from("========== STATE MANAGER ==========\n");
185 info.push_str(&format!("Stack Depth: {}\n", self.mode_stack.len()));
186
187 if !self.mode_stack.is_empty() {
188 info.push_str("\nMode Stack (oldest to newest):\n");
189 for (i, state) in self.mode_stack.iter().enumerate() {
190 info.push_str(&format!(" [{}] {:?}\n", i, state.mode));
191 }
192 }
193
194 info.push_str("\nCurrent Mode Context:\n");
195 info.push_str(&format!(
196 " Input Length: {}\n",
197 self.current_context.input_text.len()
198 ));
199 info.push_str(&format!(
200 " Cursor: {}\n",
201 self.current_context.cursor_position
202 ));
203 info.push_str(&format!(
204 " Selected Row: {:?}\n",
205 self.current_context.selected_row
206 ));
207 info.push_str(&format!(
208 " Selected Column: {}\n",
209 self.current_context.selected_column
210 ));
211
212 info
213 }
214}