use kalast::{Object3D, MINIMUM_PRECISION};
use std::path::Path;

#[test]
fn parse_simple_facet() {
    // single triangulate facet centered 2 meter side
    let obj = Object3D::new(Path::new("rsc/obj/facet.obj"));

    // checking it's only one facet
    assert_eq!(obj.number_faces(), 1);

    // getting that facet information
    let face = obj.face_iter_all().nth(0).unwrap();
    let vertices = obj.vertices();

    // check 3 vertices positions
    assert!(relative_eq!(
        vertices[(0, 0)],
        -2. / 3.,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        vertices[(1, 0)],
        2. / 3.,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        vertices[(2, 0)],
        0.,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        vertices[(0, 1)],
        -2. / 3.,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        vertices[(1, 1)],
        -4. / 3.,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        vertices[(2, 1)],
        0.,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        vertices[(0, 2)],
        4. / 3.,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        vertices[(1, 2)],
        2. / 3.,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        vertices[(2, 2)],
        0.,
        epsilon = MINIMUM_PRECISION
    ));

    // checking it's centered
    assert!(relative_eq!(face[0], 0., epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(face[1], 0., epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(face[2], 0., epsilon = MINIMUM_PRECISION));

    // checking it's poiting upward
    assert!(relative_eq!(face[3], 0., epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(face[4], 0., epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(face[5], 1., epsilon = MINIMUM_PRECISION));

    // checking it's the center position in spherical coordinates
    assert!(relative_eq!(face[6], 0., epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(face[7], 0., epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(face[8], 0., epsilon = MINIMUM_PRECISION));

    // checking area
    assert!(relative_eq!(face[9], 2., epsilon = MINIMUM_PRECISION));
}

#[test]
fn parse_complex_asteroid() {
    // test vertices, faces, centers, normals, areas and sphericals: shape,
    // first and last points
    let path = Path::new("rsc/obj/dimorphos.obj");
    let mut obj = Object3D::new(path);
    let number_faces = obj.number_faces();

    // test vertices
    assert_eq!((3, 4902), obj.vertices().shape());

    let first_vertex = obj.vertices().column(0);
    let mut first_it = first_vertex.iter();
    assert_eq!(*first_it.next().unwrap(), 0.);
    assert_eq!(*first_it.next().unwrap(), 0.);
    assert_eq!(*first_it.next().unwrap(), 63.);

    let last_vertex = obj.vertices().column(obj.vertices().ncols() - 1);
    let mut last_it = last_vertex.iter();
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        5.01333,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        -0.248387,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        -62.875687,
        epsilon = MINIMUM_PRECISION,
    ));

    // test faces
    assert_eq!((3, 9800), obj.faces().shape());

    let first_face = obj.faces().column(0);
    let mut first_it = first_face.iter();
    assert_eq!(*first_it.next().unwrap(), 2);
    assert_eq!(*first_it.next().unwrap(), 51);
    assert_eq!(*first_it.next().unwrap(), 0);

    let last_face = obj.faces().column(number_faces - 1);
    let mut last_it = last_face.iter();
    assert_eq!(*last_it.next().unwrap(), 4900);
    assert_eq!(*last_it.next().unwrap(), 50);
    assert_eq!(*last_it.next().unwrap(), 49);

    obj.recompute_faces();

    // test centers
    assert_eq!((3, 9800), obj.centers().shape());

    let first_center = obj.centers().column(0).into_owned();
    let mut first_it = first_center.iter();
    assert!(relative_eq!(
        *first_it.next().unwrap(),
        3.345524,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *first_it.next().unwrap(),
        0.082796,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *first_it.next().unwrap(),
        62.917125,
        epsilon = MINIMUM_PRECISION,
    ));

    let last_center = obj.centers().column(number_faces - 1).into_owned();
    let mut last_it = last_center.iter();
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        8.352258,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        -0.165265,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        -62.62738,
        epsilon = MINIMUM_PRECISION,
    ));

    // test normals
    assert_eq!((3, 9800), obj.normals().shape());

    let first_normal = obj.normals().column(0).into_owned();
    let mut first_it = first_normal.iter();
    assert!(relative_eq!(
        *first_it.next().unwrap(),
        0.02474,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *first_it.next().unwrap(),
        0.000987,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *first_it.next().unwrap(),
        0.999693,
        epsilon = MINIMUM_PRECISION,
    ));

    let last_normal = obj.normals().column(number_faces - 1).into_owned();
    let mut last_it = last_normal.iter();
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        0.074235,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        -0.002963,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        -0.997236,
        epsilon = MINIMUM_PRECISION,
    ));

    // test sphericals
    assert_eq!((3, 9800), obj.sphericals().shape());

    let first_spherical = obj.sphericals().column(0).into_owned();
    let mut first_it = first_spherical.iter();
    assert!(relative_eq!(
        *first_it.next().unwrap(),
        0.024743,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *first_it.next().unwrap(),
        1.517657,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *first_it.next().unwrap(),
        63.006063,
        epsilon = MINIMUM_PRECISION,
    ));

    let last_spherical = obj.sphericals().column(number_faces - 1).into_owned();
    let mut last_it = last_spherical.iter();
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        -0.019784,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        -1.438189,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        *last_it.next().unwrap(),
        63.182088,
        epsilon = MINIMUM_PRECISION,
    ));

    // test areas
    assert_eq!((1, 9800), obj.areas().shape());
    assert_eq!(0.624045, obj.areas()[0]);
    assert_eq!(1.243769, obj.areas()[number_faces - 1]);
}

#[test]
fn parse_equator() {
    // single sphere
    let mut obj = Object3D::new(Path::new("rsc/obj/sphere.obj"));

    // checking number of faces before applying the mask
    assert_eq!(obj.number_faces(), 960);

    // checking the first face before applying the mask
    let face = obj.face_iter_all().nth(0).unwrap();
    assert!(relative_eq!(
        face[0],
        298.997528,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[1],
        -41.056031,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[2],
        -395.00768,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(face[3], 0.633159, epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(
        face[4],
        -0.062361,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[5],
        -0.771506,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[6],
        -0.136459,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[7],
        -0.918368,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[8],
        497.107822,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[9],
        2661.112178,
        epsilon = MINIMUM_PRECISION
    ));

    // apply the mask
    obj.set_equator_mask(None);

    // checking number of faces after applying the mask
    assert_eq!(obj.number_faces(), 64);

    // checking the first face after applying the mask
    let face = obj.face_iter_all().nth(0).unwrap();
    assert!(relative_eq!(
        face[0],
        490.454163,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[1],
        -64.405357,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[2],
        32.515083,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(face[3], 0.990438, epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(face[4], -0.09755, epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(face[5], 0.09755, epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(
        face[6],
        -0.130571,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(face[7], 0.065637, epsilon = MINIMUM_PRECISION));
    assert!(relative_eq!(
        face[8],
        495.732354,
        epsilon = MINIMUM_PRECISION
    ));
    assert!(relative_eq!(
        face[9],
        4803.45851,
        epsilon = MINIMUM_PRECISION
    ));
}
