use roofy::math::vec3::Vec3;

#[test]
fn new() {
    assert_eq!(
        Vec3::new(1.0, 2.0, 3.0),
        Vec3 {
            x: 1.0,
            y: 2.0,
            z: 3.0
        }
    );
}

#[test]
fn splat() {
    assert_eq!(Vec3::splat(2.0), Vec3::new(2.0, 2.0, 2.0));
}

#[test]
fn const_zero() {
    assert_eq!(Vec3::ZERO, Vec3::splat(0.0));
}

#[test]
fn const_one() {
    assert_eq!(Vec3::ONE, Vec3::splat(1.0));
}

#[test]
fn const_right() {
    assert_eq!(Vec3::RIGHT, Vec3::new(1.0, 0.0, 0.0));
}

#[test]
fn const_left() {
    assert_eq!(Vec3::LEFT, Vec3::new(-1.0, 0.0, 0.0));
}

#[test]
fn const_up() {
    assert_eq!(Vec3::UP, Vec3::new(0.0, 1.0, 0.0));
}

#[test]
fn const_down() {
    assert_eq!(Vec3::DOWN, Vec3::new(0.0, -1.0, 0.0));
}

#[test]
fn const_forward() {
    assert_eq!(Vec3::FORWARD, Vec3::new(0.0, 0.0, 1.0));
}

#[test]
fn const_back() {
    assert_eq!(Vec3::BACK, Vec3::new(0.0, 0.0, -1.0));
}

#[test]
fn length_one_is_sqrt_3() {
    assert_eq!(Vec3::ONE.length(), 3_f32.sqrt());
}

#[test]
fn length_squared_one_is_3() {
    assert_eq!(Vec3::ONE.length_squared(), 3.0);
}

#[test]
fn normalize_sets_length_approx_1() {
    let mut v: Vec3 = Vec3::ONE;
    v.normalize();
    assert!((v.length() - 1.0).abs() < 1e-5);
}

#[test]
fn normalize_near_zero_sets_zero() {
    let mut v: Vec3 = Vec3::new(0.0, 0.0, 1e-7);
    v.normalize();
    assert_eq!(v, Vec3::ZERO);
}

#[test]
fn normalized_length_approx_1() {
    assert!((Vec3::ONE.normalized().length() - 1.0).abs() < 1e-5);
}

#[test]
fn normalized_near_zero_is_zero() {
    assert_eq!(Vec3::new(0.0, 0.0, 1e-7).normalized(), Vec3::ZERO);
}

#[test]
fn dot_perpendicular_is_zero() {
    assert_eq!(Vec3::RIGHT.dot(Vec3::UP), 0.0);
}

#[test]
fn dot_parallel_is_one() {
    assert_eq!(Vec3::RIGHT.dot(Vec3::RIGHT), 1.0);
}

#[test]
fn distance() {
    assert_eq!(Vec3::ZERO.distance(Vec3::RIGHT), 1.0);
}

#[test]
fn distance_squared() {
    assert_eq!(Vec3::ZERO.distance_squared(Vec3::ONE), 3.0);
}

#[test]
fn angle() {
    assert_eq!(Vec3::RIGHT.angle(Vec3::UP), 90_f32.to_radians());
}

#[test]
fn angle_degrees() {
    assert_eq!(Vec3::RIGHT.angle_degrees(Vec3::UP), 90.0);
}

#[test]
fn cross_right_up_is_forward() {
    assert_eq!(Vec3::RIGHT.cross(Vec3::UP), Vec3::FORWARD);
}

#[test]
fn project_parallel_stays_same() {
    assert_eq!((Vec3::UP * 5.0).project(Vec3::UP), Vec3::UP * 5.0);
}

#[test]
fn project_scaled_on_normal() {
    assert_eq!((Vec3::UP * 5.0).project(Vec3::UP * 2.0), Vec3::UP * 5.0);
}

#[test]
fn project_perpendicular_is_zero() {
    assert_eq!((Vec3::RIGHT * 5.0).project(Vec3::UP), Vec3::ZERO);
}

