//! Global "which resource has N vertices?" index over every `.xpr` container in
//! an extracted `resource3d` directory, answered for the vcounts a capture log
//! actually drew.
//!
//! Companion to `invert_capture`: that one asks the question inside a single
//! stage container, this one asks it across ALL containers — so a draw whose
//! geometry lives in `Common.xpr`, a `rou_*` weapon pack or a `BG_*` backdrop is
//! still identified instead of coming back "no resource".
//!
//! Usage:
//!   cargo run --release --example vcount_index -- <resource3d_dir> <capture.log> [top_n]
//!   cargo run --release --example vcount_index -- <resource3d_dir> --vcounts 10891,6000

use sylpheed_formats::mesh::{xbg7_resource_names, Xbg7Model};
use sylpheed_formats::ship_capture::{parse_capture, parse_drawlog};
use std::collections::{HashMap, HashSet};

fn main() {
    let args: Vec<String> = std::env::args().collect();
    if args.len() < 3 {
        eprintln!("usage: vcount_index <resource3d_dir> <capture.log|--vcounts a,b,c> [top_n]");
        std::process::exit(2);
    }
    let dir = &args[1];

    // Which vertex counts are we asking about, and how often was each drawn?
    let mut draw_count: HashMap<u32, usize> = HashMap::new();
    let mut bufs: HashMap<u32, HashSet<u32>> = HashMap::new();
    if args[2] == "--vcounts" {
        for v in args[3].split(',').filter_map(|s| s.trim().parse::<u32>().ok()) {
            draw_count.insert(v, 0);
        }
    } else {
        let text = std::fs::read_to_string(&args[2]).expect("read log");
        let mut draws = parse_capture(&text);
        if draws.is_empty() {
            draws = parse_drawlog(&text);
        }
        eprintln!("parsed {} draws", draws.len());
        for d in &draws {
            *draw_count.entry(d.vcount).or_default() += 1;
            bufs.entry(d.vcount).or_default().insert(d.vbase);
        }
    }
    let top_n: usize = args.get(3).and_then(|s| s.parse().ok()).unwrap_or(usize::MAX);

    // Decode every container once; keep only the vcount → names mapping.
    let mut by_vcount: HashMap<u32, Vec<String>> = HashMap::new();
    let mut files: Vec<std::path::PathBuf> = std::fs::read_dir(dir)
        .expect("read dir")
        .filter_map(|e| e.ok().map(|e| e.path()))
        .filter(|p| p.extension().is_some_and(|e| e == "xpr"))
        .collect();
    files.sort();
    let mut total_res = 0usize;
    for f in &files {
        let Ok(bytes) = std::fs::read(f) else { continue };
        let names = xbg7_resource_names(&bytes);
        if names.is_empty() {
            continue;
        }
        let want: HashSet<String> = names.iter().cloned().collect();
        let models = Xbg7Model::models_named(&bytes, &want, &|| false);
        let container = f.file_stem().unwrap().to_string_lossy().to_string();
        for m in &models {
            total_res += 1;
            let whole: usize = m.meshes.iter().map(|s| s.positions.len()).sum();
            by_vcount.entry(whole as u32).or_default().push(format!("{container}:{}", m.name));
            if m.meshes.len() > 1 {
                for (i, s) in m.meshes.iter().enumerate() {
                    by_vcount
                        .entry(s.positions.len() as u32)
                        .or_default()
                        .push(format!("{container}:{}#{i}", m.name));
                }
            }
        }
    }
    eprintln!("indexed {} resources from {} containers", total_res, files.len());

    let mut vcounts: Vec<u32> = draw_count.keys().copied().collect();
    vcounts.sort_unstable_by(|a, b| b.cmp(a));
    println!("\nvcount  draws  bufs   resources anywhere in resource3d/");
    for v in vcounts.into_iter().take(top_n) {
        let n = draw_count[&v];
        let b = bufs.get(&v).map(|s| s.len()).unwrap_or(0);
        let hit = by_vcount.get(&v).cloned().unwrap_or_default();
        let label = if hit.is_empty() {
            "— NONE".to_string()
        } else {
            let mut h = hit.clone();
            h.sort();
            let shown = h.len().min(8);
            format!("{}{}", h[..shown].join(", "), if h.len() > shown { format!(", … ({} total)", h.len()) } else { String::new() })
        };
        println!("{v:6}  {n:5}  {b:4}   {label}");
    }
}
