use std::collections::HashSet;
use std::env;
use std::fs;
use std::io::Read;
use std::io::Write;
use std::path::Path;

use dbus_codegen::ConnectionType;
use dbus_codegen::GenOpts;

fn main() {
    println!("cargo:rerun-if-env-changed=DBUS_XML_PATH");
    println!("cargo:rerun-if-changed=build.rs");

    let required_files = HashSet::from([
        "org.freedesktop.NetworkManager.AccessPoint.xml",
        "org.freedesktop.NetworkManager.Connection.Active.xml",
        "org.freedesktop.NetworkManager.xml",
        "org.freedesktop.UPower.Device.xml",
        "org.freedesktop.hostname1.xml",
    ]);
    let required_interfaces = required_files
        .iter()
        .map(|name| name.strip_suffix(".xml").unwrap().to_string())
        .collect();

    let opts = GenOpts {
        connectiontype: ConnectionType::Nonblock,
        methodtype: None,
        interfaces: Some(required_interfaces),
        ..Default::default()
    };

    let out_dir_str = env::var("OUT_DIR").unwrap();
    let out_dir = Path::new(&out_dir_str);

    let mut generated_file = fs::File::create(out_dir.join("generated.rs")).unwrap();

    let dbus_paths_raw =
        env::var("DBUS_XML_PATH").unwrap_or("/usr/share/dbus-1/interfaces".to_string());
    fs::create_dir_all(out_dir.join("generated")).unwrap();
    dbus_paths_raw
        .split(':')
        .map(fs::read_dir)
        .flat_map(Result::unwrap)
        .map(Result::unwrap)
        .filter(|file| required_files.contains(file.file_name().to_str().unwrap()))
        .for_each(|in_file| {
            println!(
                "cargo:rerun-if-changed={}",
                in_file.path().to_str().unwrap()
            );

            let mut xml = String::new();
            fs::File::open(in_file.path())
                .unwrap()
                .read_to_string(&mut xml)
                .unwrap();

            let code = dbus_codegen::generate(&xml, &opts).unwrap();

            let module_name = in_file
                .file_name()
                .into_string()
                .unwrap()
                .strip_suffix(".xml")
                .unwrap()
                .replace('.', "_");
            let out_path = out_dir.join(format!("generated/{module_name}.rs"));
            fs::File::create(out_path)
                .unwrap()
                .write_all(code.as_bytes())
                .unwrap();

            generated_file
                .write_all(
                    format!(
                        r#"mod {module_name} {{
                            include!(concat!(env!("OUT_DIR"), "/generated/{module_name}.rs"));
                        }}
                        pub use {module_name}::*;
                        "#
                    )
                    .as_bytes(),
                )
                .unwrap();
        })
}