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

namespace NavisworksTransport.UnitTests.CoordinateSystem
{
    [TestClass]
    public class HoistingCoordinateHelperTests
    {
        [TestMethod]
        public void OffsetAlongUp_YUp_ShouldChangeHostYOnly()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var startPoint = new Vector3(1.0f, 2.0f, 3.0f);

            Vector3 liftedPoint = HoistingCoordinateHelper.OffsetAlongUp3(startPoint, 10.0, adapter);

            AssertPoint(liftedPoint, 1.0, 12.0, 3.0);
        }

        [TestMethod]
        public void ProjectToAerialElevation_YUp_ShouldKeepGroundHorizontalCoords()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var groundPoint = new Vector3(4.0f, 5.0f, 6.0f);
            var aerialReferencePoint = new Vector3(1.0f, 12.0f, 3.0f);

            Vector3 projectedPoint = HoistingCoordinateHelper.ProjectToAerialElevation3(groundPoint, aerialReferencePoint, adapter);

            AssertPoint(projectedPoint, 4.0, 12.0, 6.0);
        }

        [TestMethod]
        public void RelativeHeight_YUp_ShouldUseHostYDifference()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var groundPoint = new Vector3(-10.0f, -30.0f, 8.0f);
            var aerialPoint = new Vector3(-10.0f, 35.0f, 8.0f);

            double relativeHeight = HoistingCoordinateHelper.GetRelativeHeight3(groundPoint, aerialPoint, adapter);

            Assert.AreEqual(65.0, relativeHeight, 1e-9);
        }

        [TestMethod]
        public void SetElevation_YUp_ShouldRestoreClickedGroundElevationWithoutChangingHorizontalProjection()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var aerialPoint = new Vector3(-180.50f, 28.17f, 14.83f);
            var clickedGroundPoint = new Vector3(-180.50f, -12.40f, 14.83f);

            Vector3 landingPoint = HoistingCoordinateHelper.SetElevation3(
                aerialPoint,
                HoistingCoordinateHelper.GetElevation(clickedGroundPoint, adapter),
                adapter);

            AssertPoint(landingPoint, -180.50, -12.40, 14.83);
        }

        [TestMethod]
        public void CreateHorizontalTurnPoint_YUp_ShouldOperateInHostXZPlane()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var currentPoint = new Vector3(-168.70f, 28.17f, -30.01f);
            var nextPoint = new Vector3(-180.50f, 28.17f, 14.83f);

            Vector3 turnPointKeepCurrentX = HoistingCoordinateHelper.CreateHorizontalTurnPoint3(
                currentPoint,
                nextPoint,
                keepCurrentFirstHorizontalAxis: true,
                adapter: adapter);
            Vector3 turnPointKeepCurrentZ = HoistingCoordinateHelper.CreateHorizontalTurnPoint3(
                currentPoint,
                nextPoint,
                keepCurrentFirstHorizontalAxis: false,
                adapter: adapter);

            AssertPoint(turnPointKeepCurrentX, -168.70, 28.17, 14.83);
            AssertPoint(turnPointKeepCurrentZ, -180.50, 28.17, -30.01);
        }

        [TestMethod]
        public void GetHorizontalDirection_YUp_ShouldProjectToHostXZPlane()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var fromPoint = new Vector3(-164.81f, 45.45f, 74.69f);
            var toPoint = new Vector3(-176.90f, 45.45f, 40.48f);

            Vector3 horizontalDirection = HoistingCoordinateHelper.GetHorizontalDirection3(fromPoint, toPoint, adapter);

            Assert.AreEqual(0.0, horizontalDirection.Y, 1e-5);
            Assert.AreEqual(1.0, horizontalDirection.Length(), 1e-5);
            Assert.IsTrue(horizontalDirection.X < 0.0f);
            Assert.IsTrue(horizontalDirection.Z < 0.0f);
        }

        [TestMethod]
        public void ExtendVerticalTransitionForObjectSpace_YUp_ShouldMoveLowerPointAlongHostY()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var lowerPoint = new Vector3(-191.56f, 29.05f, -2.63f);

            Vector3 extendedPoint = HoistingCoordinateHelper.ExtendVerticalTransitionForObjectSpace3(
                lowerPoint,
                4.0,
                isLowerPoint: true,
                adapter: adapter);

            AssertPoint(extendedPoint, -191.56, 25.05, -2.63);
        }

        [TestMethod]
        public void ExtendVerticalTransitionForObjectSpace_YUp_ShouldNotChangeHigherPoint()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
            var higherPoint = new Vector3(-191.56f, 45.45f, -2.63f);

            Vector3 unchangedPoint = HoistingCoordinateHelper.ExtendVerticalTransitionForObjectSpace3(
                higherPoint,
                4.0,
                isLowerPoint: false,
                adapter: adapter);

            AssertPoint(unchangedPoint, -191.56, 45.45, -2.63);
        }

        [TestMethod]
        public void OffsetAlongUp_ZUp_ShouldChangeHostZOnly()
        {
            var adapter = new HostCoordinateAdapter(CoordinateSystemType.ZUp);
            var startPoint = new Vector3(1.0f, 2.0f, 3.0f);

            Vector3 liftedPoint = HoistingCoordinateHelper.OffsetAlongUp3(startPoint, 10.0, adapter);

            AssertPoint(liftedPoint, 1.0, 2.0, 13.0);
        }

        private static void AssertPoint(Vector3 actual, double x, double y, double z)
        {
            Assert.AreEqual(x, actual.X, 1e-5);
            Assert.AreEqual(y, actual.Y, 1e-5);
            Assert.AreEqual(z, actual.Z, 1e-5);
        }
    }
}
