use bevy::prelude::*;

use crate::core::game_state::DispawnOnGameOver;
use crate::map::map::MapBounds;

#[derive(Component)]
#[require(Transform, DispawnOnGameOver)]
pub struct PhysicsBody {
    pub mass: f32,
    pub force: Vec2,
    pub velocity: Vec2,
    pub drag: f32, // velocity multiplied each physics update by (1-drag)
}

impl Default for PhysicsBody {
    fn default() -> Self {
        Self { mass: 1.0, force: Vec2::ZERO, velocity: Vec2::ZERO, drag: 0.0 }
    }
}

pub fn physics_integrate(
    bounds: Res<MapBounds>,
    mut query: Query<(&mut Transform, &mut PhysicsBody)>,
    time: Res<Time<Fixed>>,
) {
    for (mut transform, mut physicsbody) in &mut query {
        let force = physicsbody.force;
        let mass = physicsbody.mass;
        let drag = physicsbody.drag;
        let delta_secs = time.delta_secs();

        physicsbody.velocity += force * delta_secs / mass;
        physicsbody.velocity *= 1.0 - drag;

        map_bounce(&mut physicsbody.velocity, &transform, &bounds);

        transform.translation.x += physicsbody.velocity.x * delta_secs;
        transform.translation.y += physicsbody.velocity.y * delta_secs;

        physicsbody.force = Vec2::ZERO;
    }
}

fn map_bounce(velocity: &mut Vec2, transform: &Transform, bounds: &MapBounds) {
    if (transform.translation.x < bounds.min.x && velocity.x < 0.0)
        || (transform.translation.x > bounds.max.x && velocity.x > 0.0)
    {
        velocity.x *= -1.5;
    }

    if (transform.translation.y < bounds.min.y && velocity.y < 0.0)
        || (transform.translation.y > bounds.max.y && velocity.y > 0.0)
    {
        velocity.y *= -1.5;
    }
}
