// 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};
use crate::corner_table::TriangleIndex;
use crate::corner_table::common::CornerTableData;
use crate::corner_table::{CornerIndex, CornerTable, VertexFan};

impl<const ENABLE_UNSAFE: bool> CornerTable<ENABLE_UNSAFE> {
    /// Get the opposite corner (across the edge)
    #[inline(always)]
    pub(crate) fn opposite(&self, corner: CornerIndex) -> CornerIndex {
        integrity_assert!(
            !self.is_corner_deleted(corner),
            "Corner was deleted {corner:?}"
        );
        let o = self.data.opposite(corner);
        integrity_assert!(
            !self.is_corner_deleted(o),
            "Fail! Opposite corner was deleted {o:?}. Origin corner {:?}",
            self.dbg_corner(corner)
        );
        o
    }
    #[inline(always)]
    pub(crate) fn try_opposite(&self, corner: CornerIndex) -> Option<CornerIndex> {
        integrity_assert!(
            !self.is_corner_deleted(corner),
            "Corner was deleted {corner:?}"
        );
        let o = self.data.opposite(corner);
        if o.is_valid() { Some(o) } else { None }
    }

    /// Set the opposite corner (across the edge) of both ca and cb (pointing to each other)
    #[inline(always)]
    pub(crate) fn set_dual_opposite(&mut self, ca: CornerIndex, cb: CornerIndex) {
        integrity_assert!(
            !self.is_corner_deleted(ca),
            "triangle {} was deleted",
            self.data.dbg_corner(ca)
        );
        integrity_assert!(
            !self.is_corner_deleted(cb),
            "triangle {} was deleted",
            self.data.dbg_corner(cb)
        );
        self.data.set_dual_opposite(ca, cb)
    }

    #[inline(always)]
    pub(crate) fn clear_opposite(&mut self, ca: CornerIndex) {
        integrity_assert!(
            !self.is_corner_deleted(ca),
            "triangle {} was deleted",
            self.data.dbg_corner(ca)
        );
        self.data.set_opposite(ca, CornerIndex::INVALID)
    }

    #[inline(always)]
    pub(crate) fn corner(&self, vertex: VertexIndex) -> CornerIndex {
        integrity_assert!(vertex.is_valid());
        if ENABLE_UNSAFE {
            unsafe {
                *self
                    .data
                    .corner_of_vertex_vec
                    .get_unchecked(vertex.0 as usize)
            }
        } else {
            self.data.corner_of_vertex_vec[vertex.0 as usize]
        }
    }

    #[inline(always)]
    pub(crate) fn vertex(&self, corner: CornerIndex) -> VertexIndex {
        integrity_assert!(corner.is_valid());
        integrity_assert!(
            !self.is_corner_deleted(corner),
            "Corner {corner:?} was deleted"
        );
        integrity_assert!(
            self.data.vertex_of_corner(corner).is_valid(),
            "Valid corner had no vertex:{}",
            self.data.dbg_corner(corner)
        );
        self.data.vertex_of_corner(corner)
    }

    #[inline(always)]
    /// Get the next corner around the same vertex (counter-clockwise)
    pub(crate) fn swing_ccw(&self, corner: CornerIndex) -> CornerIndex {
        self.next(self.opposite(self.next(corner)))
    }

    #[inline(always)]
    /// Get the next corner around the same vertex (counter-clockwise)
    /// This operation might fail if encountering an edge boundary, returning a None
    pub(crate) fn try_swing_ccw(&self, corner: CornerIndex) -> Option<CornerIndex> {
        self.try_opposite(self.next(corner)).map(|o| self.next(o))
    }

    #[allow(dead_code)]
    #[inline(always)]
    /// Get the next corner around the same vertex (clockwise)
    pub(crate) fn swing_cw(&self, corner: CornerIndex) -> CornerIndex {
        self.prev(self.opposite(self.prev(corner)))
    }

