using Microsoft.VisualStudio.TestTools.UnitTesting;
using NavisworksTransport.Core;

namespace NavisworksTransport.UnitTests.CoordinateSystem
{
    [TestClass]
    public class VirtualObjectModelTransformTests
    {
        [TestMethod]
        public void BuildModelTransformPreservingScale_ShouldKeepExistingScaleAndReplaceRotationTranslation()
        {
            const double scaleX = 4.9213;
            const double scaleY = 3.2808;
            const double scaleZ = 6.5617;
            var targetRotation = new QuaternionData(0.0, 0.7071068, 0.0, 0.7071068);

            VirtualObjectManager.ModelTransformComponents result = VirtualObjectManager.CreateModelTransformPreservingScale(
                scaleX,
                scaleY,
                scaleZ,
                -177.129,
                18.114,
                -2.793,
                targetRotation.ToRotation3D());

            Assert.AreEqual(scaleX, result.ScaleX, 1e-4);
            Assert.AreEqual(scaleY, result.ScaleY, 1e-4);
            Assert.AreEqual(scaleZ, result.ScaleZ, 1e-4);
            AssertRotationEquivalent(targetRotation, result.Rotation);
            Assert.AreEqual(-177.129, result.TranslationX, 1e-6);
            Assert.AreEqual(18.114, result.TranslationY, 1e-6);
            Assert.AreEqual(-2.793, result.TranslationZ, 1e-6);
        }

        [TestMethod]
        public void BuildModelTransformPreservingScale_ResetPose_ShouldOnlyClearRotationAndTranslation()
        {
            VirtualObjectManager.ModelTransformComponents result = VirtualObjectManager.CreateModelTransformPreservingScale(
                12.0,
                5.0,
                7.0,
                0.0,
                0.0,
                0.0,
                QuaternionData.Identity.ToRotation3D());

            Assert.AreEqual(12.0, result.ScaleX, 1e-6);
            Assert.AreEqual(5.0, result.ScaleY, 1e-6);
            Assert.AreEqual(7.0, result.ScaleZ, 1e-6);
            AssertRotationEquivalent(QuaternionData.Identity, result.Rotation);
            Assert.AreEqual(0.0, result.TranslationX, 1e-6);
            Assert.AreEqual(0.0, result.TranslationY, 1e-6);
            Assert.AreEqual(0.0, result.TranslationZ, 1e-6);
        }

        private static void AssertRotationEquivalent(QuaternionData expected, Autodesk.Navisworks.Api.Rotation3D actual)
        {
            bool same =
                NearlyEqual(expected.X, actual.A) &&
                NearlyEqual(expected.Y, actual.B) &&
                NearlyEqual(expected.Z, actual.C) &&
                NearlyEqual(expected.W, actual.D);
            bool negatedSame =
                NearlyEqual(expected.X, -actual.A) &&
                NearlyEqual(expected.Y, -actual.B) &&
                NearlyEqual(expected.Z, -actual.C) &&
                NearlyEqual(expected.W, -actual.D);

            Assert.IsTrue(same || negatedSame, "Rotation3D quaternion should match target rotation up to sign.");
        }

        private static bool NearlyEqual(double left, double right)
        {
            return System.Math.Abs(left - right) < 1e-6;
        }

        private struct QuaternionData
        {
            public QuaternionData(double x, double y, double z, double w)
            {
                X = x;
                Y = y;
                Z = z;
                W = w;
            }

            public double X { get; }
            public double Y { get; }
            public double Z { get; }
            public double W { get; }

            public Autodesk.Navisworks.Api.Rotation3D ToRotation3D()
            {
                return new Autodesk.Navisworks.Api.Rotation3D(X, Y, Z, W);
            }

            public static QuaternionData Identity => new QuaternionData(0, 0, 0, 1);
        }
    }
}
