2025-04-23 16:34:35 -04:00

246 lines
9.2 KiB
Rust

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<Vec<PathBuf>>,
pub error: Option<String>,
}
pub struct SinfarInstallerApp<> {
pub state: InstallerState,
pub game_type: Option<GameType>,
pub install_path: Option<PathBuf>,
pub ee_exe_path: Option<PathBuf>,
pub download_progress: f32,
pub extraction_progress: f32,
pub download_error: Option<String>,
pub install_error: Option<String>,
pub download_state: std::sync::Arc<std::sync::Mutex<DownloadState>>,
pub eframe_ctx: Option<egui::Context>,
pub runtime: Arc<tokio::runtime::Runtime>
}
impl SinfarInstallerApp<> {
pub fn new(cc: &eframe::CreationContext<>, runtime: Arc<tokio::runtime::Runtime>) -> 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
}
}