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

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(PhysicsPlugins::default())
        .insert_resource(ClearColor(WHITE.into()))
        .add_systems(Startup, setup)
        .add_systems(Update, move_player)
        .add_systems(Update, follow_player)
        .add_systems(Update, draw_gizmos)
        .add_systems(
            Update,
            event_detection.run_if(on_message::<CollisionStart>.or(on_message::<CollisionEnd>)),
        )
        .add_observer(set_tree_position)
        .run();
}

#[derive(Component)]
struct Player;

#[derive(Component)]
struct RenderGizmo;

#[derive(Component)]
struct PlayArea;

#[derive(Component, Debug)]
struct TreePos(isize);

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    commands.spawn(Camera2d);

    // Spawn "trees" (which are also sensors)
    [
        ("TREE -3", -3),
        ("TREE -2", -2),
        ("TREE -2", -1),
        ("TREE 1", 1),
        ("TREE 2", 2),
        ("TREE 3", 3),
    ]
    .iter()
    .for_each(|(label, pos)| {
        commands.spawn((
            RigidBody::Static,
            Collider::rectangle(100.0, 250.0),
            Mesh2d(meshes.add(Rectangle::new(100.0, 250.0))),
            MeshMaterial2d(materials.add(Color::from(GREEN))),
            TreePos(*pos),
            children![Text2d::new(*label),],
        ));
    });

    commands.spawn((
        Player,
        RigidBody::Kinematic,
        ConstantForce::default(),
        Collider::rectangle(50.0, 50.0),
        Mesh2d(meshes.add(Rectangle::new(50.0, 50.0))),
        MeshMaterial2d(materials.add(Color::from(RED))),
        children![
            (
                Text2d::new("PLAYER"),
                Transform::from_scale(Vec3::new(0.5, 0.5, 0.5)),
            ),
            (
                Text2d::new("PLAY AREA"),
                Transform::from_xyz(0.0, 212.0, 1.0),
                TextColor(BLACK.into())
            ),
            (
                RenderGizmo,
                PlayArea,
                Visibility::Visible,
                Transform::from_scale(Vec3::new(400.0, 400.0, 0.0)),
            ),
        ],
    ));
}

fn set_tree_position(event: On<Add, TreePos>, tree_pos: Query<&TreePos>, mut commands: Commands) {
    let TreePos(pos) = tree_pos.get(event.entity).unwrap();
    let x = if (*pos) > 0 {
        (200.0 * (*pos) as f32) - 100.0
    } else {
        (200.0 * (*pos) as f32) + 100.0
    };
    let t = Transform::from_xyz(x, 0.0, 0.0);
    commands.entity(event.entity).insert(t);
}

fn move_player(
    keyboard_input: Res<ButtonInput<KeyCode>>,
    mut player: Single<Forces, With<Player>>,
) {
    const SPEED: f32 = 5.0;
    if keyboard_input.pressed(KeyCode::ArrowLeft) {
        player.apply_local_force(Vec2::NEG_X * SPEED);
    } else if keyboard_input.pressed(KeyCode::ArrowRight) {
        player.apply_local_force(Vec2::X * SPEED);
    }
}

fn follow_player(
    player: Query<&Transform, (With<Player>, Changed<Transform>)>,
    mut camera: Single<&mut Transform, (With<Camera2d>, Without<Player>)>,
) {
    player.iter().for_each(|pt| {
        camera.translation = pt.translation;
    });
}

fn draw_gizmos(mut gizmos: Gizmos, play_area: Single<&GlobalTransform, With<RenderGizmo>>) {
    gizmos.rect_2d(
        play_area.translation().truncate(),
        play_area.scale().truncate(),
        MAGENTA,
    )
}

fn event_detection(
    mut start_events: MessageReader<CollisionStart>,
    mut end_events: MessageReader<CollisionEnd>,
    trees: Query<&TreePos>,
    keyboard_input: Res<ButtonInput<KeyCode>>,
) {
    debug_assert!(!(start_events.is_empty() && end_events.is_empty()));
    let s = start_events.read().map(
        |CollisionStart {
             collider1,
             collider2,
             ..
         }| (collider1, collider2),
    );
    let e = end_events.read().map(
        |CollisionEnd {
             collider1,
             collider2,
             ..
         }| (collider1, collider2),
    );
    let mut events = s.chain(e);
    let current_tree = events.find_map(|(a, b)| {
        info!("{a:?}, {b:?}");
        trees.get(*a).or(trees.get(*b)).ok()
    });
    info!("{:?}", current_tree);
    if keyboard_input.pressed(KeyCode::ArrowLeft) {
        // moving left
    } else if keyboard_input.pressed(KeyCode::ArrowRight) {
        // moving right
    }
}
