use bevy::prelude::*;
use bevy_rapier3d::{
    dynamics::{Ccd, FixedJointBuilder, Velocity},
    geometry::{Collider, CollisionGroups, Friction},
    prelude::{
        ColliderMassProperties, ExternalForce, MultibodyJoint, PrismaticJointBuilder, RigidBody,
        Sleeping, TransformInterpolation,
    },
};

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

pub(crate) fn spawn_wheels(
    commands: &mut Commands,
    bike: Entity,
    conf: &WheelConfig,
    meshterials: &mut Meshterial,
) {
    let wheel_y = conf.y;
    let stiffness = conf.stiffness;
    let not_sleeping = Sleeping::disabled();
    let limits = conf.limits;
    let (meshes, materials) = meshterials;
    //let rake_vec: Vec3 = Vec3::new(0.0, 1.0, 0.57).normalize(); // about 30
    // degrees of rake

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

    let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP;
    let wheels_collision_group = CollisionGroups::new(membership, filter);

    // 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 mesh = gen_tires(conf);

        let material = StandardMaterial {
            base_color: Color::YELLOW,
            alpha_mode: AlphaMode::Opaque,
            perceptual_roughness: 0.1,
            ..Default::default()
        };

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

        let mass_props = ColliderMassProperties::Density(conf.density);
        let suspension_damping = conf.damping;

        let suspension_axis = if steering.is_some() {
            // rake_vec
            Vec3::Y
        } else {
            Vec3::Y
        };

        let suspension_joint_builder = PrismaticJointBuilder::new(suspension_axis)
            .local_anchor1(offset)
            .limits(limits)
            .motor_position(limits[0], stiffness, suspension_damping);
        let suspension_joint = MultibodyJoint::new(bike, suspension_joint_builder);
        let fork_rb_entity = commands
            .spawn(RigidBody::Dynamic)
            .insert(suspension_joint)
            .insert(not_sleeping)
            .id();

        let axel_parent_entity = if steering.is_some() {
            let neck_builder = FixedJointBuilder::new().local_anchor1(Vec3::new(0.0, 0.0, 0.1)); // this adds another 0.1m of trail
            let neck_joint = MultibodyJoint::new(fork_rb_entity, neck_builder);
            let neck = commands.spawn(RigidBody::Dynamic).insert(neck_joint).id();
            neck
        } else {
            fork_rb_entity
        };

        let axel_builder = FixedJointBuilder::new();
        let axel_joint = MultibodyJoint::new(axel_parent_entity, axel_builder);

        let c = &mut commands.spawn(pbr_bundle);
        c.insert((
            mass_props,
            axel_joint,
            CyberWheel,
            not_sleeping,
            Velocity::default(),
            PreviousVelocity::default(),
            ExternalForce::default(),
            SpatialBundle::default(),
            TransformInterpolation::default(),
            RigidBody::Dynamic,
            Ccd { enabled: true },
            Friction {
                coefficient: 0.0,
                combine_rule: bevy_rapier3d::dynamics::CoefficientCombineRule::Min,
            },
            wheels_collision_group,
            Collider::ball(conf.radius),
        ));
        if steering.is_some() {
            c.insert(CyberSteering);
        }
    }
}

// do mesh shit
fn gen_tires(conf: &WheelConfig) -> Mesh {
    let wheel_rad = conf.radius;
    let tire = Sphere::new(wheel_rad);
    Mesh::from(tire)
}
