//! Build script: parse xenia's xboxkrnl_table.inc and xam_table.inc to generate
//! ordinal->name lookup tables at compile time.

use std::env;
use std::fs;
use std::io::Write;
use std::path::Path;

fn parse_table(path: &Path) -> Vec<(u32, String, String)> {
    let content = match fs::read_to_string(path) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("cargo:warning=could not read {}: {}", path.display(), e);
            return Vec::new();
        }
    };

    let mut entries = Vec::new();
    for line in content.lines() {
        let line = line.trim();
        // XE_EXPORT(module, 0xNNNNNNNN, Name, kType),
        if !line.starts_with("XE_EXPORT(") { continue; }
        let inner = match line.strip_prefix("XE_EXPORT(").and_then(|s| s.strip_suffix("),")) {
            Some(s) => s,
            None => continue,
        };
        let parts: Vec<&str> = inner.splitn(4, ',').map(|s| s.trim()).collect();
        if parts.len() < 4 { continue; }
        let module = parts[0].to_string();
        let ordinal = match u32::from_str_radix(parts[1].trim_start_matches("0x").trim_start_matches("0X"), 16) {
            Ok(n) => n,
            Err(_) => continue,
        };
        let name = parts[2].to_string();
        entries.push((ordinal, name, module));
    }
    entries
}

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest = Path::new(&out_dir).join("ordinals.rs");
    let mut f = fs::File::create(&dest).unwrap();

    // Locate xenia tables relative to the workspace root
    // crates/xenia-analysis/ -> ../../ -> workspace root -> ../xenia-canary/
    let manifest = env::var("CARGO_MANIFEST_DIR").unwrap();
    let workspace_root = Path::new(&manifest).parent().unwrap().parent().unwrap();
    let project_root = workspace_root.parent().unwrap();

    let krnl_path = project_root
        .join("xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_table.inc");
    let xam_path = project_root
        .join("xenia-canary/src/xenia/kernel/xam/xam_table.inc");

    println!("cargo:rerun-if-changed={}", krnl_path.display());
    println!("cargo:rerun-if-changed={}", xam_path.display());

    let krnl = parse_table(&krnl_path);
    let xam = parse_table(&xam_path);

    writeln!(f, "/// Auto-generated from xenia's export tables.").unwrap();
    writeln!(f, "pub fn resolve_ordinal(lib: &str, ordinal: u16) -> Option<&'static str> {{").unwrap();
    writeln!(f, "    match lib {{").unwrap();

    // xboxkrnl.exe
    writeln!(f, "        \"xboxkrnl.exe\" => match ordinal {{").unwrap();
    for (ord, name, _) in &krnl {
        writeln!(f, "            0x{ord:04X} => Some(\"{name}\"),").unwrap();
    }
    writeln!(f, "            _ => None,").unwrap();
    writeln!(f, "        }},").unwrap();

    // xam.xex
    writeln!(f, "        \"xam.xex\" => match ordinal {{").unwrap();
    for (ord, name, _) in &xam {
        writeln!(f, "            0x{ord:04X} => Some(\"{name}\"),").unwrap();
    }
    writeln!(f, "            _ => None,").unwrap();
    writeln!(f, "        }},").unwrap();

    writeln!(f, "        _ => None,").unwrap();
    writeln!(f, "    }}").unwrap();
    writeln!(f, "}}").unwrap();

    eprintln!("ordinals.rs: {} xboxkrnl + {} xam entries", krnl.len(), xam.len());
}
