Skip to main content

gromnie_cli/
app.rs

1use crossterm::event::{self, Event, KeyCode, KeyModifiers};
2use ratatui::prelude::Backend;
3use ratatui::{Frame, Terminal};
4use std::error::Error;
5
6use gromnie_client::config::GromnieConfig;
7
8use super::draw::Draw;
9use super::wizards::{ConfigWizard, ConfigWizardStage, LaunchWizard, WizardStage};
10
11pub struct App {
12    pub app_screen: AppScreen,
13    pub launch_wizard: Option<LaunchWizard>,
14    pub config_wizard: Option<ConfigWizard>,
15}
16
17impl Default for App {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl App {
24    pub fn new() -> Self {
25        Self {
26            app_screen: AppScreen::Config,
27            launch_wizard: None,
28            config_wizard: Some(ConfigWizard::new()),
29        }
30    }
31
32    pub fn new_with_config(config: GromnieConfig) -> Self {
33        Self {
34            app_screen: AppScreen::Launch,
35            launch_wizard: Some(LaunchWizard::new(config)),
36            config_wizard: None,
37        }
38    }
39
40    pub fn draw(&mut self, frame: &mut Frame) {
41        match &self.app_screen {
42            AppScreen::Setup => {
43                let _ = SetupWizard::new().draw(self, frame);
44            }
45            AppScreen::Launch => {
46                if let Some(wizard) = &self.launch_wizard {
47                    let _ = wizard.clone().draw(self, frame);
48                }
49            }
50            AppScreen::Config => {
51                if let Some(wizard) = &self.config_wizard {
52                    let _ = wizard.clone().draw(self, frame);
53                }
54            }
55        }
56    }
57
58    pub fn run(&mut self, terminal: &mut Terminal<impl Backend>) -> Result<(), Box<dyn Error>> {
59        loop {
60            terminal.draw(|frame| self.draw(frame))?;
61
62            // Handle input based on current screen
63            match &self.app_screen {
64                AppScreen::Setup => {
65                    // TODO: Setup wizard input handling
66                    break;
67                }
68                AppScreen::Config => {
69                    if let Some(wizard) = &mut self.config_wizard {
70                        // Auto-advance from Welcome stage
71                        if wizard.stage == ConfigWizardStage::Welcome {
72                            wizard.stage = ConfigWizardStage::EnteringServerName;
73                            continue;
74                        }
75
76                        // Check if wizard is complete
77                        if wizard.stage == ConfigWizardStage::Complete {
78                            break;
79                        }
80
81                        // Poll for keyboard events
82                        if event::poll(std::time::Duration::from_millis(16))?
83                            && let Event::Key(key) = event::read()?
84                        {
85                            match key.code {
86                                KeyCode::Char('c')
87                                    if key.modifiers.contains(KeyModifiers::CONTROL) =>
88                                {
89                                    return Err("Configuration cancelled".into());
90                                }
91                                KeyCode::Char(c) => {
92                                    // Only accept input during text entry stages
93                                    if matches!(
94                                        wizard.stage,
95                                        ConfigWizardStage::EnteringServerName
96                                            | ConfigWizardStage::EnteringServerHost
97                                            | ConfigWizardStage::EnteringServerPort
98                                            | ConfigWizardStage::EnteringAccountUsername
99                                            | ConfigWizardStage::EnteringAccountPassword
100                                    ) {
101                                        wizard.current_input.push(c);
102                                    }
103                                }
104                                KeyCode::Backspace => {
105                                    // Remove last character during text entry
106                                    if matches!(
107                                        wizard.stage,
108                                        ConfigWizardStage::EnteringServerName
109                                            | ConfigWizardStage::EnteringServerHost
110                                            | ConfigWizardStage::EnteringServerPort
111                                            | ConfigWizardStage::EnteringAccountUsername
112                                            | ConfigWizardStage::EnteringAccountPassword
113                                    ) {
114                                        wizard.current_input.pop();
115                                    }
116                                }
117                                KeyCode::Enter => match wizard.stage {
118                                    ConfigWizardStage::EnteringServerName
119                                        if !wizard.current_input.is_empty() =>
120                                    {
121                                        wizard.server_name = wizard.current_input.clone();
122                                        wizard.current_input.clear();
123                                        wizard.stage = ConfigWizardStage::EnteringServerHost;
124                                    }
125                                    ConfigWizardStage::EnteringServerHost
126                                        if !wizard.current_input.is_empty() =>
127                                    {
128                                        wizard.server_host = wizard.current_input.clone();
129                                        wizard.current_input.clear();
130                                        wizard.stage = ConfigWizardStage::EnteringServerPort;
131                                    }
132                                    ConfigWizardStage::EnteringServerPort => {
133                                        wizard.server_port = if wizard.current_input.is_empty() {
134                                            "9000".to_string()
135                                        } else {
136                                            wizard.current_input.clone()
137                                        };
138                                        wizard.current_input.clear();
139                                        wizard.stage = ConfigWizardStage::EnteringAccountUsername;
140                                    }
141                                    ConfigWizardStage::EnteringAccountUsername
142                                        if !wizard.current_input.is_empty() =>
143                                    {
144                                        wizard.account_username = wizard.current_input.clone();
145                                        wizard.current_input.clear();
146                                        wizard.stage = ConfigWizardStage::EnteringAccountPassword;
147                                    }
148                                    ConfigWizardStage::EnteringAccountPassword
149                                        if !wizard.current_input.is_empty() =>
150                                    {
151                                        wizard.account_password = wizard.current_input.clone();
152                                        wizard.current_input.clear();
153                                        wizard.stage = ConfigWizardStage::EnteringCharacterName;
154                                    }
155                                    ConfigWizardStage::EnteringCharacterName => {
156                                        // Allow empty input (skip character config)
157                                        wizard.character_name = wizard.current_input.clone();
158                                        wizard.current_input.clear();
159                                        wizard.stage = ConfigWizardStage::Confirming;
160                                    }
161                                    ConfigWizardStage::Confirming => {
162                                        wizard.stage = ConfigWizardStage::Complete;
163                                    }
164                                    _ => {}
165                                },
166                                KeyCode::Esc => {
167                                    return Err("Configuration cancelled".into());
168                                }
169                                _ => {}
170                            }
171                        }
172                    } else {
173                        break;
174                    }
175                }
176                AppScreen::Launch => {
177                    if let Some(wizard) = &mut self.launch_wizard {
178                        // Auto-advance from Welcome stage
179                        if wizard.stage == WizardStage::Welcome {
180                            std::thread::sleep(std::time::Duration::from_millis(500));
181                            wizard.stage = WizardStage::SelectingServer;
182                            continue;
183                        }
184
185                        // Check if wizard is complete
186                        if wizard.stage == WizardStage::Complete {
187                            break;
188                        }
189
190                        // Poll for keyboard events
191                        if event::poll(std::time::Duration::from_millis(100))?
192                            && let Event::Key(key) = event::read()?
193                        {
194                            match key.code {
195                                KeyCode::Up => match wizard.stage {
196                                    WizardStage::SelectingServer
197                                        if wizard.selected_server_idx > 0 =>
198                                    {
199                                        wizard.selected_server_idx -= 1;
200                                    }
201                                    WizardStage::SelectingAccount
202                                        if wizard.selected_account_idx > 0 =>
203                                    {
204                                        wizard.selected_account_idx -= 1;
205                                    }
206                                    _ => {}
207                                },
208                                KeyCode::Down => match wizard.stage {
209                                    WizardStage::SelectingServer
210                                        if wizard.selected_server_idx
211                                            < wizard.server_list.len() - 1 =>
212                                    {
213                                        wizard.selected_server_idx += 1;
214                                    }
215                                    WizardStage::SelectingAccount
216                                        if wizard.selected_account_idx
217                                            < wizard.account_list.len() - 1 =>
218                                    {
219                                        wizard.selected_account_idx += 1;
220                                    }
221                                    _ => {}
222                                },
223                                KeyCode::Enter => match wizard.stage {
224                                    WizardStage::SelectingServer => {
225                                        wizard.stage = WizardStage::SelectingAccount;
226                                    }
227                                    WizardStage::SelectingAccount => {
228                                        wizard.stage = WizardStage::Confirming;
229                                    }
230                                    WizardStage::Confirming => {
231                                        wizard.stage = WizardStage::Complete;
232                                    }
233                                    _ => {}
234                                },
235                                KeyCode::Esc => {
236                                    return Err("Selection cancelled".into());
237                                }
238                                KeyCode::Char('c')
239                                    if key.modifiers.contains(KeyModifiers::CONTROL) =>
240                                {
241                                    return Err("Selection cancelled".into());
242                                }
243                                _ => {}
244                            }
245                        }
246                    } else {
247                        break;
248                    }
249                }
250            }
251        }
252
253        Ok(())
254    }
255}
256
257pub enum AppScreen {
258    #[allow(dead_code)]
259    Setup,
260    Launch,
261    Config,
262}
263
264struct SetupWizard {}
265
266impl SetupWizard {
267    fn new() -> Self {
268        Self {}
269    }
270}
271
272impl Draw for SetupWizard {
273    fn draw(self, _app: &mut App, frame: &mut Frame) -> Result<(), Box<dyn Error>> {
274        use ratatui::text::Line;
275        use ratatui::widgets::Block;
276
277        let area = frame.area();
278
279        let block = Block::new().title(Line::from("Progress").centered());
280        frame.render_widget(block, area);
281
282        Ok(())
283    }
284}