use std::collections::{HashMap, HashSet};
use std::env;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::process;

use lddtree::{DependencyAnalyzer, Library};

fn main() -> Result<(), Box<dyn Error>> {
    let mut args = env::args().skip(1);
    if let Some(pathname) = args.next() {
        let root = args
            .next()
            .map(|s| PathBuf::from(&s))
            .unwrap_or_else(|| PathBuf::from("/"));
        let lib_paths = args.map(|s| PathBuf::from(&s)).collect();
        let analyzer = DependencyAnalyzer::new(root).library_paths(lib_paths);
        let deps = analyzer.analyze(pathname)?;
        if let Some(interp) = deps.interpreter {
            // The interpreter is keyed by its soname (basename) in `libraries`
            let interp_name = Path::new(&interp)
                .file_name()
                .and_then(|name| name.to_str())
                .unwrap_or(&interp);
            if let Some(path) = deps
                .libraries
                .get(interp_name)
                .and_then(|lib| lib.realpath.as_ref())
            {
                println!("{} => {}", interp, path.display());
            } else {
                println!("{} => not found", interp);
            }
        }
        for needed in deps.needed {
            print_library(&needed, &deps.libraries, 0, HashSet::new());
        }
    } else {
        eprintln!("USAGE: lddtree <pathname> [root] [library path...]");
        process::exit(1);
    }
    Ok(())
}

fn print_library(
    name: &str,
    libraries: &HashMap<String, Library>,
    level: usize,
    mut history: HashSet<PathBuf>,
) {
    let padding = " ".repeat(level);
    if let Some(lib) = libraries.get(name) {
        if let Some(path) = lib.realpath.as_ref() {
            let looping = !history.insert(path.to_path_buf());
            let loop_annotation = if looping { " (DEPENDENCY CYCLE)" } else { "" };
            println!(
                "{}{} => {}{}",
                padding,
                name,
                path.display(),
                loop_annotation
            );
            if looping {
                return;
            };
        } else {
            println!("{}{} => not found", padding, name);
        }

        for needed in &lib.needed {
            print_library(needed, libraries, level + 4, history.clone());
        }
    }
}
