// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2025 lacklustr@protonmail.com https://github.com/eadf

use crate::common::VertexIndex;
use crate::common::macros::{integrity_assert, integrity_assert_eq, integrity_println};
use crate::corner_table::TriangleIndex;
use crate::{
    corner_table::CornerIndex, isotropic_remesh::IsotropicRemeshAlgo,
    isotropic_remesh::collapse_edges::CollapseData,
};
#[allow(unused_imports)]
use rayon::prelude::*;
use std::fmt::Debug;
use vector_traits::num_traits::AsPrimitive;
use vector_traits::prelude::SimdUpgradable;

impl<S, V, const ENABLE_UNSAFE: bool> IsotropicRemeshAlgo<S, V, ENABLE_UNSAFE>
where
    S: crate::common::sealed::ScalarType,
    f64: AsPrimitive<S>,
    V: Debug + Copy + From<[S; 3]> + Into<[S; 3]> + Sync + 'static,
{
    pub(crate) fn collapse_data_from_c0p(&self, c0p: CornerIndex) -> CollapseData {
        integrity_assert!(!self.corner_table.is_corner_deleted(c0p));

        let c0 = self.corner_table.next(c0p);
        CollapseData {
            c0,
            vic0: self.corner_table.vertex(c0),
            c0p,
            vic0p: self.corner_table.vertex(c0p),
            c0_fan: self.corner_table.ccw_vertex_fan(c0),
        }
    }

    pub(crate) fn collapse_edges_dihedral(&mut self, collapse_threshold_sq: S) -> bool {
        let original_num_triangles = self.corner_table.active_triangles();
        self.dirty_vertices.prepare(self.vertices.len());

        integrity_println!(
            "####### collapse_edges_dihedral length threshold:{}",
            collapse_threshold_sq.sqrt()
        );
        integrity_println!("####    collapse_edges_dihedral: building candidate list");

        //#[cfg(feature = "integrity_check")]
        //self.dump_status("pre collapse_short_edges()");

        // 1. Identify all short edges
        let mut edges_to_collapse =
            self.identify_collapse_candidates_dihedral(collapse_threshold_sq);

        // 2. Sort edges by length (shortest first) for better collapse order
        edges_to_collapse.sort_unstable_by(|a, b| {
            (a.3, a.4)
                .partial_cmp(&(b.3, b.4))
                .unwrap_or(std::cmp::Ordering::Equal)
        });

        #[cfg(feature = "integrity_check")]
        if edges_to_collapse.len() > 2 {
            let last = edges_to_collapse.len().saturating_sub(1);
            debug_assert!(
                edges_to_collapse[0].3 <= edges_to_collapse[last].3,
                "first {:?} >= last {:?}",
                edges_to_collapse[0].3,
                edges_to_collapse[last].3
            );
        }
        integrity_println!(
            "(reverse) sorted candidate edges: {:?}",
            edges_to_collapse
                .iter()
                .filter_map(
                    |(c, _v0, _vr, l, _)| (!self.corner_table.is_corner_deleted(*c))
                        .then_some((self.dbg_edge(*c), l))
                )
                .collect::<Vec<_>>()
        );

        // 3. Process edge collapses
        while let Some((cp, v0, vr, _ln_sq, _)) = edges_to_collapse.pop() {
            if self.corner_table.is_corner_deleted(cp) || v0 != self.corner_table.vertex(cp) {
                continue;
            }

            let c = self.corner_table.next(cp);
            if vr != self.corner_table.vertex(c) {
                continue;
            }

            integrity_println!(
                "####    collapse_edges_dihedral: checking candidate R:{} V₀:{} len²:{}",
                self.corner_table
                    .data
                    .dbg_corner(self.corner_table.next(cp)),
                self.corner_table.data.dbg_corner(cp),
                _ln_sq
            );

            /*#[rustfmt::skip]
            #[cfg(feature = "integrity_check")]
            let _dbg = if cp.0 == 0
            {
                integrity_println!("collapse_edges_dihedral: hello R:{} V₀:{}", self.corner_table.data.dbg_corner(self.corner_table.next(cp)), self.corner_table.data.dbg_corner(cp));
                true
            } else { false };*/

            // Check if collapse is topologically valid
            if let Some(collapse_data) =
                self.ce_dihedral_can_still_collapse_edge(self.collapse_data_from_c0p(cp))
            {
                #[cfg(feature = "integrity_check")]
                let (v0, vr) = (collapse_data.vic0p, collapse_data.vic0);

                // Perform the collapse
                self.collapse_manifold_edge(collapse_data);

                #[cfg(feature = "integrity_check")]
                self.check_mesh_integrity(
                    format!("after manifold edge collapse {:?}-{:?}", v0, vr).as_str(),
                )
                .unwrap();
            }
        }

        // 4. Remove degenerate triangles and update mesh
        // self.remove_degenerate_triangles(&triangles_to_remove);

        // 5. Compact vertices and update indices
        // self.compact_vertices(&vertex_mapping);
        //println!("collapse_short_edges() finished. Changes made:{}", original_num_triangles != self.corner_table.active_triangles());

        original_num_triangles != self.corner_table.active_triangles()
    }

    /// creates a list of (CornerId,S) where S is the length squared between `V(c)`->`V(c.next)`
    fn identify_collapse_candidates_dihedral(
        &self,
        threshold_sq: S,
    ) -> Vec<(CornerIndex, VertexIndex, VertexIndex, S, u64)> {
        // Step 1: Parallel edge detection with duplicate elimination
        let triangle_count = self.corner_table.total_triangle_count();

        let candidate_edges: Vec<_> = (0..triangle_count)
            .into_par_iter()
            .filter(|&t| !self.corner_table.is_triangle_deleted(TriangleIndex(t)))
            .flat_map(|triangle_idx| {
                let base = triangle_idx * 3;
                let mut local_edges = Vec::new();

                for c0p in (0..3).map(|i| CornerIndex(base + i)) {
                    let c0 = self.corner_table.next(c0p);

                    let vic0p = self.corner_table.vertex(c0p);
                    let vic0 = self.corner_table.vertex(c0);

                    // Only process edge in one direction to avoid duplicates
                    if vic0p.0 < vic0.0 {
                        // Canonical ordering
                        let l_sq = self.edge_length_sq(vic0p, vic0);

                        if l_sq < threshold_sq
                            && let Some((best_c0p, best_v0, best_v1)) =
                                self.get_safe_collapse_direction_dihedral(c0p)
                        {
                            integrity_println!(
                                "Added corner {best_c0p:?} {l_sq} vs {threshold_sq}"
                            );
                            local_edges.push((
                                best_c0p,
                                best_v0,
                                best_v1,
                                l_sq,
                                self.corner_table.canonical_edge_hash(best_c0p),
                            ));
                        }
                    }
                }
                local_edges
            })
            .collect();

        candidate_edges
    }

    /// figure out if it's safe to collapse `vc0p`->`vc0` or `vc0`->`vc0p`
    /// Method returns Some(c) if it is safe to collapse edge `V(c)` -> `V(c.next)`
    /// (effectively deleting `V(c.next))`
    fn get_safe_collapse_direction_dihedral(
        &self,
        c0p: CornerIndex,
    ) -> Option<(CornerIndex, VertexIndex, VertexIndex)> {
        let cd = self.collapse_data_from_c0p(c0p);
        let (c0p_flip, _c0_flip) = self
            .corner_table
            .next_prev(self.corner_table.opposite(self.corner_table.prev(c0p)));

        let cd_flip = self.collapse_data_from_c0p(c0p_flip);

        integrity_assert_eq!(
            self.corner_table.vertex(cd.c0),
            self.corner_table.vertex(c0p_flip)
        );
        integrity_assert_eq!(
            self.corner_table.vertex(cd.c0),
            self.corner_table.vertex(cd_flip.c0p)
        );
        integrity_assert_eq!(
            self.corner_table.vertex(c0p),
            self.corner_table.vertex(_c0_flip)
        );
        integrity_assert_eq!(
            self.corner_table.vertex(c0p),
            self.corner_table.vertex(cd_flip.c0)
        );

        /*integrity_println!(
            "testing c0p:{} c0:{} vc0_fan:{}",
            self.corner_table.data.dbg_corner(c0p),
            self.corner_table.data.dbg_corner(_c0),
            self.dbg_vertex_fan(&cd.c0_fan)
        );
        integrity_println!(
            "or c0p_flip:{} c0_flip:{} vc0_flip_fan:{}",
            self.corner_table.data.dbg_corner(c0p_flip),
            self.corner_table.data.dbg_corner(_c0_flip),
            self.dbg_vertex_fan(&cd_flip.c0_fan)
        );*/

        integrity_assert!(cd.c0_fan.len() > 2);
        integrity_assert_eq!(cd.c0_fan[0], cd.c0);

        integrity_assert!(cd_flip.c0_fan.len() < i16::MAX as usize);
        let c0_valence = cd.c0_fan.len() as i16;
        integrity_assert!(cd_flip.c0_fan.len() < i16::MAX as usize);
        let c0_flip_valence = cd_flip.c0_fan.len() as i16;

        /*#[rustfmt::skip]
        #[cfg(feature = "integrity_check")]
        let _dbg = if [9,3].contains(&self.corner_table.get_vertex_of_corner(cd.c0).0) && [9,3].contains(&self.corner_table.get_vertex_of_corner(c0p).0)
        {
            integrity_println!("hello R:{} V₀:{}", self.corner_table.data.dbg_corner(cd.c0), self.corner_table.data.dbg_corner(c0p));
            true
        } else { false };
        */

        //integrity_println!("c0p:{c0p} c0:{c0} c0_fan:{c0_fan:?}");
        //integrity_println!("c0p_flip:{c0p_flip} c0_flip:{c0_flip} c0_flip_fan:{c0_flip_fan:?}");
        match (c0_valence, c0_flip_valence) {
            // Both vertices have safe valence for normal collapse
            (4..=8, 4..=8) => {
                // Test c0p -> c0 direction
                if self.ce_dihedral_passes_geometric_checks_valence_4plus(&cd) {
                    integrity_println!("accepting c0p:{}", self.dbg_edge(c0p));
                    let c0 = self.corner_table.next(c0p);
                    return Some((
                        c0p,
                        self.corner_table.vertex(c0p),
                        self.corner_table.vertex(c0),
                    ));
                }
                // try the opposite direction
                if self.ce_dihedral_passes_geometric_checks_valence_4plus(&cd_flip) {
                    integrity_println!("accepting c0p_flip:{}", self.dbg_edge(c0p_flip));
                    let c0_flip = self.corner_table.next(c0p_flip);
                    return Some((
                        c0p_flip,
                        self.corner_table.vertex(c0p_flip),
                        self.corner_table.vertex(c0_flip),
                    ));
                }
            }
            // c0_flip is valence-3, collapse toward c0
            (4..=8, 3) => {
                if self.ce_dihedral_passes_geometric_checks_valence_3(&cd_flip) {
                    integrity_println!("accepting valence 3 {}", self.dbg_edge(c0p_flip));
                    let c0_flip = self.corner_table.next(c0p_flip);
                    return Some((
                        c0p_flip,
                        self.corner_table.vertex(c0p_flip),
                        self.corner_table.vertex(c0_flip),
                    ));
                }
            }
            // c0 is valence-3, collapse toward c0_flip
            (3, 4..=8) => {
                if self.ce_dihedral_passes_geometric_checks_valence_3(&cd) {
                    integrity_println!("accepting valence 3 {}", self.dbg_edge(c0p));
                    let c0 = self.corner_table.next(c0p);
                    return Some((
                        c0p,
                        self.corner_table.vertex(c0p),
                        self.corner_table.vertex(c0),
                    ));
                }
            }
            _ => {} // Valences outside acceptable ranges
        }
        match (c0_valence, c0_flip_valence) {
            (4..=8, 3) => {
                // Test c0p -> c0 direction
                if self.ce_dihedral_passes_geometric_checks_valence_4plus(&cd) {
                    integrity_println!("accepting {}", self.corner_table.data.dbg_corner(c0p));
                    let c0 = self.corner_table.next(c0p);
                    return Some((
                        c0p,
                        self.corner_table.vertex(c0p),
                        self.corner_table.vertex(c0),
                    ));
                }
            }
            (3, 4..=8) => {
                // try the opposite direction
                if self.ce_dihedral_passes_geometric_checks_valence_4plus(&cd_flip) {
                    integrity_println!("accepting {}", self.corner_table.data.dbg_corner(c0p_flip));
                    let c0_flip = self.corner_table.next(c0p_flip);
                    return Some((
                        c0p_flip,
                        self.corner_table.vertex(c0p_flip),
                        self.corner_table.vertex(c0_flip),
                    ));
                }
            }
            _ => {}
        }
        integrity_println!(
            "rejecting edge {}=>{}(R) c0_valence:{c0_valence} c0_flip_valence:{c0_flip_valence}",
            self.dbg_corner(c0p),
            self.dbg_corner(cd.c0)
        );
        None
    }

    /// Checks if the collapse edge operation passes geometric tests.
    /// ```text
    ///             tₖo
    ///     Vₖ ─────────────── Vₖ₋₁
    ///     │ \             / │
    ///     │   \    tₖ    /  │
    ///     │     \      /tₖ₋₁│
    ///     │       \cₖ/      │
    /// t₀o │ t₀ c₀  R ────── V₂
    ///     │      / c₁ \ t₂  │
    ///     │c₀p /       \    │ t₂o
    ///     │  /     t₁   \   │
    ///     │/c₁n           \ │
    ///     V₀ ─────────────── V₁
    ///             t₁o
    /// ```
    pub(super) fn ce_dihedral_passes_geometric_checks(&self, cd: &CollapseData) -> bool {
        if cd.c0_fan.len() == 3 {
            self.ce_dihedral_passes_geometric_checks_valence_3(cd)
        } else {
            self.ce_dihedral_passes_geometric_checks_valence_4plus(cd)
        }
    }

    /// Checks if the collapse edge operation passes geometric tests when valence is larger than 3.
    /// It specially checks if the triangle normals remain approximately the same before and
    /// after the collapse.
    ///
    /// ```text
    ///             tₖo                                    tₖo
    ///     Vₖ ─────────────── Vₖ₋₁                Vₖ ─────────────── Vₖ₋₁
    ///     │ \cₖp        cₖn/ │                    │cₖp         cₖn/ │
    ///     │c₀n\    tₖ    /  │                    │       tₖ    /   │
    ///     │     \      /tₖ₋₁│                    │           /tₖ₋₁ │ tₖ₋₁o
    ///     │       \cₖ/      .                    │         /      .
    /// t₀o │ t₀ c₀  R ────── V₂     =>   (old)t₀o │       /       V₂
    ///     │      / c₁ \ c₂  │                    │     /       /  │
    ///     │c₀p /       \ t₂ │ t₂o                │    /     /  c₂p│ t₂o
    ///     │  /     t₁   \   │                    │cₖ /   /   t₂   │
    ///     │/c₁n       c₁p \ │                    │ // c₂      c₂n │
    ///     V₀ ─────────────── V₁                  V₀ ───────────── V₁
    ///             t₁o                                 (old)t₁o
    ///
    /// check :
    ///   normal_similarity_opposite_winding(V₀,Vr,Vₖ,Vₖ₋₁) ie. t₀ vs tₖ
    ///   normal_similarity_opposite_winding(V₀,V₁,Vr,V₂)  ie. t₂ vs t₁
    ///
    /// check :
    ///   normal_similarity_same_winding(V₀,Vₙ,Vₙ₋₁,Vr) (for all n>=2, n<k)
    ///
    /// check for opposite normals for:
    ///   (cₙ,cₙn,cₙp) vs tₙo (for all n>=2)
    ///   (c₂n,c₂p,c₂) vs t₁o
    ///   (cₖp,cₖ,cₖn) vs t₀o
    ///   (cₙn,cₙp,cₙ) vs (cₙ₋₁p,cₙ₋₁n,cₙ₋₁) (for all n>=3)
    /// ```
    pub(super) fn ce_dihedral_passes_geometric_checks_valence_4plus(
        &self,
        cd: &CollapseData,
    ) -> bool {
        let c0_fan = &(cd.c0_fan);

        integrity_assert!(c0_fan.len() >= 4);
        integrity_assert_eq!(cd.c0, c0_fan[0]);
        integrity_assert_eq!(self.corner_table.vertex(cd.c0), cd.vic0);
        integrity_assert_eq!(self.corner_table.vertex(cd.c0p), cd.vic0p);
        integrity_assert_eq!(cd.c0, self.corner_table.next(cd.c0p));
        let v0 = self.vertex(cd.vic0p).to_simd();
        let vr = self.vertex_of_corner(cd.c0).to_simd();

        /*#[cfg(feature = "integrity_check")]
        let _dbg = if cd.vic0.0 == 10 && cd.vic0p.0 == 0
        { println!("hello R:10 V₀:V0"); true }
        else { false };*/

        for (i, &c) in c0_fan[..].iter().enumerate().skip(2) {
            integrity_assert_eq!(vr, self.vertex_of_corner(c).to_simd());
            let o = self.corner_table.opposite(c);
            // it's impossible to set V(c)==V(c.opposite())
            if self.corner_table.vertex(o) == cd.vic0p {
                integrity_println!(
                    "geometric checks failed!!: c₀p:{} c₀:{} would cause non-manifold mesh",
                    self.corner_table.data.dbg_corner(cd.c0p),
                    self.corner_table.data.dbg_corner(cd.c0),
                );
                return false;
            }

            let (cn, cp) = self.corner_table.next_prev(c);
            let co = self.corner_table.opposite(c);
            let vcn = self.vertex_of_corner(cn).to_simd();
            let vcp = self.vertex_of_corner(cp).to_simd();

            #[cfg(feature = "integrity_check")]
            let mut dbg_msg: String = format!(
                "c₀p:{} c₀:{} ",
                self.corner_table.data.dbg_corner(cd.c0p),
                self.corner_table.data.dbg_corner(cd.c0),
            );
            #[cfg(feature = "integrity_check")]
            let vicn = self.corner_table.vertex(cn);
            #[cfg(feature = "integrity_check")]
            let vicp = self.corner_table.vertex(cp);

            let result = if i == 2 {
                // First triangle: normal_similarity_for_merge(V₀,V₁,Vr,V₂)  ie. t₁ vs t₂
                #[cfg(feature = "integrity_check")]
                {
                    dbg_msg += format!(
                        "merge: i:{} v0:{:?}:{:?}, vr:{:?}:{:?} vcp:{:?}:{:?} vcn:{:?}:{:?}",
                        i,
                        cd.vic0p,
                        v0.into(),
                        cd.vic0,
                        vr.into(),
                        vicp,
                        vcp.into(),
                        vicn,
                        vcn.into(),
                    )
                    .as_str();
                }
                Self::normal_similarity_opposite_winding(
                    v0,
                    vcn,
                    vr,
                    vcp,
                    self.params.coplanar_threshold_sq,
                )
            } else if i == c0_fan.len().saturating_sub(1) {
                // Last triangle:normal_similarity_for_merge(V₀,Vr,Vₖ,Vₖ₋₁) ie. t₀ vs tₖ
                #[cfg(feature = "integrity_check")]
                {
                    dbg_msg += format!("merge: i:{} v0:{:?}:{:?}, vr:{:?}:{:?} vcp:{:?}:{:?} vcn:{:?}:{:?} true normal dot {}", i, cd.vic0p,v0.into(),cd.vic0,vr.into(),vicp,vcp.into(),vicn,vcn.into(),Self::normal_similarity_opposite_winding_slow(vr, vcn, vcp, v0)).as_str();
                }
                Self::normal_similarity_opposite_winding(
                    v0,
                    vr,
                    vcp,
                    vcn,
                    self.params.coplanar_threshold_sq,
                )
            } else {
                // Middle triangles: check if moving vertex from Vr to V₀ is OK
                #[cfg(feature = "integrity_check")]
                {
                    dbg_msg += format!(
                        "collapse i{}, v0:{:?}:{:?}, vr:{:?}:{:?} vcp:{:?}:{:?} vcn:{:?}:{:?})",
                        i,
                        cd.vic0p,
                        v0.into(),
                        cd.vic0,
                        vr.into(),
                        vicp,
                        vcp.into(),
                        vicn,
                        vcn.into(),
                    )
                    .as_str();
                }
                Self::normal_similarity_same_winding(
                    v0,
                    vcn,
                    vcp,
                    vr,
                    self.params.coplanar_threshold_sq,
                )
            };

            match result {
                Some(false) => {
                    // disallow zero area triangle for now
                    return false;
                }
                None => {
                    if cd.vic0 == VertexIndex(10) {
                        integrity_println!(
                            "ct failed: {} threshold:{}",
                            dbg_msg,
                            self.params.coplanar_threshold
                        );
                    }
                    return false;
                }
                Some(true) => {
                    integrity_println!(
                        "ct succeed: {} threshold:{}",
                        dbg_msg,
                        self.params.coplanar_threshold
                    );
                }
            }

            let vco = self.vertex_of_corner(co).to_simd();

            match Self::normal_similarity_crease_angle(
                v0,
                vcn,
                vcp,
                vco,
                self.params.crease_limit_threshold_sq,
            ) {
                Some(true) => continue,
                Some(false) => return false,
                None => {
                    if cd.vic0 == VertexIndex(7) {
                        integrity_println!(
                            "dihedral_angle checks failed. c:{}",
                            self.corner_table.dbg_triangle(c)
                        );
                    }
                    return false;
                }
            }
        }

        // Check tₖ (last triangle in fan) vs t₀o
        let ck = c0_fan[c0_fan.len().saturating_sub(1)];
        let (ckn, ckp) = self.corner_table.next_prev(ck);
        let vckn = self.vertex_of_corner(ckn).to_simd(); // Vₖ

        let vickp = self.corner_table.vertex(ckp); // Vₖ₋₁'
        let vckp = self.vertex(vickp).to_simd(); // Vₖ₋₁

        let t0o = self.corner_table.opposite(cd.c0);
        let vt0o = self.vertex_of_corner(t0o).to_simd();

        match Self::normal_similarity_crease_angle(
            vt0o,
            vckp,
            v0,
            vckn,
            self.params.crease_limit_threshold_sq,
        ) {
            Some(true) => {}
            _ => return false,
        }
        /*integrity_println!(
            "current cₖ:{} vs current t₀o:{} with V₀:V{}@cₖ was ok!",
            self.corner_table.dbg_triangle(ck),
            self.corner_table.dbg_triangle(t0o),
            cd.vic0p.0
        );*/

        // Check t₂ vs t₁o (assuming c0_fan has at least t₀, t₁, t₂)
        if c0_fan.len() >= 3 {
            let c2 = c0_fan[2];
            let c1 = c0_fan[1];
            let t1o = self.corner_table.opposite(c1);
            let (c2n, c2p) = self.corner_table.next_prev(c2);
            let vc2n = self.vertex_of_corner(c2n).to_simd();
            let vc2p = self.vertex_of_corner(c2p).to_simd();
            let vt1o = self.vertex_of_corner(t1o).to_simd();

            match Self::normal_similarity_crease_angle(
                vt1o,
                v0,
                vc2n,
                vc2p,
                self.params.crease_limit_threshold_sq,
            ) {
                Some(true) => {}
                _ => {
                    /*if cd.vic0 == VertexIndex(7) {
                        integrity_println!("geometric checks failed: t₂ vs t₁o clash");
                    }*/
                    return false;
                }
            }

            /*integrity_println!(
                "current c₂:{} vs current t₁o:{} with V₀:V{}@c₂ was ok! ",
                self.corner_table.dbg_triangle(c2),
                self.corner_table.dbg_triangle(t1o),
                cd.vic0p.0
            );*/
        }

        // Add checks for consecutive triangle pairs:
        #[allow(clippy::match_like_matches_macro)]
        if !Self::validate_consecutive_triangles(
            c0_fan
                .iter()
                .map(|&c| self.vertex_of_corner(self.corner_table.prev(c)).to_simd()),
            v0,
            2, // skip first two triangles (will be deleted)
            0,
            |normal1, len1_sq, normal2, len2_sq| match Self::validate_fan_triangle_pair(
                normal1,
                len1_sq,
                normal2,
                len2_sq,
                self.params.crease_limit_threshold_sq,
            ) {
                Some(true) => true,
                _ => {
                    integrity_println!(
                        "consecutive_fan_test_fast() failed: V₀ fan consecutive triangles {:?} l:{} and {:?} {}",
                        normal1.into(),
                        len1_sq,
                        normal2.into(),
                        len2_sq
                    );
                    false
                }
            },
        ) {
            return false;
        }

        integrity_println!(
            "geometric checks passed v3+: c₀p:{} c₀:{}",
            self.corner_table.data.dbg_corner(cd.c0p),
            self.corner_table.data.dbg_corner(cd.c0),
        );
        /*#[cfg(feature = "integrity_check")]
        if _dbg {
            print!("");
        }*/
        true
    }

    /// ```text
    /// Specialized triangle check for valence 3 case: tests if R vertex lies in the plane of V₀V₁V₂.
    /// Only three triangles exist in the R fan: t₀, t₁, and t₂.
    ///     V₂ ─────────────
    ///     │ \              \  t₂o
    ///     │   \        t₂   │
    ///     │     \           │
    ///     │       \  c₂     │
    /// t₀o │ t₀ c₀  R        │
    ///     │      / c₁ \     │
    ///     │c₀p /       \    │
    ///     │  /     t₁   \   │
    ///     │/              \ │
    ///     V₀ ────────────── V₁
    ///              t₁o
    ///
    /// Input: c₀p: The corner index at V₀ in the RV₂V₀=t₀ triangle.
    /// V(c₀)==R, V(c₀.prev())==V₀, V(c₀.next())==V₂
    /// ```
    pub(super) fn ce_dihedral_passes_geometric_checks_valence_3(&self, cd: &CollapseData) -> bool {
        integrity_assert_eq!(cd.c0_fan.len(), 3);
        integrity_assert_eq!(cd.c0, self.corner_table.next(cd.c0p));
        let c1 = cd.c0_fan[1];
        #[cfg(any(feature = "integrity_check", debug_assertions))]
        let c2 = cd.c0_fan[2];

        let vr = self.vertex_of_corner(cd.c0).to_simd();
        let v0 = self.vertex_of_corner(cd.c0p).to_simd();
        let v1 = self.vertex_of_corner(self.corner_table.prev(c1)).to_simd();
        let v2 = self
            .vertex_of_corner(self.corner_table.next(cd.c0))
            .to_simd();
        integrity_assert_eq!(
            v0,
            self.vertex_of_corner(self.corner_table.next(c1)).to_simd()
        );
        integrity_assert_eq!(
            v1,
            self.vertex_of_corner(self.corner_table.next(c2)).to_simd()
        );
        integrity_assert_eq!(
            v2,
            self.vertex_of_corner(self.corner_table.prev(c2)).to_simd()
        );

        match Self::normal_similarity_same_winding(
            vr,
            v0,
            v1,
            v2,
            self.params.coplanar_threshold_sq,
        ) {
            Some(true) => {}
            Some(false) => {
                // disallow zero area triangle for now
                return false;
            }
            None => {
                integrity_println!(
                    "geometric checks failed: c₀p:{} c₀:{} {}",
                    self.corner_table.data.dbg_corner(cd.c0p),
                    self.corner_table.data.dbg_corner(cd.c0),
                    "disparate normals"
                );
                integrity_println!("c{}", self.corner_table.dbg_triangle(cd.c0));
                return false;
            }
        }

        integrity_println!(
            "geometric checks v3 passed: c₀p:{} c₀:{}",
            self.corner_table.data.dbg_corner(cd.c0p),
            self.corner_table.data.dbg_corner(cd.c0),
        );
        true
    }

    pub(super) fn ce_dihedral_can_still_collapse_edge(
        &self,
        cd: CollapseData,
    ) -> Option<CollapseData> {
        /*#[cfg(feature = "integrity_check")]
        #[rustfmt::skip]
        let _dbg = if cd.c0p.0 == 0
        {
            integrity_println!("hello R:{} V₀:{} ce_can_still_collapse_edge", self.corner_table.data.dbg_corner(cd.c0p), self.corner_table.data.dbg_corner(cd.c0));
            true
        } else { false };*/
        if self.corner_table.is_corner_deleted(cd.c0p) {
            integrity_println!(
                "bail0: c0p:{} is deleted",
                self.corner_table.data.dbg_corner(cd.c0p)
            );
            return None;
        }

        integrity_assert!(cd.vic0.is_valid());
        integrity_assert!(cd.vic0p.is_valid());

        integrity_println!(
            "ce_can_still_collapse_edge: c0p:{} V₀={:?} R={:?}",
            self.dbg_edge(cd.c0p),
            cd.vic0p,
            cd.vic0,
        );

        // Skip if either vertex was already collapsed/touched in this iteration
        if self.dirty_vertices.is_dirty(cd.vic0p) {
            integrity_println!(
                "bail1: V₀({}) already collapsed in this iteration",
                self.corner_table.data.dbg_corner(cd.c0p)
            );
            return None;
        }
        if self.dirty_vertices.is_dirty(cd.vic0) {
            integrity_println!(
                "ce_can_still_collapse_edge bail vertex R({}) already collapsed in this iteration",
                self.corner_table.data.dbg_corner(cd.c0)
            );
            return None;
        }

        if !self.ce_dihedral_passes_geometric_checks(&cd) {
            return None;
        }
        Some(cd)
    }
}
