// ---------------------------------------------------------------------------
// BT.Framework
// MathEx.cs — 게임 빈출 수학 유틸리티 (Remap, Clamp, 부드러운 감쇠 등)
// ---------------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;

namespace BurningTimes.Core.Util
{
    /// <summary>
    /// UnityEngine.Mathf 에 없는 게임 빈출 수학 연산을 모은 유틸리티.
    /// </summary>
    /// <remarks>
    /// <para>설계 문서 §5 Tier 1. UnityEngine 참조 없이 순수 연산만 제공하므로 서버·배치 컨텍스트에서도 재사용 가능(C11 코드 범용성).</para>
    /// </remarks>
    public static class MathEx
    {
        /// <summary><see cref="float"/> 기준 작은 오차(1e-6).</summary>
        public const float Epsilon = 1e-6f;

        /// <summary>값을 <c>[0,1]</c>로 clamp한다.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static float Clamp01(float value)
        {
            if (value < 0f) return 0f;
            if (value > 1f) return 1f;
            return value;
        }

        /// <summary>값을 <c>[min,max]</c>로 clamp한다.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Clamp(int value, int min, int max)
        {
            if (value < min) return min;
            if (value > max) return max;
            return value;
        }

        /// <summary><see cref="float"/> 값을 <c>[min,max]</c>로 clamp한다.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static float Clamp(float value, float min, float max)
        {
            if (value < min) return min;
            if (value > max) return max;
            return value;
        }

        /// <summary>입력 구간 <c>[inMin,inMax]</c> 의 값을 출력 구간 <c>[outMin,outMax]</c> 로 선형 리매핑한다.</summary>
        /// <remarks>입력 구간이 0이면 <paramref name="outMin"/> 을 반환한다(0-division 회피).</remarks>
        public static float Remap(float value, float inMin, float inMax, float outMin, float outMax)
        {
            float span = inMax - inMin;
            if (span > -Epsilon && span < Epsilon) return outMin;
            float t = (value - inMin) / span;
            return outMin + (outMax - outMin) * t;
        }

        /// <summary>리매핑 후 출력 구간으로 clamp한다.</summary>
        public static float RemapClamped(float value, float inMin, float inMax, float outMin, float outMax)
        {
            float mapped = Remap(value, inMin, inMax, outMin, outMax);
            float lo = outMin < outMax ? outMin : outMax;
            float hi = outMin < outMax ? outMax : outMin;
            return Clamp(mapped, lo, hi);
        }

        /// <summary>두 실수가 <paramref name="epsilon"/> 이내로 근사하면 <c>true</c>.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool Approximately(float a, float b, float epsilon = Epsilon)
        {
            float diff = a - b;
            return diff > -epsilon && diff < epsilon;
        }

        /// <summary>프레임율 독립 부드러운 감쇠(Lerp with delta time).</summary>
        /// <param name="current">현재 값.</param>
        /// <param name="target">목표 값.</param>
        /// <param name="halfLife">목표까지 절반 접근에 걸리는 시간(초).</param>
        /// <param name="deltaTime">경과 시간(초).</param>
        /// <remarks>halfLife·deltaTime이 0 이하이면 즉시 <paramref name="target"/>을 반환한다.</remarks>
        public static float SmoothTowards(float current, float target, float halfLife, float deltaTime)
        {
            if (halfLife <= 0f || deltaTime <= 0f) return target;
            double k = Math.Pow(0.5, deltaTime / halfLife);
            return target + (float)((current - target) * k);
        }

        /// <summary>두 정수의 최대공약수(GCD). 음수 입력은 절대값으로 처리.</summary>
        public static int Gcd(int a, int b)
        {
            if (a < 0) a = -a;
            if (b < 0) b = -b;
            while (b != 0)
            {
                int t = b;
                b = a % b;
                a = t;
            }
            return a;
        }

        /// <summary>오버플로 없이 <c>long</c>으로 계산한 최소공배수(LCM). 둘 다 0이면 0.</summary>
        public static long Lcm(int a, int b)
        {
            if (a == 0 || b == 0) return 0;
            int g = Gcd(a, b);
            return (long)(a / g) * b;
        }
    }
}
