Documentation
use super::*;

/// `D6Joint` component. A D6 joint, like 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.
///
/// The `D6Joint` has 6 degrees of freedom (hence the name): 3 translational, and 3 rotational.
/// These can all be controlled individually.
///
/// Important: By default all six dimensions of motion are locked, so by default a `D6Joint` acts like a fix-joint,
/// gluing the bodies to each other. To do something else you need to unlock the dimensions with the `motion` accessor.
/// For instance, so create a ball-socket joint you would unlock the rotational degrees of movement but keep the
/// translational degrees locked.
///
/// A joint normally connects two entities, but you can also leave one of the entities
/// to the default `None` value too attach an entity to the world frame,
/// a static infinite-mass invisible global frame.
///
/// Usually accessed through `entity.d6_joint`().
///
/// The [NVIDIA `PhysX` documentation](https://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/guide/Manual/Joints.html)
/// has some more details. Some naming is a little different from the official API, but overall the concepts are the same.

pub struct D6Joint {
    id: Entity,
}

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

#[derive(Copy, Clone, Debug)]
/// The parameters for a drive on a D6 joint.
pub struct D6DriveParams {
    /// Stiffness of the drive spring: The amount of torque needed to move the joint to its target orientation (not used for velocity drive).
    pub stiffness: f32,
    /// Damping of the drive spring: Tweak to apply damping to the spring to smooth out any oscillations.
    pub damping: f32,
    /// Force limit: Maximum force/acceleration the drive can apply.
    pub force_limit: f32,
}

#[derive(Copy, Clone, Debug)]
/// The parameters for a simple spring.
pub struct D6SpringParams {
    /// Stiffness of the spring
    pub stiffness: f32,
    /// Damping of the spring.
    pub damping: f32,
}

impl D6Joint {
    impl_world_accessor!(
        /// The first entity of the joint.
        D6Joint,
        Entity1,
        Entity,
        entity_1,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The second entity of the joint.
        D6Joint,
        Entity2,
        Entity,
        entity_2,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The position of the joint relative to the first entity (`entity_1`)
        D6Joint,
        RelativePosition1,
        Vec3,
        relative_offset_1,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The orientation of the joint relative to the first entity (`entity_1`)
        D6Joint,
        RelativeOrientation1,
        Quat,
        relative_orientation_1,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The position of the joint relative to the second entity (`entity_2`)
        D6Joint,
        RelativePosition2,
        Vec3,
        relative_offset_2,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The orientation of the joint relative to the second entity (`entity_2`)
        D6Joint,
        RelativeOrientation2,
        Quat,
        relative_orientation_2,
        ValueAccessorReadWrite
    );

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

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

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

    impl_world_accessor!(
        /// If non-zero, linear projection is enabled (a way of resolving tricky physics situations by moving
        /// the parts into place), and the linear projection tolerance is set to this value.
        ///
        /// Projection is a on-physical process that violates conservation laws and collision meshes.
        /// Only turn it on if you absolutely need to.
        D6Joint,
        ProjectionLinearTolerance,
        f32,
        projection_linear_tolerance,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// If non-zero, projection is enabled (a way of resolving tricky physics situations by turning
        /// the parts into place), and the angular projection tolerance is set to this value.
        ///
        /// Projection is a on-physical process that violates conservation laws and collision meshes.
        /// Only turn it on if you absolutely need to.
        D6Joint,
        ProjectionAngularTolerance,
        f32,
        projection_angular_tolerance,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Turns on joint drive. Note that this disables the use of limits on the joint.
        D6Joint,
        DriveEnabled,
        bool,
        drive_enable,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Get the raw translation between the two parts of the joint.
        D6Joint,
        RelativePosition,
        Vec3,
        relative_position,
        ValueAccessorRead
    );

    impl_world_accessor!(
        /// Get the raw rotation between the two parts of the joint. Not decomposed into TSS (twist, swing1, swing2).
        D6Joint,
        RelativeRotation,
        Quat,
        relative_rotation,
        ValueAccessorRead
    );

