/*
This checks collisions between different capsules for narrow cases.

The test names are constructed like this:
`<shape1>_<shape2>_match<dimension>[_not]`

The shapes (`shape1`, `shape2`) are one of these shapes:
* `point`: Just a point represented as a capsule
* `sphere`: Just a sphere represented as a capsule
* `cylindrical`: A real capsule; collision takes place at the cylindrical part
* `spherical`: A real capsule; collision takes place at the spherical part

Each combination is tested once with different options.

If both shapes are the same, the test name is constructed like this:
`<shape>_match<dimension>[_not]`

The tests are only executed for the most common dimensions 2 and 3 while using `f32` as element type.

Each test also has a variant suffixed with "_not", which tests a case, where the shapes should just not collide.
*/

use collide::Collider;
use collide_capsule::Capsule;
use inner_space::InnerSpace;
use simple_vectors::Vector;

fn check<const N: usize>(a: Capsule<Vector<f32, N>>, b: Capsule<Vector<f32, N>>) {
    assert!(a.check_collision(&b))
}

fn check_not<const N: usize>(a: Capsule<Vector<f32, N>>, b: Capsule<Vector<f32, N>>) {
    assert!(!a.check_collision(&b))
}

#[test]
fn point_match2() {
    check(
        Capsule::point(Vector::new([0.0; 2])),
        Capsule::point(Vector::new([0.0; 2])),
    );
}

#[test]
fn point_match2_not() {
    check_not(
        Capsule::point(Vector::new([0.0; 2])),
        Capsule::point(Vector::new([0.01; 2])),
    );
}

#[test]
fn point_match3() {
    check(
        Capsule::point(Vector::new([1.0, 2.0, 3.0])),
        Capsule::point(Vector::new([1.0, 2.0, 3.0])),
    );
}

#[test]
fn point_match3_not() {
    check_not(
        Capsule::point(Vector::new([1.0, 2.0, 3.0])),
        Capsule::point(Vector::new([1.01, 2.0, 3.0])),
    );
}

#[test]
fn point_sphere_match2() {
    check(
        Capsule::point(Vector::new([1.0, 0.0])),
        Capsule::sphere(Vector::new([-1.0, 0.0]), 2.0),
    );
}

#[test]
fn point_sphere_match2_not() {
    check_not(
        Capsule::point(Vector::new([1.0, 0.0])),
        Capsule::sphere(Vector::new([-1.01, 0.0]), 2.0),
    );
}

#[test]
fn point_sphere_match3() {
    check(
        Capsule::point(Vector::new([0.577; 3])),
        Capsule::sphere(Vector::new([0.0; 3]), 1.0),
    );
}

#[test]
fn point_sphere_match3_not() {
    check_not(
        Capsule::point(Vector::new([0.578; 3])),
        Capsule::sphere(Vector::new([0.0; 3]), 1.0),
    );
}

#[test]
fn point_cylindrical_match2() {
    check(
        Capsule::point(Vector::new([1.414, -1.414])),
        Capsule::new(2.0, Vector::new([-1.0; 2]), Vector::new([1.0; 2])),
    );
}

#[test]
fn point_cylindrical_match2_not() {
    check_not(
        Capsule::point(Vector::new([1.415, -1.415])),
        Capsule::new(2.0, Vector::new([-1.0; 2]), Vector::new([1.0; 2])),
    );
}

#[test]
fn point_cylindrical_match3() {
    check(
        Capsule::point(Vector::new([0.0, 0.0, 0.0])),
        Capsule::new(
            5.0,
            Vector::new([3.535, -3.535, -2.0]),
            Vector::new([3.535, -3.535, 2.0]),
        ),
    );
}

#[test]
fn point_cylindrical_match3_not() {
    check_not(
        Capsule::point(Vector::new([0.0, 0.01, 0.0])),
        Capsule::new(
            5.0,
            Vector::new([3.535, -3.535, -2.0]),
            Vector::new([3.535, -3.535, 2.0]),
        ),
    );
}

#[test]
fn point_spherical_match2() {
    check(
        Capsule::point(Vector::new([3.0, 0.0])),
        Capsule::new(1.0, Vector::new([-2.0, 0.0]), Vector::new([2.0, 0.0])),
    );
}