    #[inline(always)]
    /// Get the next corner around the same vertex (clockwise)
    /// This operation might fail if encountering an edge boundary, returning a None
    pub(crate) fn try_swing_cw(&self, corner: CornerIndex) -> Option<CornerIndex> {
        self.try_opposite(self.prev(corner)).map(|c| c.prev())
    }

    #[inline(always)]
    #[allow(clippy::let_and_return)]
    /// ```text
    ///                 B
    ///               / │ \
    ///             /   │   \
    ///           / c₀n │ c₀t \
    ///         /       │       \
    ///       /         │         \
    ///     /           │           \
    ///   / c₀p      c₀ │             \
    ///  ────────────── A ──────────────
    ///
    /// Get the twin corner of the same edge: c₀ -> c₀.twin
    /// i.e: c₀.twin == c₀t == c₀.prev.opposite.next,
    ///      V(c₀.next) == V(c₀.twin) == B
    /// ```
    pub(crate) fn twin(&self, corner: CornerIndex) -> CornerIndex {
        let t = self.next(self.opposite(self.prev(corner)));
        integrity_assert!(t.is_valid());
        integrity_assert_eq!(
            self.vertex(t),
            self.vertex(self.next(corner)),
            "V(corner.next:{}) != V(corner.twin:{})",
            self.dbg_corner(corner.next()),
            self.dbg_corner(t),
        );
        t
    }

    #[inline(always)]
    /// Get the next corner in the same counter-clockwise triangle
    pub(crate) fn next(&self, corner: CornerIndex) -> CornerIndex {
        integrity_assert!(!self.is_corner_deleted(corner));
        corner.next()
    }

    #[inline(always)]
    /// Get the previous corner in the same counter-clockwise triangle
    pub(crate) fn prev(&self, corner: CornerIndex) -> CornerIndex {
        integrity_assert!(!self.is_corner_deleted(corner));
        corner.prev()
    }

    #[inline(always)]
    /// Get both next and previous corners in the same counter-clockwise triangle
    pub(crate) fn next_prev(&self, corner: CornerIndex) -> (CornerIndex, CornerIndex) {
        integrity_assert!(!self.is_corner_deleted(corner));
        corner.next_prev()
    }

    /// Get all corner indices pivoting (CCW) around a vertex, starting from a known corner
    /// `start_corner` will be the first corner of the fan.
    pub(crate) fn ccw_vertex_fan(&self, start_corner: CornerIndex) -> VertexFan {
        integrity_assert!(start_corner.is_valid());
        integrity_assert!(self.triangle_pool.is_used(start_corner.into()));

        let mut vertex_fan = VertexFan::with_capacity(super::DEFAULT_MAX_VALENCE as usize + 2);

        integrity_assert!(start_corner.is_valid());
        let mut current_corner = start_corner;

        // Traverse counter-clockwise around the vertex
        loop {
            vertex_fan.push(current_corner);

            let next_corner = self.swing_ccw(current_corner);
            integrity_assert!(next_corner.is_valid());
            // Break if we've completed the loop or reached boundary/invalid
            if next_corner == start_corner || !next_corner.is_valid() {
                break;
            }

            current_corner = next_corner;
        }

        if cfg!(feature = "integrity_check") {
            let v0 = self.vertex(start_corner);
            let mut iter = vertex_fan.iter();
            assert_eq!(start_corner, *iter.next().unwrap());
            for &_c in iter {
                assert_eq!(v0, self.vertex(_c));
            }
        }

        vertex_fan
    }

    pub(crate) fn find_swing_avoiding_triangles(
        &self,
        start: CornerIndex,
        avoid: &[TriangleIndex],
    ) -> CornerIndex {
        let mut current = self.swing_ccw(start);
        let first = current;

        loop {
            if !avoid.contains(&current.triangle()) {
                return current;
            }
            current = self.swing_ccw(current);
            if current == first {
                return CornerIndex::INVALID; // No valid swing found
            }
        }
    }

