use bevy::{color::palettes::css::*, prelude::*};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins {})
        .insert_resource(ClearColor(WHITE.into()))
        .add_message::<CameraMovement>()
        .add_systems(Startup, setup_2d)
        .add_systems(Update, (move_camera, track_camera_movement, parallax))
        .run();
}

#[derive(Component)]
struct ParallaxDistance(f32);

fn setup_2d(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    commands.spawn((
        Camera2d,
        AmbientLight {
            brightness: 160.0,
            ..default()
        },
    ));

    commands.spawn((
        Mesh2d(meshes.add(Rectangle::new(100.0, 100.0))),
        MeshMaterial2d(materials.add(Color::from(RED))),
        Transform::from_xyz(0.0, 0.0, -1.0),
        ParallaxDistance(1.0),
        children![(
            Text2d::new("Parallax Distance: 1"),
            TextColor(BLACK.into()),
            Transform::from_xyz(150.0, 50.0, 0.0)
        )],
    ));

    commands.spawn((
        Mesh2d(meshes.add(Rectangle::new(100.0, 100.0))),
        MeshMaterial2d(materials.add(Color::from(GREEN))),
        Transform::from_xyz(0.0, 0.0, -5.0),
        ParallaxDistance(5.0),
        children![(
            Text2d::new("Parallax Distance: 5"),
            TextColor(BLACK.into()),
            Transform::from_xyz(150.0, 0.0, 0.0)
        )],
    ));

    commands.spawn((
        Mesh2d(meshes.add(Rectangle::new(100.0, 100.0))),
        MeshMaterial2d(materials.add(Color::from(BLUE))),
        Transform::from_xyz(0.0, 0.0, -10.0),
        ParallaxDistance(10.0),
        children![(
            Text2d::new("Parallax Distance: 10"),
            TextColor(BLACK.into()),
            Transform::from_xyz(150.0, -50.0, 0.0)
        )],
    ));
}

fn move_camera(keys: Res<ButtonInput<KeyCode>>, mut camera: Single<&mut Transform, With<Camera>>) {
    const SPEED: f32 = 5.0;
    if keys.pressed(KeyCode::ArrowLeft) {
        camera.translation.x += SPEED;
    } else if keys.pressed(KeyCode::ArrowRight) {
        camera.translation.x -= SPEED;
    }

    if keys.pressed(KeyCode::ArrowUp) {
        camera.translation.y -= SPEED;
    } else if keys.pressed(KeyCode::ArrowDown) {
        camera.translation.y += SPEED;
    }
}

#[derive(Message)]
struct CameraMovement(Vec3);

fn track_camera_movement(
    mut events: MessageWriter<CameraMovement>,
    camera: Query<&Transform, (With<Camera>, Changed<Transform>)>,
    mut last: Local<Vec3>,
) {
    camera.iter().for_each(|Transform { translation, .. }| {
        events.write(CameraMovement(*last - *translation));
        *last = *translation;
    });
}

fn parallax(
    mut events: MessageReader<CameraMovement>,
    mut query: Query<(&mut Transform, &ParallaxDistance)>,
) {
    events.read().for_each(|CameraMovement(v)| {
        query.iter_mut().for_each(|(mut t, ParallaxDistance(d))| {
            t.translation -= v / (20.0 / d);
        });
    });
}
