// 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_assert_ne, integrity_println,
};
use crate::corner_table::common::CornerTableData;
use crate::corner_table::{CornerIndex, CornerTable, TriangleIndex};
use crate::util::index_pool::Allocation;

impl<const ENABLE_UNSAFE: bool> CornerTable<ENABLE_UNSAFE> {
    // free triangles, but before that: make sure that corner_of_vertex is valid for each vertex.
    pub(crate) fn free_two_adjacent_triangles(&mut self, t0: TriangleIndex, t1: TriangleIndex) {
        integrity_assert!(!self.is_triangle_deleted(t0));
        integrity_assert!(!self.is_triangle_deleted(t1));

        // Collect all affected vertices from both triangles
        let corners0 = t0.corners();
        let corners1 = t1.corners();

        let vertices0 = corners0.map(|c| self.data.vertex_of_corner(c));
        let vertices1 = corners1.map(|c| self.data.vertex_of_corner(c));

        // Find swing corners BEFORE deleting anything
        // Skip swings that would land on either t0 or t1
        let mut vertex_swings = [(VertexIndex::INVALID, CornerIndex::INVALID); 4];
        let mut count = 0;

        for (&corner, &vertex) in corners0
            .iter()
            .chain(&corners1)
            .zip(vertices0.iter().chain(&vertices1))
        {
            // Check if already in array
            if vertex_swings[..count].iter().any(|(v, _)| *v == vertex) {
                continue;
            }
            // If count > 3, we have more than 4 unique vertices - impossible for 2 triangles
            integrity_assert!(
                count < 4,
                "More than 4 unique vertices found for two triangles"
            );

            let swing = self.find_swing_avoiding_triangles(corner, &[t0, t1]);
            integrity_assert!(swing.is_valid());
            vertex_swings[count] = (vertex, swing);
            count += 1;

            if cfg!(not(any(feature = "integrity_check", debug_assertions))) && count == 4 {
                break;
            }
        }

        // Update vertex references
        for &(vertex, swing) in &vertex_swings[..count] {
            self.data.set_default_corner_of_vertex(vertex, swing);
        }

        // Finally, delete both triangles
        self.free_triangle_some_cleanup(t0);
        self.free_triangle_some_cleanup(t1);
    }

    pub(crate) fn free_triangle_some_cleanup(&mut self, t: TriangleIndex) {
        integrity_assert!(!self.is_triangle_deleted(t));
        for c in t.corners() {
            self.data.vertex_of_corner_vec[c.0 as usize] = VertexIndex::INVALID;
            self.data.opposite_corner_vec[c.0 as usize] = CornerIndex::INVALID;
        }
        self.triangle_pool.push(t);
        integrity_assert!(self.is_triangle_deleted(t));
    }

    /// returns a free triangle or allocates a new triangle.
    pub(crate) fn allocate_triangle(
        &mut self,
        va: VertexIndex,
        vb: VertexIndex,
        vc: VertexIndex,
    ) -> (CornerIndex, CornerIndex, CornerIndex) {
        #[cfg(feature = "integrity_check")]
        let _ = self.len();

        match self.triangle_pool.pop() {
            Allocation::Reused(t) => {
                let ca = CornerIndex(t.0 * 3);
                let cb = CornerIndex(ca.0 + 1);
                let cc = CornerIndex(cb.0 + 1);
                debug_assert!(
                    cc.0 < self.data.vertex_of_corner_vec.len() as u32,
                    "cc:{} >= vertex_of_corner.len():{}",
                    cc.0,
                    self.data.corner_of_vertex_vec.len() as u32
                );
                self.data.vertex_of_corner_vec[ca.0 as usize] = va;
                self.data.vertex_of_corner_vec[cb.0 as usize] = vb;
                self.data.vertex_of_corner_vec[cc.0 as usize] = vc;

                self.data.corner_of_vertex_vec[va.0 as usize] = ca;
                self.data.corner_of_vertex_vec[vb.0 as usize] = cb;
                self.data.corner_of_vertex_vec[vc.0 as usize] = cc;

                self.data.opposite_corner_vec[ca.0 as usize] = CornerIndex::INVALID;
                self.data.opposite_corner_vec[cb.0 as usize] = CornerIndex::INVALID;
                self.data.opposite_corner_vec[cb.0 as usize] = CornerIndex::INVALID;

                integrity_println!(
                    "reused triangle: ca:{},cb:{},cc:{} populated with invalid opposites",
                    self.data.dbg_corner(ca),
                    self.data.dbg_corner(cb),
                    self.data.dbg_corner(cc),
                );
                (ca, cb, cc)
            }
            Allocation::New(t) => {
                // create a new triangle
                integrity_assert_eq!(
                    self.data.vertex_of_corner_vec.len(),
                    self.data.opposite_corner_vec.len()
                );

                let ca = CornerIndex(t.0 * 3);
                let cb = CornerIndex(ca.0 + 1);
                let cc = CornerIndex(cb.0 + 1);
                debug_assert_eq!(ca.0, self.data.vertex_of_corner_vec.len() as u32);
                self.data.vertex_of_corner_vec.push(va);
                debug_assert_eq!(cb.0, self.data.vertex_of_corner_vec.len() as u32);
                self.data.vertex_of_corner_vec.push(vb);
                debug_assert_eq!(cc.0, self.data.vertex_of_corner_vec.len() as u32);
                self.data.vertex_of_corner_vec.push(vc);

                let maxv = VertexIndex(va.0.max(vb.0).max(vc.0));
                debug_assert!(maxv.0 <= self.data.corner_of_vertex_vec.len() as u32);
                if maxv.0 == self.data.corner_of_vertex_vec.len() as u32 {
                    self.data.corner_of_vertex_vec.push(CornerIndex::INVALID)
                }

                self.data.corner_of_vertex_vec[va.0 as usize] = ca;
                self.data.corner_of_vertex_vec[vb.0 as usize] = cb;
                self.data.corner_of_vertex_vec[vc.0 as usize] = cc;

                self.data.opposite_corner_vec.push(CornerIndex::INVALID);
                self.data.opposite_corner_vec.push(CornerIndex::INVALID);
                self.data.opposite_corner_vec.push(CornerIndex::INVALID);

                /*integrity_println!(
                    "created new triangle: ca:{},cb:{},cc:{} populated with invalid opposites",
                    self.corner_table.data.dbg_corner(ca),
                    self.corner_table.data.dbg_corner(cb),
                    self.corner_table.data.dbg_corner(cc),
                );*/
                (ca, cb, cc)
            }
        }
    }

