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

use super::{
    CyberBikeBody, CyberWheel, WheelConfig, BIKE_BODY_COLLISION_GROUP, BIKE_WHEEL_COLLISION_GROUP,
};
use crate::{action::CatControllerState, planet::PLANET_RADIUS};

type Meshterial<'a> = (
    ResMut<'a, Assets<Mesh>>,
    ResMut<'a, Assets<StandardMaterial>>,
);

pub(super) fn spawn_cyberbike(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    wheel_conf: Res<WheelConfig>,
    mut meshterials: Meshterial,
) {
    let altitude = PLANET_RADIUS - 220.0;

    let mut xform = Transform::from_translation(Vec3::X * altitude)
        .with_rotation(Quat::from_axis_angle(Vec3::Z, -89.0f32.to_radians()));
    //.with_rotation(Quat::from_axis_angle(Vec3::X, 140.0f32.to_radians()));

    let right = xform.right() * 350.0;
    xform.translation += right;

    let damping = Damping {
        angular_damping: 2.0,
        linear_damping: 0.1,
    };

    let friction = Friction {
        coefficient: 0.0,
        ..Default::default()
    };

    let restitution = Restitution {
        coefficient: 0.0,
        ..Default::default()
    };

    let mass_properties = ColliderMassProperties::Density(0.9);

    let (membership, filter) = BIKE_BODY_COLLISION_GROUP;
    let bike_collision_group = CollisionGroups::new(membership, filter);

    let scene = asset_server.load("cb-no-y_up.glb#Scene0");

    let spatialbundle = SpatialBundle {
        transform: xform,
        ..Default::default()
    };

    let bike = commands
        .spawn(RigidBody::Dynamic)
        .insert(spatialbundle)
        .insert((
            Collider::capsule(Vec3::new(0.0, 0.0, -1.0), Vec3::new(0.0, 0.0, 1.0), 0.50),
            bike_collision_group,
            mass_properties,
            damping,
            restitution,
            friction,
            Sleeping::disabled(),
            Ccd { enabled: true },
            ReadMassProperties::default(),
        ))
        .insert(TransformInterpolation {
            start: None,
            end: None,
        })
        .insert(Velocity::zero())
        .insert(ExternalForce::default())
        .with_children(|rider| {
            rider.spawn(SceneBundle {
                scene,
                ..Default::default()
            });
        })
        .insert(CyberBikeBody)
        .insert(CatControllerState::default())
        .id();

    spawn_tires(&mut commands, &xform, bike, &wheel_conf, &mut meshterials);
}

fn spawn_tires(
    commands: &mut Commands,
    xform: &Transform,
    bike: Entity,
    conf: &WheelConfig,
    meshterials: &mut Meshterial,
) {
    //return;
    // re-set the collision group
    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: 0.0,
        ..Default::default()
    };

    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);
    }

    // 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);
    }

    for offset in wheel_poses {
        let trans = xform.translation + offset;
        let wheel_pos_in_world = Transform::from_rotation(xform.rotation).with_translation(trans);
        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 = PrismaticJointBuilder::new(Vec3::Y)
            .local_anchor1(offset)
            .limits(limits)
            .motor_position(-0.1, stiffness, damping);
        let joint = MultibodyJoint::new(bike, prismatic);

        let spatial_bundle = SpatialBundle {
            transform: wheel_pos_in_world,
            ..Default::default()
        };

        let tire_spundle = SpatialBundle {
            transform: Transform::IDENTITY,
            ..Default::default()
        };

        commands
            .spawn(RigidBody::Dynamic)
            .insert(spatial_bundle)
            .insert((
                wheel_collider,
                mass_props,
                wheel_damping,
                ccd,
                not_sleeping,
                joint,
                wheels_collision_group,
                friction,
                CyberWheel,
                ExternalForce::default(),
            ))
            .with_children(|wheel| {
                wheel.spawn(tire_spundle).insert(pbr_bundle.clone()).insert(
                    TransformInterpolation {
                        start: None,
                        end: None,
                    },
                );
            });
    }
}
