// ---------------------------------------------------------------------------
// BT.Framework.Tests
// MathExTests.cs — MathEx 수학 유틸 단위 테스트
// ---------------------------------------------------------------------------
using NUnit.Framework;
using BurningTimes.Core.Util;

namespace BurningTimes.Tests.Core.Util
{
    public class MathExTests
    {
        [Test]
        public void Clamp01_BoundsToZeroOne()
        {
            Assert.That(MathEx.Clamp01(-0.5f), Is.EqualTo(0f));
            Assert.That(MathEx.Clamp01(1.5f), Is.EqualTo(1f));
            Assert.That(MathEx.Clamp01(0.5f), Is.EqualTo(0.5f));
        }

        [Test]
        public void Clamp_IntAndFloatHonorBounds()
        {
            Assert.That(MathEx.Clamp(5, 0, 10), Is.EqualTo(5));
            Assert.That(MathEx.Clamp(-1, 0, 10), Is.EqualTo(0));
            Assert.That(MathEx.Clamp(11, 0, 10), Is.EqualTo(10));
            Assert.That(MathEx.Clamp(1.5f, 0f, 1f), Is.EqualTo(1f));
        }

        [Test]
        public void Remap_LinearMappingWorks()
        {
            // [0,100] -> [0,1]
            Assert.That(MathEx.Remap(50f, 0f, 100f, 0f, 1f), Is.EqualTo(0.5f).Within(MathEx.Epsilon));
            // [0,100] -> [-1,1]
            Assert.That(MathEx.Remap(0f, 0f, 100f, -1f, 1f), Is.EqualTo(-1f).Within(MathEx.Epsilon));
            Assert.That(MathEx.Remap(100f, 0f, 100f, -1f, 1f), Is.EqualTo(1f).Within(MathEx.Epsilon));
        }

        [Test]
        public void Remap_ZeroSpan_ReturnsOutMin()
        {
            // 입력 범위가 0이면 0-division 없이 outMin 반환
            Assert.That(MathEx.Remap(5f, 3f, 3f, -10f, 10f), Is.EqualTo(-10f));
        }

        [Test]
        public void RemapClamped_ClipsOutsideInputRange()
        {
            Assert.That(MathEx.RemapClamped(-50f, 0f, 100f, 0f, 1f), Is.EqualTo(0f));
            Assert.That(MathEx.RemapClamped(150f, 0f, 100f, 0f, 1f), Is.EqualTo(1f));
        }

        [Test]
        public void Approximately_WithinEpsilonReturnsTrue()
        {
            Assert.IsTrue(MathEx.Approximately(1.0f, 1.0f + 1e-7f));
            Assert.IsFalse(MathEx.Approximately(1.0f, 1.1f));
        }

        [Test]
        public void SmoothTowards_ReachesTargetEventually()
        {
            float current = 0f;
            for (int i = 0; i < 1000; i++)
            {
                current = MathEx.SmoothTowards(current, 10f, halfLife: 0.1f, deltaTime: 0.1f);
            }
            Assert.That(current, Is.EqualTo(10f).Within(0.01f));
        }

        [Test]
        public void SmoothTowards_ZeroOrNegativeHalfLife_SnapsToTarget()
        {
            Assert.That(MathEx.SmoothTowards(0f, 10f, halfLife: 0f, deltaTime: 0.1f), Is.EqualTo(10f));
            Assert.That(MathEx.SmoothTowards(0f, 10f, halfLife: -1f, deltaTime: 0.1f), Is.EqualTo(10f));
        }

        [Test]
        public void Gcd_BasicCases()
        {
            Assert.That(MathEx.Gcd(12, 18), Is.EqualTo(6));
            Assert.That(MathEx.Gcd(17, 13), Is.EqualTo(1));
            Assert.That(MathEx.Gcd(-12, 18), Is.EqualTo(6));
            Assert.That(MathEx.Gcd(0, 5), Is.EqualTo(5));
        }

        [Test]
        public void Lcm_BasicCases()
        {
            Assert.That(MathEx.Lcm(4, 6), Is.EqualTo(12));
            Assert.That(MathEx.Lcm(0, 5), Is.EqualTo(0));
        }
    }
}
