use eframe::{egui, Frame}; use std::path::PathBuf; use std::sync::Arc; use crate::installer::validator::*; use crate::installer::downloader::DownloadManager; use crate::ui::download_progress::download_progress; use crate::ui::game_selection::game_selection; use crate::ui::installation_progress::installation_progress; use crate::ui::path_selection::path_selection; #[derive(Debug, Clone, PartialEq, Eq)] pub enum GameType { Diamond, EnhancedEdition, } #[derive(Debug, Clone, PartialEq)] pub enum InstallerState { GameSelection, PathSelection, Download, Installing, Complete, } #[derive(Default)] pub struct DownloadState { pub progress: f32, pub completed: bool, pub files: Option>, pub error: Option, } pub struct SinfarInstallerApp<> { pub state: InstallerState, pub game_type: Option, pub install_path: Option, pub ee_exe_path: Option, pub download_progress: f32, pub extraction_progress: f32, pub download_error: Option, pub install_error: Option, pub download_state: std::sync::Arc>, pub eframe_ctx: Option, pub runtime: Arc } impl SinfarInstallerApp<> { pub fn new(cc: &eframe::CreationContext<>, runtime: Arc) -> Self { Self { state: InstallerState::GameSelection, game_type: Some(GameType::Diamond), install_path: None, ee_exe_path: None, download_progress: 0.0, extraction_progress: 0.0, download_error: None, install_error: None, download_state: std::sync::Arc::new(std::sync::Mutex::new(DownloadState::default())), eframe_ctx: None, runtime, // Store the runtime } } fn next_state(&mut self) { self.state = match self.state { InstallerState::GameSelection => InstallerState::PathSelection, InstallerState::PathSelection => InstallerState::Download, InstallerState::Download => InstallerState::Installing, InstallerState::Installing => InstallerState::Complete, InstallerState::Complete => return, }; } fn previous_state(&mut self) { self.state = match self.state { InstallerState::GameSelection => return, InstallerState::PathSelection => InstallerState::GameSelection, InstallerState::Download => InstallerState::PathSelection, InstallerState::Installing => return, // Can't go back during installation InstallerState::Complete => return, }; } } impl<> eframe::App for SinfarInstallerApp<> { fn update(&mut self, ctx: &egui::Context, _frame: &mut Frame) { self.eframe_ctx = Some(ctx.clone()); if self.state == InstallerState::Download { if let Ok(state) = self.download_state.lock() { self.download_progress = state.progress; // if state.completed { // if let Some(files) = &state.files { // // Store the files for installation // //self.next_state(); // Proceed to installation // //self.start_installation(); // } // } if let Some(error) = &state.error { self.download_error = Some(error.clone()); } } } egui::CentralPanel::default().show(ctx, |ui| { ui.vertical_centered(|ui| { ui.heading("Sinfar NWN Custom Content Installer"); ui.add_space(10.0); match self.state { InstallerState::GameSelection => { game_selection::render(ui, self); }, InstallerState::PathSelection => { path_selection::render(ui, self); }, InstallerState::Download => { download_progress::render(ui, self); }, InstallerState::Installing => { installation_progress::render(ui, self); }, InstallerState::Complete => { ui.label("Installation complete!"); ui.label("Launch the game from the desktop shortcut to start playing."); if ui.button("Close").clicked() { std::process::exit(0); } }, } }); ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| { ui.horizontal(|ui| { let can_continue = match self.state { InstallerState::GameSelection => self.game_type.is_some(), InstallerState::PathSelection => { match self.game_type { Some(GameType::Diamond) => validator::validate_diamond_path(self.install_path.as_ref().unwrap()), Some(GameType::EnhancedEdition) =>{ validator::validate_ee_path(self.install_path.as_ref().unwrap()) && validator::validate_ee_exe_path(self.ee_exe_path.as_ref().unwrap()) }, None => false, } }, _ => true, }; if self.state != InstallerState::Installing && self.state != InstallerState::Complete { if ui.add_enabled(can_continue, egui::Button::new("Next")).clicked() { self.next_state(); if self.state == InstallerState::Download { self.start_download(); } else if self.state == InstallerState::Installing { self.start_installation(); } } if self.state != InstallerState::GameSelection { if ui.button("Back").clicked() { self.previous_state(); } } } }); }); }); } } // Implementation of key installer functions impl SinfarInstallerApp<> { pub fn start_download(&mut self) { // let temp_dir = std::env::temp_dir().join("sinfar_installer"); // println!("{}", temp_dir.display()) // Reset progress and errors self.download_progress = 0.0; self.download_error = None; // Initialize download state let download_state = std::sync::Arc::new(std::sync::Mutex::new(DownloadState::default())); self.download_state = download_state.clone(); // Clone the game type for the async closure let game_type = self.game_type.clone().unwrap(); // Create a context handle for requesting UI updates let ctx = eframe::egui::Context::clone(self.eframe_ctx.as_ref().unwrap()); let repaint_ctx_progress = ctx.clone(); let repaint_ctx_complete = ctx.clone(); // Spawn the download task self.runtime.spawn(async move { // Create a download manager with progress callback let progress_state = download_state.clone(); let manager = DownloadManager::new(move |progress, _file| { // Update progress through shared state if let Ok(mut state) = progress_state.lock() { state.progress = progress; repaint_ctx_progress.request_repaint(); } }); // Start the download process match manager.download_all_files(&game_type).await { Ok(files) => { // Mark download as complete with the files if let Ok(mut state) = download_state.lock() { state.completed = true; state.files = Some(files); repaint_ctx_complete.request_repaint(); } } Err(e) => { // Record the error if let Ok(mut state) = download_state.lock() { state.error = Some(format!("Download failed: {}", e)); ctx.request_repaint(); } } } }); } pub fn start_installation(&mut self) { // This would be spawned in a separate thread //let game_type = self.game_type.clone().unwrap(); //let install_path = self.install_path.clone().unwrap(); //let ee_exe_path = self.ee_exe_path.clone(); // Implementation with sevenz-rust would go here // Update self.extraction_progress as files are extracted } }