use crate::aria2_client::TorrentStatus;
use crate::torrent_search::TorrentSearchResult;
use crossterm::event::{self, Event, KeyCode, KeyEvent};
use std::io;
#[derive(Debug, Clone, PartialEq)]
pub enum AppMode {
Normal,
Search,
Results,
Searching,
}
#[derive(Debug)]
pub struct App {
pub mode: AppMode,
pub search_query: String,
pub search_results: Vec<TorrentSearchResult>,
pub active_downloads: Vec<TorrentStatus>,
pub selected_index: usize,
pub should_quit: bool,
pub search_in_progress: bool,
pub status_message: String,
pub selected_category: Option<String>,
pub download_requested: bool,
pub loading_frame: usize,
pub search_progress: String,
}
impl App {
pub fn new() -> Self {
App {
mode: AppMode::Normal,
search_query: String::new(),
search_results: Vec::new(),
active_downloads: Vec::new(),
selected_index: 0,
should_quit: false,
search_in_progress: false,
status_message: "Starting up...".to_string(),
selected_category: None,
download_requested: false,
loading_frame: 0,
search_progress: String::new(),
}
}
pub fn start_search(&mut self) {
self.mode = AppMode::Searching;
self.search_in_progress = true;
self.status_message = "Initializing search across multiple sources...".to_string();
self.search_progress = "Starting search...".to_string();
self.search_results.clear();
self.selected_index = 0;
self.loading_frame = 0;
}
pub fn finish_search(&mut self, results: Vec<TorrentSearchResult>) {
self.search_in_progress = false;
self.search_results = results;
self.mode = AppMode::Results;
self.status_message = format!("Found {} results", self.search_results.len());
self.selected_index = 0;
}
pub fn search_error(&mut self, error: String) {
self.search_in_progress = false;
self.mode = AppMode::Normal;
self.status_message = format!("Search failed: {}", error);
}
pub fn update_loading_animation(&mut self) {
if self.search_in_progress {
self.loading_frame = (self.loading_frame + 1) % 8;
let progress_messages = [
"Connecting to YTS movie database...",
"Searching YTS for movies...",
"Connecting to PirateBay API...",
"Searching PirateBay torrents...",
"Checking 1337x mirrors...",
"Searching 1337x database...",
"Sorting results by seeders...",
"Finalizing search results...",
];
let message_index = (self.loading_frame / 4) % progress_messages.len();
self.search_progress = progress_messages[message_index].to_string();
}
}
pub fn get_loading_indicator(&self) -> &'static str {
const LOADING_FRAMES: &[&str] = &[
"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧"
];
LOADING_FRAMES[self.loading_frame]
}
pub fn handle_input(&mut self) -> io::Result<()> {
if event::poll(std::time::Duration::from_millis(100))? {
if let Event::Key(key) = event::read()? {
match self.mode {
AppMode::Normal => self.handle_normal_mode(key),
AppMode::Search => self.handle_search_mode(key),
AppMode::Results => self.handle_results_mode(key),
AppMode::Searching => self.handle_searching_mode(key),
}
}
}
Ok(())
}
fn handle_normal_mode(&mut self, key: KeyEvent) {
match key.code {
KeyCode::Char('q') => self.should_quit = true,
KeyCode::Char('s') => self.mode = AppMode::Search,
KeyCode::Down | KeyCode::Char('j') => {
if !self.active_downloads.is_empty() {
self.selected_index = (self.selected_index + 1) % self.active_downloads.len();
}
}
KeyCode::Up | KeyCode::Char('k') => {
if !self.active_downloads.is_empty() {
self.selected_index = if self.selected_index == 0 {
self.active_downloads.len() - 1
} else {
self.selected_index - 1
};
}
}
_ => {}
}
}
fn handle_search_mode(&mut self, key: KeyEvent) {
match key.code {
KeyCode::Esc => {
self.mode = AppMode::Normal;
self.search_query.clear();
}
KeyCode::Enter => {
if !self.search_query.is_empty() {
self.start_search();
}
}
KeyCode::Backspace => {
self.search_query.pop();
}
KeyCode::Char(c) => {
self.search_query.push(c);
}
_ => {}
}
}
fn handle_results_mode(&mut self, key: KeyEvent) {
match key.code {
KeyCode::Esc => {
self.mode = AppMode::Normal;
self.search_results.clear();
self.selected_index = 0;
}
KeyCode::Enter => {
if !self.search_results.is_empty() {
self.download_requested = true;
}
}
KeyCode::Down | KeyCode::Char('j') => {
if !self.search_results.is_empty() {
self.selected_index = (self.selected_index + 1) % self.search_results.len();
}
}
KeyCode::Up | KeyCode::Char('k') => {
if !self.search_results.is_empty() {
self.selected_index = if self.selected_index == 0 {
self.search_results.len() - 1
} else {
self.selected_index - 1
};
}
}
_ => {}
}
}
fn handle_searching_mode(&mut self, key: KeyEvent) {
match key.code {
KeyCode::Esc => {
self.search_in_progress = false;
self.mode = AppMode::Normal;
self.status_message = "Search cancelled".to_string();
}
_ => {
}
}
}
}