//! Do port/starboard twins decode to mirror images of each other?
//!
//! A runtime capture proved the container stores both halves of the `e106` hull
//! as separate, X-reflected buffers. That gives a **capture-free invariant**:
//! a `…_01`/`…_02` pair of equal vertex count should decode to geometry that is
//! an exact X-mirror — never to identical geometry (that is the collapse the
//! distinct-assignment fix targets), and never to something unrelated (that is a
//! mis-anchor no count-based metric can see).
//!
//! Usage: twin_mirror_audit <resource3d_dir>
use sylpheed_formats::mesh::Xbg7Model;
use std::collections::BTreeMap;

fn key(p: [f32; 3]) -> (i64, i64, i64) {
    (
        (p[0] * 1e3).round() as i64,
        (p[1] * 1e3).round() as i64,
        (p[2] * 1e3).round() as i64,
    )
}

/// Elementwise equality with a tolerance. Truncating keys is too strict for a
/// mirrored pair: the halves are authored, not bit-negated, so they differ in
/// the last digits and an exact key test reports them as unrelated.
fn near(p: [f32; 3], q: [f32; 3]) -> bool {
    (0..3).all(|c| (p[c] - q[c]).abs() <= 1e-3 * (1.0 + q[c].abs()))
}

fn main() {
    let dir = std::env::args().nth(1).expect("resource3d dir");
    let mut files: Vec<_> = std::fs::read_dir(&dir)
        .unwrap()
        .flatten()
        .map(|e| e.path())
        .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("xpr"))
        .collect();
    files.sort();

    let (mut same, mut mirrored, mut unrelated, mut related) = (0usize, 0usize, 0usize, 0usize);
    let mut examples: Vec<String> = Vec::new();
    for f in &files {
        let Ok(bytes) = std::fs::read(f) else { continue };
        let models = Xbg7Model::anchor_models_cancellable(&bytes, 0.0, &|| false);
        let by_name: BTreeMap<&str, &Xbg7Model> =
            models.iter().map(|m| (m.name.as_str(), m)).collect();
        for m in &models {
            let Some(stem) = m.name.strip_suffix("_01") else { continue };
            let Some(t) = by_name.get(format!("{stem}_02").as_str()) else { continue };
            let a: Vec<[f32; 3]> = m.meshes.iter().flat_map(|s| s.positions.clone()).collect();
            let b: Vec<[f32; 3]> = t.meshes.iter().flat_map(|s| s.positions.clone()).collect();
            if a.len() != b.len() || a.is_empty() {
                continue;
            }
            let ident = a.iter().zip(&b).all(|(p, q)| near(*p, *q));
            let mirr = a.iter().zip(&b).all(|(p, q)| near([-p[0], p[1], p[2]], *q));
            // A pair that is neither may still be RELATED: mirrored on another
            // axis, or the same point cloud in a different vertex order. Only a
            // pair that is none of these is evidence of a mis-anchor.
            let mirr_y = a.iter().zip(&b).all(|(p, q)| near([p[0], -p[1], p[2]], *q));
            let mirr_z = a.iter().zip(&b).all(|(p, q)| near([p[0], p[1], -p[2]], *q));
            let set = |v: &Vec<[f32; 3]>| {
                let mut s: Vec<_> = v.iter().map(|p| key(*p)).collect();
                s.sort_unstable();
                s
            };
            let same_cloud = set(&a) == set(&b);
            // …and the same point cloud after mirroring, for a pair whose
            // halves are authored in different vertex order.
            let mirrored_cloud = {
                let am: Vec<[f32; 3]> = a.iter().map(|p| [-p[0], p[1], p[2]]).collect();
                set(&am) == set(&b)
            };
            if !ident && !mirr && (mirr_y || mirr_z || same_cloud || mirrored_cloud) {
                related += 1;
                continue;
            }
            // Identical GEOMETRY is only a collapse if it also comes from the
            // same buffer: a container may store a part twice unmirrored, and
            // then two twins on two copies decode identically and correctly.
            let shared_buffer = m.meshes[0].vbuf_offset.is_some()
                && m.meshes[0].vbuf_offset == t.meshes[0].vbuf_offset;
            if ident && !shared_buffer {
                related += 1;
                continue;
            }
            if ident {
                same += 1;
                if examples.len() < 6 {
                    examples.push(format!(
                        "identical: {} / {}_02 in {}",
                        m.name,
                        stem,
                        f.file_name().unwrap().to_string_lossy()
                    ));
                }
            } else if mirr {
                mirrored += 1;
            } else {
                unrelated += 1;
                if examples.len() < 6 {
                    examples.push(format!(
                        "unrelated: {} / {}_02 in {}",
                        m.name,
                        stem,
                        f.file_name().unwrap().to_string_lossy()
                    ));
                }
            }
        }
    }
    println!("twin pairs of equal vertex count: {}", same + mirrored + unrelated + related);
    println!("  exact X-mirror (expected) : {mirrored}");
    println!("  IDENTICAL, one buffer (collapse) : {same}");
    println!("  related other way (Y/Z mirror, reordered): {related}");
    println!("  unrelated  (mis-anchor?)  : {unrelated}");
    for e in examples {
        println!("  e.g. {e}");
    }
}
