use cmake::Config;
use copy_dir::copy_dir;
use std::{
    env,
    path::{Path, PathBuf},
    process::Command,
};

pub struct Build {
    source_dir: PathBuf,
    out_dir: PathBuf,
}

pub struct Artifacts {
    include_dir: PathBuf,
    lib_dir: PathBuf,
    lib: String,
}

impl Build {
    pub fn new() -> Build {
        Build {
            source_dir: Path::new(env!("CARGO_MANIFEST_DIR")).join("boolector"),
            out_dir: PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR not set"))
                .join("vendor-build"),
        }
    }

    pub fn prerequisites(&mut self) -> &mut Self {
        // it is not allowed to modify files outside of OUT_DIR,
        // so everything has to be copied to OUT_DIR before the build can be started
        if !self.out_dir.exists() {
            copy_dir(&self.source_dir, &self.out_dir)
                .expect("Unable to copy Boolector sources to OUT_DIR");
        }

        if !self.out_dir.join("deps/install/lib/liblgl.a").exists() {
            self.run_command(
                Command::new("/usr/bin/env")
                    .arg("bash")
                    .arg(self.out_dir.join("contrib/setup-lingeling.sh"))
                    .current_dir(&self.out_dir),
                "Setup Lingeling",
            );
        }

        println!(
            "cargo:rustc-link-search=native={}",
            self.out_dir.join("deps/install/lib").display()
        );
        println!("cargo:rustc-link-lib=static=lgl");
        println!(
            "cargo:include={}",
            self.out_dir.join("deps/install/include").display()
        );
        println!(
            "cargo:lib={}",
            self.out_dir.join("deps/install/lib").display()
        );

        if !self
            .out_dir
            .join("deps/install/lib/libbtor2parser.a")
            .exists()
        {
            self.run_command(
                Command::new("/usr/bin/env")
                    .arg("bash")
                    .arg(self.out_dir.join("contrib/setup-btor2tools.sh"))
                    .current_dir(&self.out_dir),
                "Setup btor2tools",
            );
        }

        self
    }

    pub fn build(&mut self) -> Artifacts {
        // TODO: Unfortunately BUILDDIR is not overwriteable either implement
        // whole `Configure.sh` or find out how to overwrite `$BUILDDIR`
        let target_dir = Config::new(&self.out_dir).build();

        Artifacts {
            lib_dir: target_dir.join("lib"),
            include_dir: target_dir.join("include"),
            lib: "boolector".to_string(),
        }
    }

    fn run_command(&self, command: &mut Command, desc: &str) {
        println!("running {:?}", command);
        let status = command.status().unwrap();
        if !status.success() {
            panic!(
                "
Error {}:
    Command: {:?}
    Exit status: {}
    ",
                desc, command, status
            );
        }
    }
}

impl Artifacts {
    pub fn include_dir(&self) -> &Path {
        &self.include_dir
    }

    pub fn lib_dir(&self) -> &Path {
        &self.lib_dir
    }

    pub fn lib(&self) -> &String {
        &self.lib
    }

    pub fn print_cargo_metadata(&self) {
        println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
        println!("cargo:rustc-link-lib=static={}", self.lib);
        println!("cargo:include={}", self.include_dir.display());
        println!("cargo:lib={}", self.lib_dir.display());
    }
}
