use crate::acns::tor::torrc::TorrcGenerator;
use crate::acns::ACNError;
use std::io::{BufRead, BufReader};
use std::process::{Child, Command, Stdio};

#[derive(Debug)]
pub struct TorRunner {
    process: Box<Child>,
}

impl TorRunner {
    /// run Tor at the given tor_path with the given torrc
    pub fn run(torrc: TorrcGenerator, torrc_path: &str, tor_path: &str, tor_data_dir: &str) -> Result<TorRunner, ACNError> {
        match torrc.build(torrc_path) {
            Ok(()) => {
                let child = Command::new(tor_path)
                    .arg("-f")
                    .arg(torrc_path)
                    .arg("--DataDirectory")
                    .arg(tor_data_dir)
                    .stdout(Stdio::piped())
                    .spawn()
                    .unwrap();
                Ok(TorRunner { process: Box::new(child) })
            }
            Err(e) => Err(e),
        }
    }

    pub fn wait_until_bootstrapped(&mut self) {
        let child_stdout = self.process.stdout.as_mut().unwrap();
        let mut reader = BufReader::new(child_stdout);
        loop {
            let mut line = String::new();
            reader.read_line(&mut line);
            if line.is_empty() == false {
                if line.contains("Bootstrapped 100% (done): Done") {
                    return;
                }
            }
        }
    }
}

/// Kill Tor when the TorRunner drops out of scope
impl Drop for TorRunner {
    fn drop(&mut self) {
        match self.process.kill() {
            Ok(()) => eprintln!("Killing Tor...{:?}", self.process.wait()),
            Err(_err) => eprintln!("Could not Kill Tor..."),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::acns::tor::run::TorRunner;
    use crate::acns::tor::torrc::TorrcGenerator;
    use std::fs::remove_file;
    use std::thread::sleep;
    use std::time::Duration;

    #[test]
    fn test_run_tor() {
        match TorRunner::run(TorrcGenerator::new().with_socks_port(9055), "./torrc", "/usr/sbin/tor") {
            Ok(runner) => {
                sleep(Duration::new(5, 0));
                assert!(remove_file("./torrc").is_ok());
                println!("Runner {:?}", runner)
            }
            _ => panic!("tor did not run"),
        }
        sleep(Duration::new(10, 0));
    }
}
