// ==================
// Standard simplices
// ==================

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct Simplex2 {
    /// The `neighbours` and `vertices` must be stored in counter-clockwise (positive) direction.
    /// The relative ordering must be such that the vertex corresponding to a neighbouring triangle
    /// is the vertex opposite the triangle.
    pub neighbours: [usize; 3],
    /// See notes on `neighbours`
    pub vertices: [usize; 3],
}
pub type Triangle = Simplex2;

// ================
// Causal simplices
// ================

/// Kind/type/variant specification of a triangle/2-simplex.
///
/// Variant `S21` refers to a triangle with 2 vertices at the previous time-slice
/// to the other one, where variant `S12` is the opposite.
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord, Serialize, Deserialize,
)]
pub enum CausalSimplex2Kind {
    #[default]
    S21 = 0,
    S12 = 1,
}
pub type CausalTriangleKind = CausalSimplex2Kind;

/// Triangle structure with causal information as used in 2D CDT
///
/// Also see the documentation of the fields
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct CausalSimplex2 {
    pub t: u16,
    pub kind: CausalTriangleKind,
    /// The `neighbours` and `vertices` must be stored in counter-clockwise (positive) direction.
    /// The relative ordering must be such that the vertex corresponding to a neighbouring triangle
    /// is the vertex opposite the triangle.
    /// Additionally, to fix the possible cyclic permutations, the first (element 0) neighbour
    /// must be the time-like neighbour, i.e. the one that is on the next or previous time-slice.
    pub neighbours: [usize; 3],
    /// See notes on `neighbours`
    pub vertices: [usize; 3],
}
pub type CausalTriangle = CausalSimplex2;

// =======================================
// Causal to non-causal simplex conversion
// =======================================

impl From<CausalSimplex2> for Simplex2 {
    /// Convert [`CausalTriangle`] to [`Triangle`] by simply dropping `time` and `kind` information.
    fn from(value: CausalSimplex2) -> Self {
        Simplex2 {
            neighbours: value.neighbours,
            vertices: value.vertices,
        }
    }
}

impl From<&CausalSimplex2> for Simplex2 {
    /// Convert [`&CausalTriangle`](CausalTriangle) to [`Triangle`] by copying
    /// only the neighbour and vertex information.
    fn from(value: &CausalSimplex2) -> Self {
        Simplex2 {
            neighbours: value.neighbours,
            vertices: value.vertices,
        }
    }
}

/// Methods for working with [`Simplex2`] when `neighbours` are encoded by `half-edges`,
/// see [`Triangulation2D`](super::triangulation2d_edge::Triangulation2D)
impl Simplex2 {
    /// Returns the label of the neighbouring triangle on the `index` side.
    ///
    /// # Panic
    /// This method will panic `index >= 3`
    #[inline]
    pub fn get_neighbour(&self, index: usize) -> usize {
        let nbr = self.neighbours[index];
        nbr / 3
    }

    /// Returns the label and backindex of neighbouring triangle on the `index` side.
    ///
    /// # Panic
    /// This method will panic `index >= 3`
    #[inline]
    pub fn get_neighbour_backindex(&self, index: usize) -> (usize, usize) {
        let nbr = self.neighbours[index];
        (nbr / 3, nbr % 3)
    }

    /// Returns the neighbouring triangle labels of the [`Triangle`]
    #[inline]
    pub fn get_neighbours(&self) -> [usize; 3] {
        let adjacent_edges = self.neighbours;
        adjacent_edges.map(|nbr| nbr / 3)
    }

    /// Returns the neighbouring triangle labels and back indices in tuple pairs of the [`Triangle`]
    #[inline]
    pub fn get_neighbours_backindices(&self) -> [(usize, usize); 3] {
        let adjacent_edges = self.neighbours;
        adjacent_edges.map(|nbr| (nbr / 3, nbr % 3))
    }
}

/// Methods for working with [`CausalSimplex2`] when `neighbours` are encoded by `half-edges`,
/// see [`CausalTriangulation2D`](super::causal_triangulation2d_edge::CausalTriangulation2D)
impl CausalSimplex2 {
    /// Returns the label of the neighbouring triangle on the `index` side.
    ///
    /// # Panic
    /// This method will panic `index >= 3`
    #[inline]
    pub fn get_neighbour(&self, index: usize) -> usize {
        let nbr = self.neighbours[index];
        nbr / 3
    }

    /// Returns the label and backindex of neighbouring triangle on the `index` side.
    ///
    /// # Panic
    /// This method will panic `index >= 3`
    #[inline]
    pub fn get_neighbour_backindex(&self, index: usize) -> (usize, usize) {
        let nbr = self.neighbours[index];
        (nbr / 3, nbr % 3)
    }

    /// Returns the neighbouring triangle labels of the [`Triangle`]
    #[inline]
    pub fn get_neighbours(&self) -> [usize; 3] {
        let adjacent_edges = self.neighbours;
        adjacent_edges.map(|nbr| nbr / 3)
    }

    /// Returns the neighbouring triangle labels and back indices in tuple pairs of the [`Triangle`]
    #[inline]
    pub fn get_neighbours_backindices(&self) -> [(usize, usize); 3] {
        let adjacent_edges = self.neighbours;
        adjacent_edges.map(|nbr| (nbr / 3, nbr % 3))
    }
}
