/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */
using Live2D.Cubism.Core;
using UnityEngine;
namespace Live2D.Cubism.Framework.Physics
{
    /// 
    /// Math utilities for physics.
    /// 
    internal static class CubismPhysicsMath
    {
        /// 
        /// Gets radian from degrees.
        /// 
        /// Degrees.
        /// Radian.
        public static float DegreesToRadian(float degrees)
        {
            return (degrees / 180.0f) * Mathf.PI;
        }
        /// 
        /// Gets degrees from radian.
        /// 
        /// Radian.
        /// Degrees.
        public static float RadianToDegrees(float radian)
        {
            return (radian * 180.0f) / Mathf.PI;
        }
        /// 
        /// Gets angle from both vector direction.
        /// 
        /// From vector.
        /// To vector.
        /// Angle of radian.
        public static float DirectionToRadian(Vector2 from, Vector2 to)
        {
            var q1 = Mathf.Atan2(to.y, to.x);
            var q2 = Mathf.Atan2(from.y, from.x);
            return GetAngleDiff(q1, q2);
        }
        /// 
        /// Gets difference of angle.
        /// 
        /// 
        /// 
        /// 
        public static float GetAngleDiff(float q1, float q2)
        {
            var ret = q1 - q2;
            while (ret < -Mathf.PI)
            {
                ret += (Mathf.PI * 2.0f);
            }
            while (ret > Mathf.PI)
            {
                ret -= (Mathf.PI * 2.0f);
            }
            return ret;
        }
        /// 
    /// Gets angle from both vector direction.
    /// 
    /// From vector.
    /// To vector.
    /// Angle of degrees.
    public static float DirectionToDegrees(Vector2 from, Vector2 to)
        {
            var radian = DirectionToRadian(from, to);
            var degree = (float)RadianToDegrees(radian);
            if ((to.x - from.x) > 0.0f)
            {
                degree = -degree;
            }
            return degree;
        }
        /// 
        /// Gets vector direction from angle.
        /// 
        /// Radian.
        /// Direction of vector.
        public static Vector2 RadianToDirection(float totalAngle)
        {
            var ret = Vector2.zero;
            ret.x = Mathf.Sin(totalAngle);
            ret.y = (float)Mathf.Cos(totalAngle);
            return ret;
        }
        /// 
        /// Gets range of value.
        /// 
        /// Minimum value.
        /// Maximum value.
        /// 
        private static float GetRangeValue(float min, float max)
        {
            var maxValue = Mathf.Max(min, max);
            var minValue = Mathf.Min(min, max);
            return Mathf.Abs(maxValue - minValue);
        }
        /// 
        /// Gets middle value.
        /// 
        /// Minimum value.
        /// Maximum value.
        /// 
        private static float GetDefaultValue(float min, float max)
        {
            var minValue = Mathf.Min(min, max);
            return minValue + (GetRangeValue(min, max) / 2.0f);
        }
        /// 
        /// Normalize parameter value.
        /// 
        /// Target parameter.
        /// Value of normalized minimum.
        /// Value of normalized maximum.
        /// Value of normalized default.
        /// True if input is inverted; otherwise.
        /// 
        public static float Normalize(CubismParameter parameter,
            float normalizedMinimum,
            float normalizedMaximum,
            float normalizedDefault,
            bool isInverted = false)
        {
            var result = 0.0f;
            var maxValue = Mathf.Max(parameter.MaximumValue, parameter.MinimumValue);
            if (maxValue < parameter.Value)
            {
                parameter.Value = maxValue;
            }
            var minValue = Mathf.Min(parameter.MaximumValue, parameter.MinimumValue);
            if (minValue > parameter.Value)
            {
                parameter.Value = minValue;
            }
            var minNormValue = Mathf.Min(normalizedMinimum, normalizedMaximum);
            var maxNormValue = Mathf.Max(normalizedMinimum, normalizedMaximum);
            var middleNormValue = normalizedDefault;
            var middleValue = GetDefaultValue(minValue, maxValue);
            var paramValue = parameter.Value - middleValue;
            switch ((int)Mathf.Sign(paramValue))
            {
                case 1:
                    {
                        var nLength = maxNormValue - middleNormValue;
                        var pLength = maxValue - middleValue;
                        if (pLength != 0.0f)
                        {
                            result = paramValue * (nLength / pLength);
                            result += middleNormValue;
                        }
                        break;
                    }
                case -1:
                    {
                        var nLength = minNormValue - middleNormValue;
                        var pLength = minValue - middleValue;
                        if (pLength != 0.0f)
                        {
                            result = paramValue * (nLength / pLength);
                            result += middleNormValue;
                        }
                        break;
                    }
                case 0:
                    {
                        result = middleNormValue;
                        break;
                    }
            }
            return (isInverted) ? result : (result * -1.0f);
        }
    }
}