    pub(crate) fn valence(&self, start_corner: CornerIndex) -> i16 {
        integrity_assert!(start_corner.is_valid());
        let mut current_corner = start_corner;
        let mut valence = 1; // Count the first edge

        // Traverse around the vertex
        loop {
            let next_corner = self.swing_ccw(current_corner);
            if next_corner == start_corner {
                break; // Completed the loop
            }
            valence += 1;
            current_corner = next_corner;
        }
        integrity_assert!(valence > 2);
        valence
    }

    /// sets the `vertex_of_corner_vec[corner]` = `vertex` and `corner_of_vertex[vertex]` = `corner`
    #[inline(always)]
    pub(crate) fn link_corner_vertex(&mut self, corner: CornerIndex, vertex: VertexIndex) {
        self.set_corner_of_vertex(vertex, corner);
        self.set_vertex_of_corner(corner, vertex);
    }

    /// sets `corner_of_vertex[vertex]` = `corner`
    #[inline(always)]
    pub(crate) fn set_corner_of_vertex(&mut self, vertex: VertexIndex, corner: CornerIndex) {
        integrity_assert!(vertex.is_valid());
        integrity_assert!(corner.is_valid());
        integrity_assert!(!self.is_corner_deleted(corner));
        if ENABLE_UNSAFE {
            unsafe {
                *self
                    .data
                    .corner_of_vertex_vec
                    .get_unchecked_mut(vertex.usize()) = corner;
            }
        } else {
            self.data.corner_of_vertex_vec[vertex.usize()] = corner;
        }
    }

    /// sets `vertex_of_corner[vertex]` = `corner`
    #[inline(always)]
    pub(crate) fn set_vertex_of_corner(&mut self, corner: CornerIndex, vertex: VertexIndex) {
        integrity_assert!(vertex.is_valid());
        integrity_assert!(corner.is_valid());
        integrity_assert!(!self.is_corner_deleted(corner));
        if ENABLE_UNSAFE {
            unsafe {
                *self
                    .data
                    .vertex_of_corner_vec
                    .get_unchecked_mut(corner.usize()) = vertex;
            }
        } else {
            self.data.vertex_of_corner_vec[corner.usize()] = vertex;
        }
    }
}

impl<const ENABLE_UNSAFE: bool> CornerTableData<ENABLE_UNSAFE> {
    /// Set the opposite corner (across the edge) of both ca and cb (pointing to each other)
    #[inline(always)]
    pub(super) fn set_dual_opposite(&mut self, ca: CornerIndex, cb: CornerIndex) {
        self.set_opposite(ca, cb);
        self.set_opposite(cb, ca);
    }

    /// Set the opposite corner of ca. So that ca.opposite == cb
    #[inline(always)]
    pub(super) fn set_opposite(&mut self, ca: CornerIndex, cb: CornerIndex) {
        if ENABLE_UNSAFE {
            unsafe {
                *self.opposite_corner_vec.get_unchecked_mut(ca.usize()) = cb;
            }
        } else {
            self.opposite_corner_vec[ca.usize()] = cb;
        }
    }

    #[inline(always)]
    pub(super) fn opposite(&self, corner: CornerIndex) -> CornerIndex {
        if ENABLE_UNSAFE {
            unsafe { *self.opposite_corner_vec.get_unchecked(corner.0 as usize) }
        } else {
            self.opposite_corner_vec[corner.0 as usize]
        }
    }

    #[inline(always)]
    pub(super) fn vertex_of_corner(&self, corner: CornerIndex) -> VertexIndex {
        if ENABLE_UNSAFE {
            unsafe { *self.vertex_of_corner_vec.get_unchecked(corner.0 as usize) }
        } else {
            self.vertex_of_corner_vec[corner.0 as usize]
        }
    }
}
