use ulid::Ulid;

use crate::game::physics::{PhysicsObject, PhysicsState};

fn generate_physics_state(desc: impl IntoIterator<Item = (Ulid, PhysicsObject)>) -> PhysicsState {
    PhysicsState {
        objects: desc.into_iter().collect(),
    }
}

#[test]
fn lerp_0_gives_prev_state() {
    let obj_id = Ulid::new();

    let prev = generate_physics_state(vec![(
        obj_id,
        PhysicsObject {
            position: glam::Vec3::new(0.0, 0.0, 0.0),
        },
    )]);

    let current = generate_physics_state(vec![(
        obj_id,
        PhysicsObject {
            position: glam::Vec3::new(1.0, 0.0, 0.0),
        },
    )]);

    assert_eq!(prev, current.lerp(&prev, 0.0));
}

#[test]
fn lerp_1_gives_current_state() {
    let obj_id = Ulid::new();

    let prev = generate_physics_state(vec![(
        obj_id,
        PhysicsObject {
            position: glam::Vec3::new(0.0, 0.0, 0.0),
        },
    )]);

    let current = generate_physics_state(vec![(
        obj_id,
        PhysicsObject {
            position: glam::Vec3::new(1.0, 0.0, 0.0),
        },
    )]);

    assert_eq!(current, current.lerp(&prev, 1.0));
}

#[test]
fn lerp_is_linear() {
    let obj_id = Ulid::new();

    let prev = generate_physics_state(vec![(
        obj_id,
        PhysicsObject {
            position: glam::Vec3::new(0.0, 0.0, 0.0),
        },
    )]);

    let current = generate_physics_state(vec![(
        obj_id,
        PhysicsObject {
            position: glam::Vec3::new(1.0, 0.0, 0.0),
        },
    )]);

    let betwixt = generate_physics_state(vec![(
        obj_id,
        PhysicsObject {
            position: glam::Vec3::new(0.5, 0.0, 0.0),
        },
    )]);

    assert_eq!(betwixt, current.lerp(&prev, 0.5));
}

#[test]
fn lerp_copies_unique_current_objects_and_lerps_shared_objects() {
    let obj1_id = Ulid::new();
    let obj2_id = Ulid::new();
    let obj3_id = Ulid::new();

    let prev = generate_physics_state(vec![
        (
            obj1_id,
            PhysicsObject {
                position: glam::Vec3::new(0.0, 0.0, 0.0),
            },
        ),
        (
            obj2_id,
            PhysicsObject {
                position: glam::Vec3::new(1.0, 0.0, 0.0),
            },
        ),
    ]);

    let curr = generate_physics_state(vec![
        (
            obj3_id,
            PhysicsObject {
                position: glam::Vec3::new(0.0, 0.0, 0.0),
            },
        ),
        (
            obj2_id,
            PhysicsObject {
                position: glam::Vec3::new(0.0, 1.0, 1.0),
            },
        ),
    ]);

    let betwixt = generate_physics_state(vec![
        (
            obj3_id,
            PhysicsObject {
                position: glam::Vec3::new(0.0, 0.0, 0.0),
            },
        ),
        (
            obj2_id,
            PhysicsObject {
                position: glam::Vec3::new(0.5, 0.5, 0.5),
            },
        ),
    ]);

    assert_eq!(betwixt, curr.lerp(&prev, 0.5));
}