#[test]
fn reflect() {
    assert_eq!(
        Vec3::new(1.0, -1.0, 0.0).reflect(Vec3::UP),
        Vec3::new(1.0, 1.0, 0.0)
    );
}

#[test]
fn min() {
    assert_eq!(
        Vec3::ONE.min(Vec3::new(2.0, -2.0, 2.0)),
        Vec3::new(1.0, -2.0, 1.0)
    );
}

#[test]
fn max() {
    assert_eq!(
        Vec3::ONE.max(Vec3::new(2.0, -2.0, 2.0)),
        Vec3::new(2.0, 1.0, 2.0)
    );
}

#[test]
fn lerp_at_0_returns_start() {
    assert_eq!(Vec3::ZERO.lerp(Vec3::ONE, 0.0), Vec3::ZERO);
}

#[test]
fn lerp_at_1_returns_end() {
    assert_eq!(Vec3::ZERO.lerp(Vec3::ONE, 1.0), Vec3::ONE);
}

#[test]
fn lerp_at_half_returns_midpoint() {
    assert_eq!(Vec3::ZERO.lerp(Vec3::ONE, 0.5), Vec3::splat(0.5));
}

#[test]
fn lerp_clamped_at_negative_returns_start() {
    assert_eq!(Vec3::ZERO.lerp_clamped(Vec3::ONE, -1.0), Vec3::ZERO);
}

#[test]
fn lerp_clamped_at_2_returns_end() {
    assert_eq!(Vec3::ZERO.lerp_clamped(Vec3::ONE, 2.0), Vec3::ONE);
}

#[test]
fn add_vec3() {
    assert_eq!(Vec3::ONE + Vec3::ONE, Vec3::splat(2.0));
}

#[test]
fn add_assign_vec3() {
    let mut v: Vec3 = Vec3::ONE;
    v += Vec3::ONE;
    assert_eq!(v, Vec3::splat(2.0));
}

#[test]
fn sub_vec3() {
    assert_eq!(Vec3::ONE - Vec3::ONE, Vec3::ZERO);
}

#[test]
fn sub_assign_vec3() {
    let mut v: Vec3 = Vec3::ONE;
    v -= Vec3::ONE;
    assert_eq!(v, Vec3::ZERO);
}

#[test]
fn mul_vec3() {
    assert_eq!(Vec3::splat(2.0) * Vec3::splat(2.0), Vec3::splat(4.0));
}

#[test]
fn mul_assign_vec3() {
    let mut v: Vec3 = Vec3::splat(2.0);
    v *= Vec3::splat(2.0);
    assert_eq!(v, Vec3::splat(4.0));
}

#[test]
fn mul_f32() {
    assert_eq!(Vec3::splat(2.0) * 2.0, Vec3::splat(4.0));
}

#[test]
fn mul_assign_f32() {
    let mut v: Vec3 = Vec3::splat(2.0);
    v *= 2.0;
    assert_eq!(v, Vec3::splat(4.0));
}

#[test]
fn div_vec3() {
    assert_eq!(Vec3::splat(2.0) / Vec3::splat(2.0), Vec3::ONE);
}

#[test]
fn div_assign_vec3() {
    let mut v: Vec3 = Vec3::splat(2.0);
    v /= Vec3::splat(2.0);
    assert_eq!(v, Vec3::ONE);
}

#[test]
fn div_f32() {
    assert_eq!(Vec3::splat(2.0) / 2.0, Vec3::ONE);
}

#[test]
fn div_assign_f32() {
    let mut v: Vec3 = Vec3::splat(2.0);
    v /= 2.0;
    assert_eq!(v, Vec3::ONE);
}

#[test]
fn partial_eq() {
    assert!(Vec3::ZERO == Vec3::ZERO);
}

#[test]
fn partial_eq_slightly_different() {
    assert!(Vec3::ZERO == Vec3::new(0.0, 0.0, 1e-7));
}

#[test]
fn not_partial_eq() {
    assert!(Vec3::ZERO != Vec3::ONE);
}

#[test]
fn neg() {
    assert_eq!(-Vec3::ONE, Vec3::splat(-1.0));
}

#[test]
fn default() {
    assert_eq!(Vec3::default(), Vec3::ZERO);
}
