use approx::assert_relative_eq;
use nalgebra::{Point2, Rotation2, Translation2, Vector2};

use crate::{
    curve::NurbsCurve2D,
    misc::Transformable,
    prelude::PeriodicInterpolation,
    prelude::{CurveIntersectionSolverOptions, Intersects},
};

use super::KnotStyle;

const OPTIONS: CurveIntersectionSolverOptions<f64> = CurveIntersectionSolverOptions {
    minimum_distance: 1e-4,
    knot_domain_division: 500,
    max_iters: 1000,
    step_size_tolerance: 1e-8,
    cost_tolerance: 1e-10,
};

#[test]
fn problem_case() {
    let dx = 1.25;
    let dy = 1.5;
    let subject = NurbsCurve2D::<f64>::interpolate_periodic(
        &vec![
            Point2::new(-dx, -dy),
            Point2::new(0., -dy * 1.25),
            Point2::new(dx, -dy),
            Point2::new(dx, dy),
            Point2::new(0., dy * 1.25),
            Point2::new(-dx, dy),
        ],
        3,
        KnotStyle::Centripetal,
    )
    .unwrap();
    let clip = NurbsCurve2D::<f64>::polyline(
        &[
            Point2::new(-1., -1.),
            Point2::new(1., -1.),
            Point2::new(1., 1.),
            Point2::new(-1., 1.),
            Point2::new(-1., -1.),
        ],
        true,
    );
    let delta: f64 = 17.58454421724;
    let trans = Translation2::new(delta.cos(), 0.) * Rotation2::new(delta);
    let clip = clip.transformed(&trans.into());
    let intersections = subject.find_intersection(&clip, Some(OPTIONS)).unwrap();
    assert_eq!(intersections.len(), 2);
}

#[test]
fn problem_case2() {
    let dx = 1.25;
    let dy = 1.5;
    let subject = NurbsCurve2D::<f64>::interpolate_periodic(
        &vec![
            Point2::new(-dx, -dy),
            Point2::new(0., -dy * 1.25),
            Point2::new(dx, -dy),
            Point2::new(dx, dy),
            Point2::new(0., dy * 1.25),
            Point2::new(-dx, dy),
        ],
        3,
        KnotStyle::Centripetal,
    )
    .unwrap();
    let clip = NurbsCurve2D::<f64>::polyline(
        &[
            Point2::new(-1., -1.),
            Point2::new(1., -1.),
            Point2::new(1., 1.),
            Point2::new(-1., 1.),
            Point2::new(-1., -1.),
        ],
        true,
    );

    let delta: f64 = 1.2782841177;
    let trans = Translation2::new(delta.cos(), 0.) * Rotation2::new(delta);
    let clip = clip.transformed(&trans.into());
    let intersections = subject.find_intersection(&clip, Some(OPTIONS)).unwrap();
    assert_eq!(intersections.len(), 2);
}

#[test]
fn greville_abscissae_polyline() {
    let polyline = NurbsCurve2D::<f64>::polyline(
        &[
            Point2::new(0., 0.),
            Point2::new(1., 0.),
            Point2::new(1., 1.),
            Point2::new(0., 1.),
        ],
        false,
    );
    let greville = polyline.greville_abscissae().unwrap();
    polyline
        .control_points()
        .iter()
        .zip(greville.iter())
        .for_each(|(p, t)| {
            let pt = Point2::new(p.x, p.y);
            assert_relative_eq!(polyline.point_at(*t), pt);
        });
}

#[test]
fn greville_abscissae_circle() {
    let curve =
        NurbsCurve2D::<f64>::try_circle(&Point2::origin(), &Vector2::x(), &Vector2::y(), 1.)
            .unwrap();
    let greville = curve.greville_abscissae().unwrap();
    curve
        .control_points()
        .iter()
        .zip(greville.iter())
        .for_each(|(p, t)| {
            let pt = Point2::new(p.x, p.y);
            assert_relative_eq!(curve.point_at(*t), pt);
        });
}
