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

namespace NavisworksTransport.UnitTests.CoordinateSystem
{
    [TestClass]
    public class AssemblyInstallationReferenceBuilderTests
    {
        [TestMethod]
        public void Build_ShouldCreatePlaneParallelToOpticalAxisAndProjectedAnchor()
        {
            Vector3 axisBase = new Vector3(0f, 0f, 0f);
            Vector3 axisDirection = Vector3.UnitX;
            Vector3 pickPoint = new Vector3(2f, 3f, 4f);

            AssemblyInstallationReferenceResult result =
                AssemblyInstallationReferenceBuilder.Build(axisBase, axisDirection, pickPoint);

            Assert.AreEqual(5f, result.OffsetDistance, 1e-4f);
            Assert.AreEqual(2f, result.AnchorPoint.X, 1e-4f);
            Assert.AreEqual(3f, result.AnchorPoint.Y, 1e-4f);
            Assert.AreEqual(4f, result.AnchorPoint.Z, 1e-4f);
            Assert.AreEqual(0f, Vector3.Dot(result.PlaneNormal, result.OpticalAxisDirection), 1e-4f);
            Assert.AreEqual(0f, Vector3.Dot(result.PlaneSpanDirection, result.OpticalAxisDirection), 1e-4f);
            Assert.AreEqual(0f, Vector3.Dot(result.PlaneNormal, result.PlaneSpanDirection), 1e-4f);
        }

        [TestMethod]
        public void BuildFromTwoPoints_ShouldCreatePlaneParallelToOpticalAxisAndContainBothPoints()
        {
            Vector3 axisBase = new Vector3(0f, 0f, 0f);
            Vector3 axisDirection = Vector3.UnitX;
            Vector3 pickPoint1 = new Vector3(2f, 3f, 4f);
            Vector3 pickPoint2 = new Vector3(6f, 3f, 6f);

            AssemblyInstallationReferenceResult result =
                AssemblyInstallationReferenceBuilder.Build(axisBase, axisDirection, pickPoint1, pickPoint2);

            Assert.AreEqual(3f, result.OffsetDistance, 1e-4f);
            Assert.AreEqual(4f, result.AnchorPoint.X, 1e-4f);
            Assert.AreEqual(3f, result.AnchorPoint.Y, 1e-4f);
            Assert.AreEqual(0f, result.AnchorPoint.Z, 1e-4f);
            Assert.AreEqual(0f, Vector3.Dot(result.PlaneNormal, result.OpticalAxisDirection), 1e-4f);
            Assert.AreEqual(0f, Vector3.Dot(result.PlaneSpanDirection, result.OpticalAxisDirection), 1e-4f);
            Assert.AreEqual(0f, Vector3.Dot(result.PlaneNormal, result.PlaneSpanDirection), 1e-4f);
            Assert.AreEqual(0f, Vector3.Dot(pickPoint1 - result.AnchorPoint, result.PlaneNormal), 1e-4f);
            Assert.AreEqual(0f, Vector3.Dot(pickPoint2 - result.AnchorPoint, result.PlaneNormal), 1e-4f);
        }

        [TestMethod]
        public void BuildFromTwoPoints_ShouldUseOpticalAxisProjectionAsInstallationCenterLine()
        {
            Vector3 axisBase = new Vector3(0f, 0f, 0f);
            Vector3 axisDirection = Vector3.UnitX;
            Vector3 pickPoint1 = new Vector3(2f, 3f, 4f);
            Vector3 pickPoint2 = new Vector3(6f, 3f, 6f);

            AssemblyInstallationReferenceResult result =
                AssemblyInstallationReferenceBuilder.Build(axisBase, axisDirection, pickPoint1, pickPoint2);

            Assert.AreEqual(0f, result.InstallLineBasePoint.X, 1e-4f);
            Assert.AreEqual(3f, result.InstallLineBasePoint.Y, 1e-4f);
            Assert.AreEqual(0f, result.InstallLineBasePoint.Z, 1e-4f);
            Assert.AreEqual(4f, result.AnchorPoint.X, 1e-4f);
            Assert.AreEqual(3f, result.AnchorPoint.Y, 1e-4f);
            Assert.AreEqual(0f, result.AnchorPoint.Z, 1e-4f);
        }

        [TestMethod]
        public void BuildFromTwoPoints_WhenPlanePointsAreNearlyAxisAligned_ShouldThrow()
        {
            Vector3 axisBase = new Vector3(0f, 0f, 0f);
            Vector3 axisDirection = Vector3.UnitX;
            Vector3 pickPoint1 = new Vector3(2f, 3f, 4f);
            Vector3 pickPoint2 = new Vector3(6f, 3.00001f, 4.00001f);

            InvalidOperationException ex = Assert.ThrowsException<InvalidOperationException>(
                () => AssemblyInstallationReferenceBuilder.Build(axisBase, axisDirection, pickPoint1, pickPoint2));

            StringAssert.Contains(ex.Message, "无法确定安装参考面");
        }

        [TestMethod]
        public void Build_WhenPickPointTooCloseToOpticalAxis_ShouldThrow()
        {
            Vector3 axisBase = new Vector3(0f, 0f, 0f);
            Vector3 axisDirection = Vector3.UnitX;
            Vector3 pickPoint = new Vector3(2f, 0f, 0f);

            InvalidOperationException ex = Assert.ThrowsException<InvalidOperationException>(
                () => AssemblyInstallationReferenceBuilder.Build(axisBase, axisDirection, pickPoint));

            StringAssert.Contains(ex.Message, "距离光轴过小");
        }
    }
}
