use crate::geometry::*;
use earcutr::earcut;

pub fn triangulate(polygon: &Polygon) -> Result<Vec<Triangle>, earcutr::Error> {
    if polygon.len() < 3 {
        return Ok(vec![Triangle(polygon[0], polygon[1], polygon[2])]);
    }

    let points: Vec<f64> = polygon
        .iter()
        .flat_map(|point| vec![point.x as f64, point.y as f64])
        .collect();
    let indices = earcut(&points, &[], 2)?;

    Ok(indices
        .chunks(3)
        .map(|chunk| Triangle(polygon[chunk[0]], polygon[chunk[1]], polygon[chunk[2]]))
        .collect())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_triangulate() {
        let polygon = vec![
            Point { x: 0, y: 0 },
            Point { x: 3, y: 4 },
            Point { x: 4, y: 0 },
            Point { x: 2, y: 2 },
        ];

        let triangles = match triangulate(&polygon) {
            Ok(triangles) => triangles,
            Err(_) => panic!("Error"),
        };

        assert_eq!(triangles.len(), polygon.len() - 2);
    }

    #[test]
    fn test_triangulate_2() {
        let polygon = vec![
            Point { x: 0, y: 0 },
            Point { x: 3, y: 4 },
            Point { x: 4, y: 0 },
            Point { x: 6, y: 1 },
            Point { x: 7, y: 0 },
            Point { x: 7, y: 3 },
            Point { x: 6, y: 5 },
            Point { x: 1, y: 8 },
        ];

        let triangles = triangulate(&polygon).unwrap();

        assert_eq!(triangles.len(), polygon.len() - 2);
    }

    #[test]
    fn test_triangulate_3() {
        let polygon = vec![
            Point { x: 0, y: 0 },
            Point { x: 4, y: 0 },
            Point { x: 5, y: 2 },
            Point { x: 8, y: 1 },
            Point { x: 10, y: 6 },
            Point { x: 6, y: 4 },
            Point { x: 10, y: 8 },
            Point { x: 6, y: 10 },
            Point { x: 6, y: 16 },
            Point { x: 5, y: 14 },
            Point { x: 4, y: 15 },
            Point { x: 0, y: 16 },
            Point { x: 2, y: 8 },
        ];

        let triangles = triangulate(&polygon).unwrap();

        assert_eq!(triangles.len(), polygon.len() - 2);
    }

    #[test]
    fn test_triangulate_triangle() {
        let polygon = vec![
            Point { x: 0, y: 0 },
            Point { x: 3, y: 4 },
            Point { x: 4, y: 0 },
        ];
        let triangles = match triangulate(&polygon) {
            Ok(triangles) => triangles,
            Err(_) => panic!("Error"),
        };

        assert_eq!(triangles.len(), 1);
        assert_eq!(triangles[0], Triangle(polygon[0], polygon[1], polygon[2]));
    }
}