#[test]
fn point_spherical_match2_not() {
    check_not(
        Capsule::point(Vector::new([3.0, 0.01])),
        Capsule::new(1.0, Vector::new([-2.0, 0.0]), Vector::new([2.0, 0.0])),
    );
}

#[test]
fn point_spherical_match3() {
    check(
        Capsule::point(Vector::new([2.0; 3]) + Vector::new([1.414, -1.414, 0.0])),
        Capsule::new(2.0, Vector::new([-2.0; 3]), Vector::new([2.0; 3])),
    );
}

#[test]
fn point_spherical_match3_not() {
    check_not(
        Capsule::point(Vector::new([2.0; 3]) + Vector::new([1.415, -1.415, 0.0])),
        Capsule::new(2.0, Vector::new([-2.0; 3]), Vector::new([2.0; 3])),
    );
}

#[test]
fn sphere_match2() {
    check(
        Capsule::sphere(Vector::new([1.0, 0.0]), 100.0),
        Capsule::sphere(Vector::new([-100.0, 0.0]), 1.0),
    );
}

#[test]
fn sphere_match2_not() {
    check_not(
        Capsule::sphere(Vector::new([1.01, 0.0]), 100.0),
        Capsule::sphere(Vector::new([-100.0, 0.0]), 1.0),
    );
}

#[test]
fn sphere_match3() {
    check(
        Capsule::sphere(Vector::new([0.0; 3]), 5.0),
        Capsule::sphere(Vector::new([5.773; 3]), 5.0),
    );
}

#[test]
fn sphere_match3_not() {
    check_not(
        Capsule::sphere(Vector::new([0.0; 3]), 4.99),
        Capsule::sphere(Vector::new([5.773; 3]), 5.0),
    );
}

#[test]
fn sphere_cylindrical_match2() {
    check(
        Capsule::sphere(Vector::new([1.414, -1.414]), 1.0),
        Capsule::new(1.0, Vector::new([-1.0; 2]), Vector::new([1.0; 2])),
    );
}

#[test]
fn sphere_cylindrical_match2_not() {
    check_not(
        Capsule::sphere(Vector::new([1.414, -1.414]), 1.0),
        Capsule::new(0.99, Vector::new([-1.0; 2]), Vector::new([1.0; 2])),
    );
}

#[test]
fn sphere_cylindrical_match3() {
    check(
        Capsule::sphere(Vector::new([0.0, 0.0, 4.0]), 2.0),
        Capsule::new(
            2.0,
            Vector::new([-1.0, -1.0, 0.0]),
            Vector::new([1.0, 1.0, 0.0]),
        ),
    );
}

#[test]
fn sphere_cylindrical_match3_not() {
    check_not(
        Capsule::sphere(Vector::new([0.0, 0.0, 4.01]), 2.0),
        Capsule::new(
            2.0,
            Vector::new([-1.0, -1.0, 0.0]),
            Vector::new([1.0, 1.0, 0.0]),
        ),
    );
}

#[test]
fn sphere_spherical_match2() {
    check(
        Capsule::sphere(Vector::new([2.0, 5.0]), 2.0),
        Capsule::new(1.0, Vector::new([-4.0; 2]), Vector::new([2.0; 2])),
    );
}

#[test]
fn sphere_spherical_match2_not() {
    check_not(
        Capsule::sphere(Vector::new([2.0, 5.01]), 2.0),
        Capsule::new(1.0, Vector::new([-4.0; 2]), Vector::new([2.0; 2])),
    );
}

#[test]
fn sphere_spherical_match3() {
    check(
        Capsule::sphere(Vector::new([5.0, 0.0, 0.0]) + Vector::new([-4.041; 3]), 2.0),
        Capsule::new(
            5.0,
            Vector::new([5.0, 0.0, 0.0]),
            Vector::new([6.0, 0.0, 0.0]),
        ),
    );
}

#[test]
fn sphere_spherical_match3_not() {
    check_not(
        Capsule::sphere(Vector::new([5.0, 0.0, 0.0]) + Vector::new([-4.042; 3]), 2.0),
        Capsule::new(
            5.0,
            Vector::new([5.0, 0.0, 0.0]),
            Vector::new([6.0, 0.0, 0.0]),
        ),
    );
}

