use std::env;
use std::fs::{read_dir, File};
use std::io::prelude::*;
use std::path::Path;

fn join_file(s: &str, base: Option<&str>) -> String {
    let dir =
        Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join(base.unwrap_or_default());
    dir.join(s).to_string_lossy().to_string()
}

fn join_util(s: &str) -> String {
    join_file(s, Some("util"))
}

fn read_all() -> std::io::Result<impl Iterator<Item = String>> {
    let dir = read_dir(join_util(""))?;
    let iter = dir
        .into_iter()
        .map(|f| f.unwrap().file_name().to_string_lossy().to_string());
    Ok(iter)
}

fn read_list(s: &str) -> std::io::Result<Vec<String>> {
    let hidden_list = join_file(s, None);
    let mut file = File::open(hidden_list)?;
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    Ok(content.split('\n').map(|s| s.trim().to_owned()).collect())
}

fn main() -> std::io::Result<()> {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let dest = Path::new(&out_dir).join("get_all_utils.rs");
    let hidden_list = read_list("hidden_list")?;
    let last_list = read_list("last_list")?;
    let mut file = File::create(dest)?;
    let mut inner = vec![];
    let mut last_inner = vec![];
    for path in read_all()? {
        let mut splited = path.rsplitn(2, '.');
        let ty = splited.next().unwrap();
        let name = splited.next().unwrap();
        let hidden = hidden_list.iter().any(|s| s == name);
        let is_last = last_list.iter().any(|s| s == name);
        let s = format!(
            "
                Util {{
                    name: \"util/{}\",
                    ty: \"{}\",
                    content: std::include_str!(r\"{}\"),
                    is_hidden: {},
                }}
                ",
            name,
            ty,
            join_util(&path),
            hidden
        );
        if is_last {
            last_inner.push(s);
        } else {
            inner.push(s);
        }
    }
    file.write_all(
        b"#[derive(Debug, PartialEq, Eq, Clone, Copy)]
        pub struct Util {
            pub name: &'static str,
            pub ty: &'static str,
            pub content: &'static str,
            pub is_hidden: bool,
        }",
    )?;
    file.write_all(b"pub fn get_all() -> &'static [Util] {\n")?;
    file.write_all(format!("    &[{}, {}]", inner.join(","), last_inner.join(",")).as_bytes())?;
    file.write_all(b"}\n")?;
    Ok(())
}