    /// compress the corner table
    pub(crate) fn defragment_triangles(&mut self) {
        let old_size = self.triangle_pool.active_count();

        self.triangle_pool.defragment(|from_idx, to_idx| {
            self.data.remap_triangle_references(from_idx, to_idx);
        });

        // Truncate corner arrays to match compacted size
        self.data
            .vertex_of_corner_vec
            .truncate((old_size * 3) as usize);
        self.data
            .opposite_corner_vec
            .truncate((old_size * 3) as usize);
    }

    #[inline(always)]
    pub(crate) fn delete_vertex(&mut self, vertex: VertexIndex) {
        debug_assert!(vertex.is_valid());
        self.data.corner_of_vertex_vec[vertex.0 as usize] = CornerIndex::INVALID;
        integrity_println!("Deleting vertex {vertex:?}");
    }

    #[inline(always)]
    pub(crate) fn handle_new_vertex(&mut self, vertex: VertexIndex) {
        #[cfg(feature = "integrity_check")]
        let _ = self.len();

        debug_assert!(vertex.is_valid());
        debug_assert_eq!(vertex.0, self.data.corner_of_vertex_vec.len() as u32);

        self.data.corner_of_vertex_vec.push(CornerIndex::INVALID);

        #[cfg(feature = "integrity_check")]
        let _ = self.len();
    }

    #[inline(always)]
    pub(crate) fn handle_reused_vertex(&mut self, vertex: VertexIndex) {
        #[cfg(feature = "integrity_check")]
        let _ = self.len();

        debug_assert!(vertex.is_valid());
        debug_assert!(vertex.0 < self.data.corner_of_vertex_vec.len() as u32);

        integrity_println!("Resused vertex {vertex:?}");

        #[cfg(feature = "integrity_check")]
        let _ = self.len();
    }
}

impl<const ENABLE_UNSAFE: bool> CornerTableData<ENABLE_UNSAFE> {
    fn remap_triangle_references(&mut self, old_idx: TriangleIndex, new_idx: TriangleIndex) {
        integrity_assert_ne!(old_idx, new_idx);

        // Swap: move triangle at last_used into the hole
        let new_base = new_idx.0 * 3;
        let old_base = old_idx.0 * 3;

        integrity_println!(
            "moving triangle {old_idx:?}: {} {} {}",
            self.dbg_corner(CornerIndex(old_base + 0)),
            self.dbg_corner(CornerIndex(old_base + 1)),
            self.dbg_corner(CornerIndex(old_base + 2))
        );

        for i in 0..3 {
            self.vertex_of_corner_vec[(new_base + i) as usize] =
                self.vertex_of_corner_vec[(old_base + i) as usize];
            self.set_dual_opposite(
                CornerIndex(new_base + i),
                self.opposite_corner_vec[(old_base + i) as usize],
            );
            self.set_default_corner_of_vertex(
                self.vertex_of_corner_vec[(new_base + i) as usize],
                CornerIndex(new_base + i),
            );
        }

        integrity_println!(
            "to triangle {new_idx:?}: {} {} {}",
            self.dbg_corner(CornerIndex(new_base + 0)),
            self.dbg_corner(CornerIndex(new_base + 1)),
            self.dbg_corner(CornerIndex(new_base + 2))
        );
        //integrity_println!("moved triangle {old_idx} to {new_idx}");
    }
}
