use std::env;
use std::fs::read_dir;
use std::path::{Path, PathBuf};
use std::process::Command;

use failure::Error;

pub fn find_lib_path() -> Result<PathBuf, Error> {
    let directories = collect_possible_directories();

    if directories.is_empty() {
        bail!("Unable to find possible LLVM shared lib locations.");
    }

    for directory in &directories {
        if let Some(library) = find_library_in_directory(directory) {
            return Ok(library);
        }
    }

    bail!(
        "Unable to find LLVM shared lib in possible locations:\n- {}",
        directories
            .into_iter()
            .map(|item| item.to_str().unwrap().to_owned())
            .collect::<Vec<_>>()
            .join("\n- ")
    );
}

fn collect_possible_directories() -> Vec<PathBuf> {
    let mut paths = vec![];
    let separator = if cfg!(windows) { ';' } else { ':' };

    if let Ok(lib_paths) = env::var("LD_LIBRARY_PATH") {
        for item in lib_paths.split(separator) {
            paths.push(PathBuf::from(item));
        }
    }

    if let Ok(lib_paths) = env::var("DYLD_FALLBACK_LIBRARY_PATH") {
        for item in lib_paths.split(separator) {
            paths.push(PathBuf::from(item));
        }
    }

    if let Ok(bin_paths) = env::var("PATH") {
        for item in bin_paths.split(separator) {
            let mut possible_path = PathBuf::from(item);

            possible_path.pop();
            possible_path.push("lib");
            paths.push(possible_path);
        }
    }

    paths
}

fn find_library_in_directory(directory: &Path) -> Option<PathBuf> {
    match read_dir(directory) {
        Ok(files) => files
            .filter_map(Result::ok)
            .find(|file| file.file_name().to_string_lossy().starts_with("libLLVM"))
            .map(|file| file.path()),

        Err(_) => None,
    }
}