#[test]
fn cylindrical_match2() {
    check(
        Capsule::new(2.0, Vector::new([-1.0, 1.0]), Vector::new([-1.0, -1.0])),
        Capsule::new(1.0, Vector::new([2.0, 0.0]), Vector::new([2.0, -2.0])),
    );
}

#[test]
fn cylindrical_match2_not() {
    check_not(
        Capsule::new(2.0, Vector::new([-1.0, 1.0]), Vector::new([-1.01, -1.0])),
        Capsule::new(1.0, Vector::new([2.0, 0.0]), Vector::new([2.0, -2.0])),
    );
}

#[test]
fn cylindrical_match3() {
    check(
        Capsule::new(
            1.0,
            Vector::new([-1.0, -1.0, 0.0]),
            Vector::new([1.0, 1.0, 0.0]),
        ),
        Capsule::new(
            1.0,
            Vector::new([-1.414, 1.414, -5.0]),
            Vector::new([-1.414, 1.414, 5.0]),
        ),
    );
}

#[test]
fn cylindrical_match3_not() {
    check_not(
        Capsule::new(
            1.0,
            Vector::new([-1.0, -1.0, 0.0]),
            Vector::new([1.0, 1.0, 0.0]),
        ),
        Capsule::new(
            1.0,
            Vector::new([-1.415, 1.415, -5.0]),
            Vector::new([-1.415, 1.415, 5.0]),
        ),
    );
}

#[test]
fn cylindrical_spherical_match2() {
    check(
        Capsule::new(1.0, Vector::new([0.0, -1.0]), Vector::new([0.0, 1.0])),
        Capsule::new(2.0, Vector::new([-5.0, 0.0]), Vector::new([-3.0, 0.0])),
    );
}

#[test]
fn cylindrical_spherical_match2_not() {
    check_not(
        Capsule::new(1.0, Vector::new([0.0, -1.0]), Vector::new([0.0, 1.0])),
        Capsule::new(2.0, Vector::new([-5.0, 0.0]), Vector::new([-3.01, 0.0])),
    );
}

#[test]
fn cylindrical_spherical_match3() {
    let dir = Vector::new([1.0, 1.0, -1.0]).normalize();
    let start = Vector::new([-2.0; 3]) + dir * 2.12;
    check(
        Capsule::new(1.0, Vector::new([-5.0; 3]), Vector::new([5.0; 3])),
        Capsule::new(1.0, start, start + dir),
    );
}

#[test]
fn cylindrical_spherical_match3_not() {
    let dir = Vector::new([1.0, 1.0, -1.0]).normalize();
    let start = Vector::new([-2.0; 3]) + dir * 2.13;
    check_not(
        Capsule::new(1.0, Vector::new([-5.0; 3]), Vector::new([5.0; 3])),
        Capsule::new(1.0, start, start),
    );
}

#[test]
fn spherical_match2() {
    check(
        Capsule::new(2.0, Vector::new([2.0, 5.0]), Vector::new([4.0, 5.0])),
        Capsule::new(1.0, Vector::new([-4.0; 2]), Vector::new([2.0; 2])),
    );
}

#[test]
fn spherical_match2_not() {
    check_not(
        Capsule::new(2.0, Vector::new([2.0, 5.01]), Vector::new([4.0, 5.01])),
        Capsule::new(1.0, Vector::new([-4.0; 2]), Vector::new([2.0; 2])),
    );
}

#[test]
fn spherical_match3() {
    let start = Vector::new([5.0, 0.0, 0.0]) + Vector::new([4.041; 3]);
    check(
        Capsule::new(2.0, start, start + Vector::new([0.0, 5.0, 0.0])),
        Capsule::new(
            5.0,
            Vector::new([0.0, 0.0, 0.0]),
            Vector::new([5.0, 0.0, 0.0]),
        ),
    );
}

#[test]
fn spherical_match3_not() {
    let start = Vector::new([5.0, 0.0, 0.0]) + Vector::new([4.042; 3]);
    check_not(
        Capsule::new(2.0, start, start + Vector::new([0.0, 5.0, 0.0])),
        Capsule::new(
            5.0,
            Vector::new([0.0, 0.0, 0.0]),
            Vector::new([5.0, 0.0, 0.0]),
        ),
    );
}
