use crate::constants::MIN_CONTOUR_VERTICES_COUNT;
use crate::operations::{shrink_collinear_vertices, Orient};

use super::mesh::Mesh;
use super::operations::{BoundaryEndpoints, DelaunayTriangulatable};
use super::quad_edge::{QuadEdge, UNDEFINED_QUAD_EDGE};

#[derive(Clone)]
pub(crate) struct DelaunayTriangulation<Endpoint> {
    left_side: QuadEdge,
    mesh: Mesh<Endpoint>,
    right_side: QuadEdge,
}

impl<Endpoint> BoundaryEndpoints<Endpoint> for DelaunayTriangulation<Endpoint>
where
    for<'a> &'a Endpoint: Orient,
{
    fn get_boundary_endpoints(&self) -> Vec<&Endpoint> {
        let endpoints = self.mesh.get_endpoints();
        if endpoints.len() < MIN_CONTOUR_VERTICES_COUNT {
            endpoints.iter().collect()
        } else {
            let mut result = Vec::new();
            let start = self.left_side;
            let mut edge = start;
            loop {
                result.push(self.mesh.get_start(edge));
                let candidate = self.mesh.to_right_from_end(edge);
                if candidate == start {
                    break;
                }
                edge = candidate;
            }
            shrink_collinear_vertices(&result)
        }
    }
}

impl<Endpoint: Ord> From<Vec<Endpoint>> for DelaunayTriangulation<Endpoint>
where
    Mesh<Endpoint>: DelaunayTriangulatable,
{
    fn from(mut endpoints: Vec<Endpoint>) -> Self {
        endpoints.sort();
        endpoints.dedup();
        let mut mesh = Mesh::from(endpoints);
        let (left_side, right_side) = mesh.delaunay_triangulation();
        Self {
            left_side,
            mesh,
            right_side,
        }
    }
}

impl<Endpoint> DelaunayTriangulation<Endpoint> {
    pub(crate) fn is_empty(&self) -> bool {
        let result = self.mesh.is_empty();
        debug_assert_eq!(self.left_side == UNDEFINED_QUAD_EDGE, result);
        debug_assert_eq!(self.right_side == UNDEFINED_QUAD_EDGE, result);
        result
    }
}

impl<Endpoint: PartialOrd> DelaunayTriangulation<Endpoint>
where
    for<'a> &'a Endpoint: Orient,
{
    pub(crate) fn iter_triangles_vertices(
        &self,
    ) -> impl Iterator<Item = (&Endpoint, &Endpoint, &Endpoint)> {
        self.mesh.to_triangles_base_edges().map(move |base_edge| {
            self.mesh.triangle_base_to_vertices(base_edge)
        })
    }
}
