Documentation
use super::*;

/// Joint component. A joint connects two dynamic, or one static and one dynamic, entity physically.
/// It doesn't have to be a component of one of the entities it connects, but that can be convenient at times.
///
/// There are many different kinds of joints. A `Joint` can represent all standard joint types except D6,
/// which we have `D6Joint` for.
///
/// If you don't set a `second_entity`, the joint will be to kind of a static invisible global frame - not super
/// realistic, but often useful.
///
/// Usually accessed through `entity.joint`().
///
/// Most of the time, you'll be better served by a `D6Joint`. `Joint` represent `PhysX`'s legacy joint types. They
/// can be easier to use for simpler applications but `D6Joint` has all the capabilities and are more flexible.



pub struct Joint {
    id: Entity,
}

impl std::fmt::Debug for Joint {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Joint")
            .field("entity", &self.id.name())
            .finish_non_exhaustive()
    }
}

impl Joint {
    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the joint type of the entity.
        ///
        /// Used to set/get the mesh style.
        Joint,
        Type,
        JointType,
        joint_type,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The first entity of the joint.
        Joint,
        FirstEntity,
        Entity,
        first_entity,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The second entity of the joint.
        Joint,
        SecondEntity,
        Entity,
        second_entity,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The location on the first entity that the joint is attached to.
        Joint,
        FirstOffset,
        Vec3,
        first_offset,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The location on the second entity that the joint is attached to.
        Joint,
        SecondOffset,
        Vec3,
        second_offset,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The orientation of the joint on the second entity that the joint is attached to.
        /// For hinge joints, the default is the exact X axis, rotated by this orientation.
        Joint,
        FirstOrientation,
        Quat,
        first_orientation,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The orientation of the joint on the second entity that the joint is attached to.
        /// For hinge joints, the default is the exact X axis, rotated by this orientation.
        Joint,
        SecondOrientation,
        Quat,
        second_orientation,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The limit bounds of a joint. x is min, y is max, what exactly it limits depends on the joint type:
        /// * Ball joint: x is the y angle limit of the cone (around the x axis), while y sets the z angle limit.
        /// * Hinge: x is the lower angle limit, y is the upper angle limit.
        /// * Slider: x is the lower position limit, y is the upper position limit.
        /// * Distance: x is the minimum distance, y is the maximum distance.
        Joint,
        Limits,
        Vec2,
        limits,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The limit type of a joint.
        Joint,
        LimitType,
        JointLimitType,
        limit_type,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The joint velocity (angular for hinge joints, linear for linear joints).
        /// Can only be retrieved for now, need to use forces to change it.
        Joint,
        Velocity,
        f32,
        velocity,
        ValueAccessorRead
    );

    impl_world_accessor!(
        /// Turns on joint drive. Currently only works with Hinge joints. `D6Joint` has more flexible drive capabilities, though.
        Joint,
        DriveEnabled,
        bool,
        drive_enable,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// The drive velocity. Does nothing unless `drive_enable` is set to true.
        Joint,
        DriveVelocity,
        f32,
        drive_velocity,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Sets the maximum force the joint drive can exercise.
        Joint,
        DriveForceLimit,
        f32,
        drive_force_limit,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Sets the gear ratio of the joint drive.
        Joint,
        DriveGearRatio,
        f32,
        drive_gear_ratio,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Enables freespin on the joint drive (it'll continue to spin by inertia if you
        /// turn off the drive).
        Joint,
        DriveFreespin,
        bool,
        drive_freespin,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Sets the force threshold for breaking the joint. Default is std::f32::MAX (effectively unbreakable).
        Joint,
        BreakForce,
        f32,
        break_force,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Sets the torque threshold for breaking the joint. Default is std::f32::MAX (effectively unbreakable).
        Joint,
        BreakTorque,
        f32,
        break_torque,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Lets you check whether the joint is broken. Cannot be modified.
        Joint,
        IsBroken,
        bool,
        is_broken,
        ValueAccessorRead
    );

    impl_world_accessor!(
        /// Lets you check the angles or position of the joint. Interpreted differently depending on the joint type:
        /// - Hinge: x means the angle. You can use `hinge_angle` instead.
        /// - Ball: y means the y-angle, z means the z-angle. You can use `ball_swing_angles` instead.
        /// - Linear: x means the position along the joint. You can use `linear_position` instead.
        Joint,
        AngleOrPosition,
        Vec3,
        angle_or_position,
        ValueAccessorRead
    );

    /// Utility function for `angle_or_position`: If the joint is a hinge, returns the angle directly.
    pub fn hinge_angle(&self) -> f32 {
        self.angle_or_position().get().x
    }

    /// Utility function: If the joint is linear, returns the position along the line directly.
    pub fn linear_position(&self) -> f32 {
        self.angle_or_position().get().x
    }

    /// Utility function: If the joint is a ball joint, return the swing angles.
    pub fn ball_swing_angles(&self) -> Vec2 {
        Vec2::new(
            self.angle_or_position().get().y,
            self.angle_or_position().get().z,
        )
    }
}

impl_world_component!(Joint);