pub mod terminal {
    use tokio::time::{self, Duration};
    use std::io::{self, stdout, Write};
    use std::future::Future;
    use crossterm::{
        cursor::{Hide, Show},
        ExecutableCommand,
    };

    #[derive(Debug, Clone, Copy, PartialEq)]
    pub enum AnimationMode {
        Rotate,
        Bounce,
        Bounce2,
        Loading,
    }

    pub struct ProgressRing {
        pub total_steps: usize,
        pub current_step: usize,
        pub is_running: bool,
        pub symbols: Vec<&'static str>,
        pub width: usize,
        pub display_char: char,
    }

    impl ProgressRing {
        pub fn new(total_steps: usize, width: usize, rotate_symbols: Option<Vec<&'static str>>, display_char: Option<char>) -> Self {
            ProgressRing {
                total_steps,
                current_step: 0,
                is_running: false,
                symbols: rotate_symbols.unwrap_or_else(|| vec!["|", "/", "-", "\\"]),
                width,
                display_char: display_char.unwrap_or('*'),
            }
        }

        pub fn start(&mut self) {
            self.is_running = true;
            self.current_step = 0;
        }

        pub fn stop(&mut self, message: &str) {
            print!("\r{}", " ".repeat(self.width));
            println!("\r{}", message);
            io::stdout().flush().unwrap();
            self.is_running = false;
        }

        pub fn update_display(&mut self, mode: AnimationMode) {
            print!("\r{}", " ".repeat(self.width));
            print!("\r");
            match mode {
                AnimationMode::Rotate => {
                    let symbol = self.symbols[self.current_step % self.symbols.len()];
                    print!("{}", symbol);
                },
                AnimationMode::Bounce | AnimationMode::Bounce2 => {
                    let position = self.current_step % (2 * self.width - 2);
                    let position = if position >= self.width {
                        2 * self.width - 2 - position
                    } else {
                        position
                    };
                    print!("{}{}", " ".repeat(position), self.display_char);
                },
                AnimationMode::Loading => {
                    let dots = ".".repeat(self.current_step % 4 + 1);
                    print!("Loading{}", dots);
                },
            }
            io::stdout().flush().unwrap();
            self.current_step += 1;
        }

        pub async fn run_with_animation<Fut>(ring: &mut ProgressRing, mode: AnimationMode, task: Fut)
        where
            Fut: Future<Output = ()>,
        {
            stdout().execute(Hide).expect("Error hiding cursor!");
            ring.start();
            tokio::select! {
                _ = async {
                    while ring.is_running && ring.current_step < ring.total_steps {
                        ring.update_display(mode);
                        time::sleep(Duration::from_millis(100)).await;
                    }
                } => {},
                _ = task => {},
            }
            ring.stop("Task completed...");
            stdout().execute(Show).expect("Error showing cursor!");
        }
    }
}
