//! Avian physics from [`PhysicsBody`] authoring data.

use avian3d::prelude::*;
use bevy::prelude::*;

use crate::{
    authoring_component_active, AuthoringComponentStates, AuthoringRigidBody, ColliderDesc,
    ColliderShapeDesc, InspectorOrder, LevelObject, PhysicsBody, RigidBodyDesc,
    COMPONENT_COLLIDER_DESC, COMPONENT_PHYSICS_BODY, COMPONENT_RIGID_BODY_DESC,
};

#[allow(clippy::type_complexity)]
pub fn hydrate_physics(
    mut commands: Commands,
    physics: Query<
        (
            Entity,
            &PhysicsBody,
            Option<&AuthoringComponentStates>,
            Option<&InspectorOrder>,
        ),
        (
            With<LevelObject>,
            Or<(
                Changed<PhysicsBody>,
                Changed<AuthoringComponentStates>,
                Changed<InspectorOrder>,
            )>,
        ),
    >,
    new_physics: Query<
        (
            Entity,
            &PhysicsBody,
            Option<&AuthoringComponentStates>,
            Option<&InspectorOrder>,
        ),
        (With<LevelObject>, Added<PhysicsBody>),
    >,
    split_physics: Query<
        (
            Entity,
            Option<&RigidBodyDesc>,
            Option<&ColliderDesc>,
            Option<&AuthoringComponentStates>,
            Option<&InspectorOrder>,
        ),
        (
            With<LevelObject>,
            Or<(
                Changed<RigidBodyDesc>,
                Added<RigidBodyDesc>,
                Changed<ColliderDesc>,
                Added<ColliderDesc>,
                Changed<AuthoringComponentStates>,
                Changed<InspectorOrder>,
            )>,
        ),
    >,
) {
    for (entity, body, states, legacy_order) in new_physics.iter().chain(physics.iter()) {
        if !authoring_component_active(states, legacy_order, COMPONENT_PHYSICS_BODY) {
            commands
                .entity(entity)
                .remove::<RigidBody>()
                .remove::<Collider>();
            continue;
        }
        let rigid_body = match body.body {
            AuthoringRigidBody::Static => RigidBody::Static,
            AuthoringRigidBody::Kinematic => RigidBody::Kinematic,
            AuthoringRigidBody::Dynamic => RigidBody::Dynamic,
        };

        commands
            .entity(entity)
            .remove::<RigidBody>()
            .remove::<Collider>()
            .insert((rigid_body, body.collider.clone()));
    }

    for (entity, rigid_body, collider, states, legacy_order) in &split_physics {
        let mut entity_commands = commands.entity(entity);
        entity_commands.remove::<RigidBody>().remove::<Collider>();

        if let Some(rigid_body) = rigid_body
            .filter(|_| authoring_component_active(states, legacy_order, COMPONENT_RIGID_BODY_DESC))
        {
            entity_commands.insert(rigid_body_from_desc(rigid_body.body));
        }

        let Some(collider) = collider else {
            continue;
        };
        if !authoring_component_active(states, legacy_order, COMPONENT_COLLIDER_DESC) {
            continue;
        }
        if !collider.enabled || matches!(collider.shape, ColliderShapeDesc::StaticMesh { .. }) {
            continue;
        }
        if let Some(constructor) = collider.shape.to_constructor() {
            entity_commands.insert(constructor);
        }
    }
}

pub fn rigid_body_from_desc(body: AuthoringRigidBody) -> RigidBody {
    match body {
        AuthoringRigidBody::Static => RigidBody::Static,
        AuthoringRigidBody::Kinematic => RigidBody::Kinematic,
        AuthoringRigidBody::Dynamic => RigidBody::Dynamic,
    }
}
