use bevy::prelude::{shape::UVSphere as Tire, *};
use bevy_rapier3d::prelude::{
    Ccd, CoefficientCombineRule, Collider, ColliderMassProperties, CollisionGroups, Damping,
    ExternalForce, Friction, MultibodyJoint, PrismaticJointBuilder, Restitution,
    RevoluteJointBuilder, RigidBody, Sleeping, TransformInterpolation,
};

use super::{CyberSteering, CyberWheel, Meshterial, WheelConfig, BIKE_WHEEL_COLLISION_GROUP};

pub fn spawn_tires(
    commands: &mut Commands,
    _xform: &Transform,
    bike: Entity,
    conf: &WheelConfig,
    meshterials: &mut Meshterial,
) {
    let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP;
    let wheels_collision_group = CollisionGroups::new(membership, filter);
    let wheel_y = conf.y;
    let wheel_rad = conf.radius;
    let stiffness = conf.stiffness;
    let not_sleeping = Sleeping::disabled();
    let ccd = Ccd { enabled: true };
    let limits = conf.limits;
    let (meshes, materials) = meshterials;

    let tire = Tire {
        radius: wheel_rad,
        ..Default::default()
    };
    let material = StandardMaterial {
        base_color: Color::Rgba {
            red: 0.01,
            green: 0.01,
            blue: 0.01,
            alpha: 1.0,
        },
        alpha_mode: AlphaMode::Opaque,
        perceptual_roughness: 0.5,
        ..Default::default()
    };

    let pbr_bundle = PbrBundle {
        material: materials.add(material),
        mesh: meshes.add(Mesh::from(tire)),
        ..Default::default()
    };

    let friction = Friction {
        coefficient: conf.friction,
        combine_rule: CoefficientCombineRule::Average,
    };

    let mut wheel_poses = Vec::with_capacity(2);

    // front
    {
        let wheel_x = 0.0;
        let wheel_z = -conf.front_forward;
        let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
        wheel_poses.push((offset, Some(CyberSteering)));
    }

    // rear
    {
        let wheel_x = 0.0;
        let wheel_z = conf.rear_back;
        let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
        wheel_poses.push((offset, None));
    }

    for (offset, steering) in wheel_poses {
        let wheel_damping = Damping {
            linear_damping: 0.8,
            ..Default::default()
        };
        let wheel_collider = Collider::ball(wheel_rad);
        let mass_props = ColliderMassProperties::Density(0.1);
        let damping = conf.damping;

        let prismatic_axis = if steering.is_some() {
            Vec3::new(0.0, 1.0, 0.57).normalize() // about 30 degrees of rake
        } else {
            Vec3::Y
        };

        let prismatic_builder = PrismaticJointBuilder::new(prismatic_axis)
            .local_anchor1(offset)
            .limits(limits)
            .motor_position(limits[0], stiffness, damping);
        let prismatic_joint = MultibodyJoint::new(bike, prismatic_builder);
        let fork_rb_entity = commands
            .spawn(RigidBody::Dynamic)
            .insert(prismatic_joint)
            .insert(not_sleeping)
            .id();

        let axel_parent_entity = if let Some(steering) = steering {
            let neck_builder =
                RevoluteJointBuilder::new(Vec3::Y).local_anchor1(Vec3::new(0.0, 0.0, 0.1));
            let neck_joint = MultibodyJoint::new(fork_rb_entity, neck_builder);
            let neck = commands
                .spawn(RigidBody::Dynamic)
                .insert(neck_joint)
                .insert(steering)
                .insert(not_sleeping)
                .id();
            neck
        } else {
            fork_rb_entity
        };

        let revolute_builder = RevoluteJointBuilder::new(Vec3::X);
        let axel_joint = MultibodyJoint::new(axel_parent_entity, revolute_builder);

        commands.spawn(pbr_bundle.clone()).insert((
            wheel_collider,
            mass_props,
            wheel_damping,
            ccd,
            not_sleeping,
            axel_joint,
            wheels_collision_group,
            friction,
            CyberWheel,
            ExternalForce::default(),
            Restitution::new(conf.restitution),
            SpatialBundle::default(),
            TransformInterpolation::default(),
            RigidBody::Dynamic,
        ));
    }
}
