mod camera {
    use approx::assert_abs_diff_eq;
    use winit::dpi::PhysicalSize;

    use crate::screen::Camera;

    #[test]
    fn camera_is_to_scale_and_transforms_properly() {
        // The camera is a projection from [-WHE, +WHE] to [0, 1]
        let mut camera = Camera {
            width: 512.0,
            height: 1024.0,
            ..Default::default()
        };

        let vpjm = camera.build_view_projection_matrix();
        assert_abs_diff_eq!(
            vpjm.project_point3(glam::Vec3::new(0.0, 0.0, 0.0)),
            glam::Vec3::new(0.5, 0.5, 0.0),
        );
        assert_abs_diff_eq!(
            vpjm.project_point3(glam::Vec3::new(1.0, 1.0, 0.0)),
            glam::Vec3::new(1.0, 0.75, 0.0)
        );

        camera.transform.translation.x = -0.5;
        camera.transform.translation.y = -0.5;
        let vpjm = camera.build_view_projection_matrix();

        assert_abs_diff_eq!(
            vpjm.project_point3(glam::Vec3::new(0.0, 0.0, 0.0)),
            glam::Vec3::new(0.25, 0.25, 0.0)
        );
        assert_abs_diff_eq!(
            vpjm.project_point3(glam::Vec3::new(1.0, 1.0, 0.0)),
            glam::Vec3::new(0.75, 0.5, 0.0)
        );

        camera.transform *= glam::Affine3A::from_axis_angle(glam::Vec3::Z, std::f32::consts::PI);
        let vpjm = camera.build_view_projection_matrix();

        assert_abs_diff_eq!(
            vpjm.project_point3(glam::Vec3::new(0.0, 0.0, 0.0)),
            glam::Vec3::new(0.75, 0.5, 0.0)
        );
        assert_abs_diff_eq!(
            vpjm.project_point3(glam::Vec3::new(1.0, 1.0, 0.0)),
            glam::Vec3::new(0.25, 0.25, 0.0)
        );

        camera.transform.translation.x = 0.0;
        camera.transform.translation.y = 0.0;
        let vpjm = camera.build_view_projection_matrix();

        assert_abs_diff_eq!(
            vpjm.project_point3(glam::Vec3::new(0.0, 0.0, 0.0)),
            glam::Vec3::new(1.0, 0.75, 0.0),
        );
        assert_abs_diff_eq!(
            vpjm.project_point3(glam::Vec3::new(1.0, 1.0, 0.0)),
            glam::Vec3::new(0.5, 0.5, 0.0)
        );
    }

    #[test]
    fn camera_updates_aspect_properly() {
        let mut camera = Camera {
            width: 400.0,
            height: 400.0,
            ..Default::default()
        };

        // The ratio of screen to camera is about the same (is the same in this case)
        assert_eq!(
            camera.update_aspect(PhysicalSize::new(400, 400)),
            Some((1.0, 1.0))
        );

        // we dont update it if we don't have to.
        assert_eq!(camera.update_aspect(PhysicalSize::new(400, 400)), None);

        // window is too wide, scale down width
        assert_eq!(
            camera.update_aspect(PhysicalSize::new(400, 200)),
            Some((0.5, 1.0))
        );

        // window is too tall, scale down height
        assert_eq!(
            camera.update_aspect(PhysicalSize::new(200, 400)),
            Some((1.0, 0.5))
        )
    }
}