    impl_world_accessor!(
        /// Gets the rotation decomposed into TSS (twist, swing1, swing2).
        D6Joint,
        RelativeAngles,
        Vec3,
        relative_angles,
        ValueAccessorRead
    );

    impl_world_accessor!(
        /// The limit bounds of a D6 joint. x is min, y is max, what exactly it limits depends on the joint type.
        D6Joint,
        LinearLimit,
        f32,
        linear_limit,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The linear limit type of a D6 joint.
        D6Joint,
        LinearLimitType,
        JointLimitType,
        linear_limit_type,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The linear limit spring of a D6 joint.
        D6Joint,
        LinearLimitSpring,
        D6SpringParams,
        linear_limit_spring,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The twist limit bounds of a D6 joint.
        D6Joint,
        TwistLimit,
        Vec2,
        twist_limit,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The linear limit type of a D6 joint.
        D6Joint,
        TwistLimitType,
        JointLimitType,
        twist_limit_type,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The linear limit spring of a D6 joint.
        D6Joint,
        TwistLimitSpring,
        D6SpringParams,
        twist_limit_spring,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The swing limit bounds of a D6 joint (angular)
        D6Joint,
        SwingLimit,
        Vec2,
        swing_limit,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The linear limit type of a D6 joint.
        D6Joint,
        SwingLimitType,
        JointLimitType,
        swing_limit_type,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// The swing limit spring of a D6 joint.
        D6Joint,
        SwingLimitSpring,
        D6SpringParams,
        swing_limit_spring,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Goal position of the D6 drive.
        D6Joint,
        DrivePosition,
        Vec3,
        drive_position,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Goal rotation of the D6 drive.
        D6Joint,
        DriveRotation,
        Quat,
        drive_rotation,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Goal linear velocity of the D6 drive.
        D6Joint,
        DriveLinearVelocity,
        Vec3,
        drive_linear_velocity,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Goal angular velocity of the D6 drive.
        D6Joint,
        DriveAngularVelocity,
        Vec3,
        drive_angular_velocity,
        ValueAccessorReadWrite
    );

    impl_world_accessor_indexed!(
        /// Set the motion type of each of the D6 joint's six axes, `D6Axis`. Can be locked, limited or free.
        ///
        /// By default, all six axis are locked, so you need to unlock any axis you would like enable movement of.
        ///
        /// ## Hinge
        ///
        /// Free movement aroudn the X axis:
        ///
        /// ``` rust
        /// joint.motion(D6Axis::Twist).set(D6MotionType::Free);
        /// ```
        ///
        /// ## Ball-socket joint
        ///
        /// ``` rust
        /// joint.motion(D6Axis::Twist).set(D6MotionType::Free);
        /// joint.motion(D6Axis::Swing1).set(D6MotionType::Free);
        /// joint.motion(D6Axis::Swing2).set(D6MotionType::Free);
        /// ```
        D6Joint,
        MotionX,
        D6MotionType,
        motion,
        D6Axis,
        ValueAccessorReadWrite
    );

    impl_world_accessor_indexed!(
        /// Lets you control the six possible drives from `D6Drive` by setting a `D6DriveParams`.
        D6Joint,
        DriveParamsX,
        D6DriveParams,
        drive_params,
        D6Drive,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Controls whether drive is a force (false) or acceleration (true). Acceleration is much easier to tune since it
        /// will automatically compute the appropriate force for the masses involved, so it's the default and
        /// recommended value.
        D6Joint,
        DriveIsAcceleration,
        bool,
        drive_is_acceleration,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Returns the linear force currently exerted on the joint constraint.
        D6Joint,
        LinearForce,
        Vec3,
        linear_force,
        ValueAccessorRead
    );

    impl_world_accessor!(
        /// Returns the angular force currently exerted on the joint constraint.
        D6Joint,
        AngularForce,
        Vec3,
        angular_force,
        ValueAccessorRead
    );
}

impl_world_component!(D6Joint);