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

namespace NavisworksTransport.UnitTests.CoordinateSystem
{
    [TestClass]
    public class ModelAxisConventionTests
    {
        [TestMethod]
        public void DefaultForYUp_ShouldUseXForwardAndYUp()
        {
            ModelAxisConvention convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp);

            Assert.AreEqual(LocalAxisDirection.PositiveX, convention.ForwardAxis);
            Assert.AreEqual(LocalAxisDirection.PositiveY, convention.UpAxis);
        }

        [TestMethod]
        public void DefaultForZUp_ShouldUseXForwardAndZUp()
        {
            ModelAxisConvention convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp);

            Assert.AreEqual(LocalAxisDirection.PositiveX, convention.ForwardAxis);
            Assert.AreEqual(LocalAxisDirection.PositiveZ, convention.UpAxis);
        }

        [TestMethod]
        public void YUpConvention_CreateLinearTransform_ShouldMapLocalYToWorldUp()
        {
            var convention = new ModelAxisConvention(LocalAxisDirection.PositiveX, LocalAxisDirection.PositiveY);
            Matrix4x4 linear = convention.CreateLinearTransform3(new Vector3(1, 0, 0), new Vector3(0, 0, 1));

            AssertColumn(linear, 0, 1, 0, 0);
            AssertColumn(linear, 1, 0, 0, 1);
            AssertColumn(linear, 2, 0, -1, 0);
        }

        [TestMethod]
        public void ZUpConvention_CreateLinearTransform_ShouldMapLocalZToWorldUp()
        {
            var convention = new ModelAxisConvention(LocalAxisDirection.PositiveX, LocalAxisDirection.PositiveZ);
            Matrix4x4 linear = convention.CreateLinearTransform3(new Vector3(1, 0, 0), new Vector3(0, 0, 1));

            AssertColumn(linear, 0, 1, 0, 0);
            AssertColumn(linear, 1, 0, 1, 0);
            AssertColumn(linear, 2, 0, 0, 1);
        }

        [TestMethod]
        public void YUpConvention_CreateRotation_ShouldAlignLocalAxesToRequestedWorldAxes()
        {
            var convention = new ModelAxisConvention(LocalAxisDirection.PositiveX, LocalAxisDirection.PositiveY);
            Quaternion rotation = convention.CreateQuaternion(new Vector3(0, 1, 0), new Vector3(0, 0, 1));
            Matrix4x4 linear = Matrix4x4.CreateFromQuaternion(rotation);

            AssertColumn(linear, 0, 0, 1, 0);
            AssertColumn(linear, 1, 0, 0, 1);
            AssertColumn(linear, 2, 1, 0, 0);
        }

        [TestMethod]
        public void ReferenceRodAssetConvention_ShouldUseXForwardAndZUp()
        {
            ModelAxisConvention convention = ModelAxisConvention.CreateReferenceRodAssetConvention();

            Assert.AreEqual(LocalAxisDirection.PositiveX, convention.ForwardAxis);
            Assert.AreEqual(LocalAxisDirection.PositiveZ, convention.UpAxis);
            Assert.AreEqual(LocalAxisDirection.NegativeY, convention.SideAxis);
        }

        [TestMethod]
        public void VirtualObjectAssetConvention_CreateScaleVector_ShouldMapLengthWidthHeightToLocalAxes()
        {
            ModelAxisConvention convention = ModelAxisConvention.CreateVirtualObjectAssetConvention();

            var scale = convention.CreateScaleVector3(12.0, 5.0, 7.0);

            Assert.AreEqual(12.0, scale.X, 1e-6);
            Assert.AreEqual(5.0, scale.Y, 1e-6);
            Assert.AreEqual(7.0, scale.Z, 1e-6);
        }

        [TestMethod]
        public void YUpConvention_CreateScaleVector_ShouldMapUpSizeToLocalY()
        {
            ModelAxisConvention convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp);

            var scale = convention.CreateScaleVector3(12.0, 5.0, 7.0);

            Assert.AreEqual(12.0, scale.X, 1e-6);
            Assert.AreEqual(7.0, scale.Y, 1e-6);
            Assert.AreEqual(5.0, scale.Z, 1e-6);
        }

        private static void AssertColumn(Matrix4x4 matrix, int column, double x, double y, double z)
        {
            switch (column)
            {
                case 0:
                    Assert.AreEqual(x, matrix.M11, 1e-6);
                    Assert.AreEqual(y, matrix.M21, 1e-6);
                    Assert.AreEqual(z, matrix.M31, 1e-6);
                    break;
                case 1:
                    Assert.AreEqual(x, matrix.M12, 1e-6);
                    Assert.AreEqual(y, matrix.M22, 1e-6);
                    Assert.AreEqual(z, matrix.M32, 1e-6);
                    break;
                case 2:
                    Assert.AreEqual(x, matrix.M13, 1e-6);
                    Assert.AreEqual(y, matrix.M23, 1e-6);
                    Assert.AreEqual(z, matrix.M33, 1e-6);
                    break;
                default:
                    Assert.Fail("Only first 3 columns are valid.");
                    break;
            }
        }
    }
}
