/*
 * @license Private Source License v1.0.0
 * @copyright 2023 Reinhardt Weyers
 *
 * @author Reinhardt Weyers
 *
 * @version 0.1.0
 * 
 * Created On: 04/12/25
 * Last edited: 04/12/25
 */

using UnityEngine;

namespace RetroDev.Newton2D
{
    public static class Newton2D
    {
        /// <summary>
        /// Apply a force to a 2D rigidbody in a specified direction.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="forceDirection">The direction in which to apply the force.</param>
        /// <param name="forceMagnitude">The magnitude or strength of the force.</param>
        public static void ApplyForce(Rigidbody2D rigidbody, Vector2 forceDirection, float forceMagnitude)
        {
            Vector2 force = forceDirection.normalized * forceMagnitude;
            rigidbody.AddForce(force);
        }

        /// <summary>
        /// Set the velocity of a 2D rigidbody.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="velocity">The new velocity.</param>
        public static void SetVelocity(Rigidbody2D rigidbody, Vector2 velocity)
        {
            rigidbody.linearVelocity = velocity;
        }

        /// <summary>
        /// Simulate a jump by applying an upward force to a 2D rigidbody.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="jumpForce">The force applied for the jump.</param>
        public static void Jump(Rigidbody2D rigidbody, float jumpForce)
        {
            rigidbody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
        }

        /// <summary>
        /// Set constraints on a 2D rigidbody.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="freezePositionX">Freeze or unfreeze the X-axis position.</param>
        /// <param name="freezePositionY">Freeze or unfreeze the Y-axis position.</param>
        /// <param name="freezeRotation">Freeze or unfreeze rotation.</param>
        public static void SetConstraints(Rigidbody2D rigidbody, bool freezePositionX, bool freezePositionY, bool freezeRotation)
        {
            rigidbody.constraints = (freezePositionX ? RigidbodyConstraints2D.FreezePositionX : RigidbodyConstraints2D.None) |
                                    (freezePositionY ? RigidbodyConstraints2D.FreezePositionY : RigidbodyConstraints2D.None) |
                                    (freezeRotation ? RigidbodyConstraints2D.FreezeRotation : RigidbodyConstraints2D.None);
        }

        /// <summary>
        /// Apply torque to a 2D rigidbody.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="torque">The torque to be applied.</param>
        public static void ApplyTorque(Rigidbody2D rigidbody, float torque)
        {
            rigidbody.AddTorque(torque);
        }

        /// <summary>
        /// Apply linear damping to a 2D rigidbody.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="damping">The linear damping factor.</param>
        public static void ApplyLinearDamping(Rigidbody2D rigidbody, float damping)
        {
            rigidbody.linearDamping = damping;
        }

        /// <summary>
        /// Apply angular damping to a 2D rigidbody.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="damping">The angular damping factor.</param>
        public static void ApplyAngularDamping(Rigidbody2D rigidbody, float damping)
        {
            rigidbody.angularDamping = damping;
        }

        /// <summary>
        /// Apply a force to a 2D rigidbody at a specific position.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="force">The force to be applied.</param>
        /// <param name="position">The position at which to apply the force.</param>
        public static void ApplyForceAtPosition(Rigidbody2D rigidbody, Vector2 force, Vector2 position)
        {
            rigidbody.AddForceAtPosition(force, position);
        }

        /// <summary>
        /// Apply a torque to a 2D rigidbody around a specific point.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="torque">The torque to be applied.</param>
        /// <param name="position">The point around which to apply the torque.</param>
        public static void ApplyTorqueAtPosition(Rigidbody2D rigidbody, float torque, Vector2 position)
        {
            Vector2 torqueDirection = position - (Vector2)rigidbody.worldCenterOfMass;
            rigidbody.AddTorque(torque * Mathf.Sign(Vector3.Cross(torqueDirection, Vector3.up).z));
        }

        /// <summary>
        /// Apply a force to simulate linear drag.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="linearDrag">The linear drag to be applied.</param>
        public static void ApplyLinearDrag(Rigidbody2D rigidbody, float linearDrag)
        {
            rigidbody.linearVelocity *= 1f - linearDrag * Time.fixedDeltaTime;
        }

        /// <summary>
        /// Apply angular drag to simulate rotational drag.
        /// </summary>
        /// <param name="rigidbody">The 2D rigidbody.</param>
        /// <param name="angularDrag">The angular drag to be applied.</param>
        public static void ApplyAngularDrag(Rigidbody2D rigidbody, float angularDrag)
        {
            rigidbody.angularVelocity *= 1f - angularDrag * Time.fixedDeltaTime;
        }
    }
}
