using Microsoft.VisualStudio.TestTools.UnitTesting;
using NavisworksTransport.Utils.CoordinateSystem;
using System;
using System.Numerics;

namespace NavisworksTransport.UnitTests.CoordinateSystem
{
    [TestClass]
    public class RotatedObjectExtentHelperTests
    {
        [TestMethod]
        public void YUp_HostY90_ForRealObject_ShouldKeepUpExtentUnchanged()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp);
            Quaternion correction = adapter.CreateHostRotationCorrection(
                new LocalEulerRotationCorrection(0.0, 90.0, 0.0));

            var result = RotatedObjectExtentHelper.CalculateProjectedSemanticExtents(
                convention,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                correctionQuaternion: correction);

            Assert.AreEqual(4.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(6.0, result.sideExtent, 1e-6);
            Assert.AreEqual(2.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void YUp_HostZ90_ForRealObject_ShouldPromoteForwardSizeToUpExtent()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp);
            Quaternion correction = adapter.CreateHostRotationCorrection(
                new LocalEulerRotationCorrection(0.0, 0.0, 90.0));

            var result = RotatedObjectExtentHelper.CalculateProjectedSemanticExtents(
                convention,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                correctionQuaternion: correction);

            Assert.AreEqual(2.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(4.0, result.sideExtent, 1e-6);
            Assert.AreEqual(6.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void ZUp_HostZDegrees_ShouldPromoteForwardSizeToUpExtent()
        {
            // ZUp 下 ZDegrees = non-up 轴(=Y)。绕 Y 转 90°：X(forward)→-Z(up), Z(up)→X。
            // forward(6) 提升到 up，up(2) 降到 forward。
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.ZUp);
            var convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp);
            Quaternion correction = adapter.CreateCanonicalRotationCorrection(
                new LocalEulerRotationCorrection(0.0, 0.0, 90.0));

            var result = RotatedObjectExtentHelper.CalculateProjectedSemanticExtents(
                convention,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                correctionQuaternion: correction);

            Assert.AreEqual(2.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(4.0, result.sideExtent, 1e-6);
            Assert.AreEqual(6.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void ZUp_HostYDegrees_ShouldKeepUpExtentUnchanged()
        {
            // ZUp 下 YDegrees = up 轴(=Z)。绕 Z 转 90°：X→Y, Y→-X, Z(up) 不变。
            // forward 和 side 互换，up 不变。
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.ZUp);
            var convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp);
            Quaternion correction = adapter.CreateCanonicalRotationCorrection(
                new LocalEulerRotationCorrection(0.0, 90.0, 0.0));

            var result = RotatedObjectExtentHelper.CalculateProjectedSemanticExtents(
                convention,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                correctionQuaternion: correction);

            Assert.AreEqual(4.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(6.0, result.sideExtent, 1e-6);
            Assert.AreEqual(2.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostY90_ShouldSwapForwardAndSide()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 90.0, 0.0));

            Assert.AreEqual(4.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(6.0, result.sideExtent, 1e-6);
            Assert.AreEqual(2.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostY45_ShouldBlendForwardAndSide_AndKeepUp()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 45.0, 0.0));

            double expectedPlanar = 5.0 * Math.Sqrt(2.0);
            Assert.AreEqual(expectedPlanar, result.forwardExtent, 1e-6);
            Assert.AreEqual(expectedPlanar, result.sideExtent, 1e-6);
            Assert.AreEqual(2.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostY135_ShouldBlendForwardAndSide_AndKeepUp()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 135.0, 0.0));

            double expectedPlanar = 5.0 * Math.Sqrt(2.0);
            Assert.AreEqual(expectedPlanar, result.forwardExtent, 1e-6);
            Assert.AreEqual(expectedPlanar, result.sideExtent, 1e-6);
            Assert.AreEqual(2.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostY180_ShouldKeepSemanticExtents()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 180.0, 0.0));

            Assert.AreEqual(6.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(4.0, result.sideExtent, 1e-6);
            Assert.AreEqual(2.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostY270_ShouldSwapForwardAndSide()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 270.0, 0.0));

            Assert.AreEqual(4.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(6.0, result.sideExtent, 1e-6);
            Assert.AreEqual(2.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostX90_ShouldKeepForward_AndSwapSideWithUp()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(90.0, 0.0, 0.0));

            Assert.AreEqual(6.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(2.0, result.sideExtent, 1e-6);
            Assert.AreEqual(4.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostX45_ShouldKeepForward_AndBlendSideWithUp()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(45.0, 0.0, 0.0));

            double expectedBlend = 3.0 * Math.Sqrt(2.0);
            Assert.AreEqual(6.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(expectedBlend, result.sideExtent, 1e-6);
            Assert.AreEqual(expectedBlend, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostX135_ShouldKeepForward_AndBlendSideWithUp()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(135.0, 0.0, 0.0));

            double expectedBlend = 3.0 * Math.Sqrt(2.0);
            Assert.AreEqual(6.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(expectedBlend, result.sideExtent, 1e-6);
            Assert.AreEqual(expectedBlend, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostX180_ShouldKeepSemanticExtents()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(180.0, 0.0, 0.0));

            Assert.AreEqual(6.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(4.0, result.sideExtent, 1e-6);
            Assert.AreEqual(2.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostX270_ShouldKeepForward_AndSwapSideWithUp()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(270.0, 0.0, 0.0));

            Assert.AreEqual(6.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(2.0, result.sideExtent, 1e-6);
            Assert.AreEqual(4.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostZ90_ShouldPromoteUpToForward_AndKeepSide()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 0.0, 90.0));

            Assert.AreEqual(2.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(4.0, result.sideExtent, 1e-6);
            Assert.AreEqual(6.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostZ45_ShouldBlendForwardWithUp_AndKeepSide()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 0.0, 45.0));

            double expectedBlend = 4.0 * Math.Sqrt(2.0);
            Assert.AreEqual(expectedBlend, result.forwardExtent, 1e-6);
            Assert.AreEqual(4.0, result.sideExtent, 1e-6);
            Assert.AreEqual(expectedBlend, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostZ135_ShouldBlendForwardWithUp_AndKeepSide()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 0.0, 135.0));

            double expectedBlend = 4.0 * Math.Sqrt(2.0);
            Assert.AreEqual(expectedBlend, result.forwardExtent, 1e-6);
            Assert.AreEqual(4.0, result.sideExtent, 1e-6);
            Assert.AreEqual(expectedBlend, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostZ180_ShouldKeepSemanticExtents()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 0.0, 180.0));

            Assert.AreEqual(6.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(4.0, result.sideExtent, 1e-6);
            Assert.AreEqual(2.0, result.upExtent, 1e-6);
        }

        [TestMethod]
        public void Ground_YUp_HostZ270_ShouldPromoteUpToForward_AndKeepSide()
        {
            var result = RotatedObjectExtentHelper.CalculateGroundSemanticExtents(
                CoordinateSystemType.YUp,
                forwardSize: 6.0,
                sideSize: 4.0,
                upSize: 2.0,
                hostCorrection: new LocalEulerRotationCorrection(0.0, 0.0, 270.0));

            Assert.AreEqual(2.0, result.forwardExtent, 1e-6);
            Assert.AreEqual(4.0, result.sideExtent, 1e-6);
            Assert.AreEqual(6.0, result.upExtent, 1e-6);
        }
    }
}
