use std::fs;

use tempfile::NamedTempFile;

use crate::{env::BuildEnv, error::Error, util::run_external_command};

const DTC_ARCHITECTURES: &[&str] = &["aarch64", "riscv64"];

fn find_all_device_trees(env: &BuildEnv) -> Vec<String> {
    let mut res = vec![];
    for &arch in DTC_ARCHITECTURES {
        let Ok(dir) = fs::read_dir(env.workspace_root.join("etc/dtb").join(arch)) else {
            continue;
        };

        for entry in dir {
            let Ok(entry) = entry else {
                continue;
            };
            let Ok(file_type) = entry.file_type() else {
                continue;
            };
            if !file_type.is_file() {
                continue;
            }
            let name = entry.file_name();
            let Some(name) = name.to_str() else {
                continue;
            };
            if !name.ends_with(".dts") {
                continue;
            }
            res.push(format!("etc/dtb/{arch}/{name}"));
        }
    }
    res
}

pub fn build_device_tree(env: &BuildEnv, name: &str) -> Result<String, Error> {
    let Some((head, filename)) = name.rsplit_once("/") else {
        log::error!("Invalid device tree path: {name:?}");
        return Err(Error::UsageError);
    };
    let Some(filename) = filename.strip_suffix(".dts") else {
        log::error!("Invalid device tree filename: {name:?}");
        return Err(Error::UsageError);
    };
    let Some((_, arch)) = head.rsplit_once("/") else {
        log::error!("Invalid device tree path: {name:?}");
        return Err(Error::UsageError);
    };
    if !DTC_ARCHITECTURES.contains(&arch) {
        log::error!("Unexpected dtb architecture {arch:?}: {name:?}");
        return Err(Error::UsageError);
    }
    log::info!("Building device tree: {arch}/{filename}.dts");
    let cpp_output = NamedTempFile::new()?.into_temp_path();
    // let _ = env;
    let output = format!("target/{arch}-unknown-none/dtb/{filename}.dtb");
    fs::create_dir_all(format!("target/{arch}-unknown-none/dtb"))?;
    run_external_command(
        "cpp",
        [
            "-undef",
            "-x",
            "assembler-with-cpp",
            &format!("-I{}/etc/dtb", env.workspace_root.display()),
            name,
            &format!("{}", cpp_output.display()),
        ],
        false,
    )?;
    run_external_command(
        "dtc",
        [
            "-O",
            "dtb",
            "-o",
            &output,
            &format!("{}", cpp_output.display()),
        ],
        false,
    )?;
    drop(cpp_output);

    Ok(output)
}

pub fn build_device_trees(env: &BuildEnv) -> Result<(), Error> {
    let device_trees = find_all_device_trees(env);
    for device_tree in device_trees {
        build_device_tree(env, &device_tree)?;
    }
    Ok(())
}
