use crate::math::Real;

/// The spring-like model used for constraints resolution.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum MotorModel {
    /// The solved spring-like equation is:
    /// `acceleration = stiffness * (pos - target_pos) + damping * (vel - target_vel)`
    AccelerationBased,
    /// The solved spring-like equation is:
    /// `force = stiffness * (pos - target_pos) + damping * (vel - target_vel)`
    ForceBased,
}

impl Default for MotorModel {
    fn default() -> Self {
        MotorModel::AccelerationBased
    }
}

impl MotorModel {
    /// Combines the coefficients used for solving the spring equation.
    ///
    /// Returns the coefficients (erp_inv_dt, cfm_coeff, cfm_gain).
    pub fn combine_coefficients(
        self,
        dt: Real,
        stiffness: Real,
        damping: Real,
    ) -> (Real, Real, Real) {
        match self {
            MotorModel::AccelerationBased => {
                let erp_inv_dt = stiffness * crate::utils::inv(dt * stiffness + damping);
                let cfm_coeff = crate::utils::inv(dt * dt * stiffness + dt * damping);
                (erp_inv_dt, cfm_coeff, 0.0)
            }
            MotorModel::ForceBased => {
                let erp_inv_dt = stiffness * crate::utils::inv(dt * stiffness + damping);
                let cfm_gain = crate::utils::inv(dt * dt * stiffness + dt * damping);
                (erp_inv_dt, 0.0, cfm_gain)
            }
        }
    }
}
