using System;
using System.Collections.Generic;
using System.Numerics;
using Autodesk.Navisworks.Api;

namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// Navisworks 外部坐标与内部 Canonical Space 之间的统一适配器。
    ///
    /// 约定:
    /// - 外部坐标 = Navisworks API 返回的世界坐标
    /// - 内部坐标 = Canonical Space,固定为 Z-up
    ///
    /// 当前仅支持:
    /// - Host Z-up -> Canonical Z-up(恒等)
    /// - Host Y-up -> Canonical Z-up(绕 X 轴 +90° 的等价变换)
    /// </summary>
    public sealed class HostCoordinateAdapter
    {
        /// <summary>
        /// Canonical Space 固定为 Z-up。
        /// </summary>
        public static readonly Vector3 CanonicalUpVector3 = new Vector3(0f, 0f, 1f);
        public static readonly Vector3D CanonicalUp = new Vector3D(0, 0, 1);

        /// <summary>
        /// 当前宿主坐标系类型。
        /// </summary>
        public CoordinateSystemType HostType { get; }

        /// <summary>
        /// 宿主坐标系中"向上轴"的索引。
        /// Y-up => 1, Z-up => 2。
        /// </summary>
        public int HostUpAxisIndex => HostType == CoordinateSystemType.YUp ? 1 : 2;

        /// <summary>
        /// 宿主坐标系中的单位 up 向量。
        /// </summary>
        public Vector3 HostUpVector3 => FromCanonicalVector3(CanonicalUpVector3);
        public Vector3D HostUpVector => FromCanonicalVector(CanonicalUp);

        public HostCoordinateAdapter(CoordinateSystemType hostType)
        {
            if (hostType == CoordinateSystemType.AutoDetect)
            {
                throw new ArgumentException("HostCoordinateAdapter 不接受 AutoDetect,必须传入明确的宿主坐标系。", nameof(hostType));
            }

            HostType = hostType;
        }

        /// <summary>
        /// 从当前坐标系管理器创建适配器。
        /// </summary>
        public static HostCoordinateAdapter CreateFromManager()
        {
            var hostType = CoordinateSystemManager.Instance.ConfiguredType == CoordinateSystemType.AutoDetect
                ? CoordinateSystemManager.Instance.GetResolvedType()
                : CoordinateSystemManager.Instance.ConfiguredType;

            return new HostCoordinateAdapter(hostType);
        }

        public Point3D ToCanonicalPoint(Point3D hostPoint)
        {
            if (hostPoint == null)
            {
                throw new ArgumentNullException(nameof(hostPoint));
            }

            return ToPoint3D(ToCanonicalPoint3(ToVector3(hostPoint)));
        }

        public Point3D FromCanonicalPoint(Point3D canonicalPoint)
        {
            if (canonicalPoint == null)
            {
                throw new ArgumentNullException(nameof(canonicalPoint));
            }

            return ToPoint3D(FromCanonicalPoint3(ToVector3(canonicalPoint)));
        }

        public Vector3D ToCanonicalVector(Vector3D hostVector)
        {
            if (hostVector == null)
            {
                throw new ArgumentNullException(nameof(hostVector));
            }

            return ToVector3D(ToCanonicalVector3(ToNumericsVector(hostVector)));
        }

        public Vector3D FromCanonicalVector(Vector3D canonicalVector)
        {
            if (canonicalVector == null)
            {
                throw new ArgumentNullException(nameof(canonicalVector));
            }

            return ToVector3D(FromCanonicalVector3(ToNumericsVector(canonicalVector)));
        }

        public BoundingBox3D ToCanonicalBounds(BoundingBox3D hostBounds)
        {
            if (hostBounds == null)
            {
                throw new ArgumentNullException(nameof(hostBounds));
            }

            CanonicalBounds3 canonicalBounds = ToCanonicalBounds3(new CanonicalBounds3(
                ToVector3(hostBounds.Min),
                ToVector3(hostBounds.Max)));
            return new BoundingBox3D(ToPoint3D(canonicalBounds.Min), ToPoint3D(canonicalBounds.Max));
        }

        public BoundingBox3D FromCanonicalBounds(BoundingBox3D canonicalBounds)
        {
            if (canonicalBounds == null)
            {
                throw new ArgumentNullException(nameof(canonicalBounds));
            }

            CanonicalBounds3 hostBounds = FromCanonicalBounds3(new CanonicalBounds3(
                ToVector3(canonicalBounds.Min),
                ToVector3(canonicalBounds.Max)));
            return new BoundingBox3D(ToPoint3D(hostBounds.Min), ToPoint3D(hostBounds.Max));
        }

        public Vector3 ToCanonicalPoint3(Vector3 hostPoint)
        {
            switch (HostType)
            {
                case CoordinateSystemType.ZUp:
                    return hostPoint;
                case CoordinateSystemType.YUp:
                    return new Vector3(hostPoint.X, -hostPoint.Z, hostPoint.Y);
                default:
                    throw new InvalidOperationException($"不支持的宿主坐标系: {HostType}");
            }
        }

        public Vector3 FromCanonicalPoint3(Vector3 canonicalPoint)
        {
            switch (HostType)
            {
                case CoordinateSystemType.ZUp:
                    return canonicalPoint;
                case CoordinateSystemType.YUp:
                    return new Vector3(canonicalPoint.X, canonicalPoint.Z, -canonicalPoint.Y);
                default:
                    throw new InvalidOperationException($"不支持的宿主坐标系: {HostType}");
            }
        }

        public Vector3 ToCanonicalVector3(Vector3 hostVector)
        {
            return ToCanonicalPoint3(hostVector);
        }

        public Vector3 FromCanonicalVector3(Vector3 canonicalVector)
        {
            return FromCanonicalPoint3(canonicalVector);
        }

        public Matrix4x4 FromCanonicalLinearTransform(Matrix4x4 canonicalLinear)
        {
            Vector3 hostX = FromCanonicalVector3(new Vector3(canonicalLinear.M11, canonicalLinear.M21, canonicalLinear.M31));
            Vector3 hostY = FromCanonicalVector3(new Vector3(canonicalLinear.M12, canonicalLinear.M22, canonicalLinear.M32));
            Vector3 hostZ = FromCanonicalVector3(new Vector3(canonicalLinear.M13, canonicalLinear.M23, canonicalLinear.M33));

            return new Matrix4x4(
                hostX.X, hostY.X, hostZ.X, 0f,
                hostX.Y, hostY.Y, hostZ.Y, 0f,
                hostX.Z, hostY.Z, hostZ.Z, 0f,
                0f, 0f, 0f, 1f);
        }

        public Rotation3D FromCanonicalRotation(Quaternion canonicalRotation)
        {
            return FromCanonicalRotationLegacy(canonicalRotation);
        }

        public Rotation3D FromCanonicalRotationLegacy(Quaternion canonicalRotation)
        {
            Matrix4x4 canonicalLinear = Matrix4x4.CreateFromQuaternion(canonicalRotation);
            Matrix4x4 hostLinear = FromCanonicalLinearTransform(canonicalLinear);
            return CreateRotationFromLinearComponents(
                hostLinear.M11, hostLinear.M12, hostLinear.M13,
                hostLinear.M21, hostLinear.M22, hostLinear.M23,
                hostLinear.M31, hostLinear.M32, hostLinear.M33);
        }

        public Rotation3D FromCanonicalRotationDirect(Quaternion canonicalRotation)
        {
            Matrix4x4 canonicalLinear = Matrix4x4.CreateFromQuaternion(canonicalRotation);
            Matrix4x4 hostLinear = FromCanonicalLinearTransform(canonicalLinear);
            Quaternion hostQuaternion = Quaternion.Normalize(Quaternion.CreateFromRotationMatrix(hostLinear));
            return new Rotation3D(hostQuaternion.X, hostQuaternion.Y, hostQuaternion.Z, hostQuaternion.W);
        }

        /// <summary>
        /// 将"宿主坐标系语义下"的四元数直接包装成 Navisworks 的 Rotation3D。
        ///
        /// 项目硬约束:
        /// - Rotation3D(x, y, z, w) 的参数顺序固定是 (x, y, z, w)
        /// - Rotation3D.A/B/C/D 分别对应 x/y/z/w
        ///
        /// 这里不做任何轴交换、符号翻转或重新解释,只负责把已经确定好的宿主四元数写入 Navisworks。
        /// </summary>
        public Rotation3D FromHostQuaternion(Quaternion hostRotation)
        {
            return FromHostQuaternionLegacy(hostRotation);
        }

        public Rotation3D FromHostQuaternionLegacy(Quaternion hostRotation)
        {
            Matrix4x4 hostLinear = Matrix4x4.CreateFromQuaternion(hostRotation);
            return CreateRotationFromLinearComponents(
                hostLinear.M11, hostLinear.M12, hostLinear.M13,
                hostLinear.M21, hostLinear.M22, hostLinear.M23,
                hostLinear.M31, hostLinear.M32, hostLinear.M33);
        }

        public Rotation3D FromHostQuaternionDirect(Quaternion hostRotation)
        {
            Quaternion normalizedHostRotation = Quaternion.Normalize(hostRotation);
            return new Rotation3D(
                normalizedHostRotation.X,
                normalizedHostRotation.Y,
                normalizedHostRotation.Z,
                normalizedHostRotation.W);
        }

        /// <summary>
        /// 根据宿主坐标系的世界轴，构造一个“宿主 X/Y/Z 角度修正”四元数。
        ///
        /// 关键约束：
        /// - XDegrees 永远表示绕宿主世界 X 轴旋转
        /// - YDegrees 永远表示绕宿主世界 up 轴旋转
        /// - ZDegrees 永远表示绕宿主世界 non-up 轴旋转
        /// - 乘法顺序和 MoveItemCombinedAxisRotationAndTranslation 一致：
        ///   up * nonUp * X，即先X、再nonUp、最后up
        ///
        /// 通过 HostUpVector3 自动兼容 YUp 和 ZUp，不引入 HostType 分支。
        /// </summary>
        public Quaternion CreateHostRotationCorrection(LocalEulerRotationCorrection correction)
        {
            if (correction.IsZero)
            {
                return Quaternion.Identity;
            }

            Vector3 hostForward = Vector3.UnitX;
            Vector3 hostUp = HostUpVector3;
            Vector3 hostNonUp = Vector3.Normalize(Vector3.Cross(hostForward, hostUp));

            Quaternion qx = Quaternion.CreateFromAxisAngle(hostForward, DegreesToRadians(correction.XDegrees));
            Quaternion qnonUp = Quaternion.CreateFromAxisAngle(hostNonUp, DegreesToRadians(correction.ZDegrees));
            Quaternion qup = Quaternion.CreateFromAxisAngle(hostUp, DegreesToRadians(correction.YDegrees));
            return Quaternion.Normalize(qup * qnonUp * qx);
        }

        /// <summary>
        /// 将宿主世界轴角度修正叠加到一个已经位于宿主坐标系中的基姿态上。
        ///
        /// 参数语义:
        /// - baselineQuaternion: 已经求好的宿主基姿态
        /// - correction: 仍按宿主世界 X/Y/Z 解释的角度修正
        ///
        /// 注意:
        /// - 这里的组合顺序直接决定"修正是绕宿主世界轴"还是"绕基姿态当前轴"
        /// - 后续若修改乘法顺序,必须同步更新真实场景测试,不能靠肉眼猜
        /// - 本方法只用于宿主坐标系;不要把资产坐标系或内部坐标系的四元数传进来混用
        /// </summary>
        public Quaternion ComposeHostQuaternion(Quaternion baselineQuaternion, LocalEulerRotationCorrection correction)
        {
            Quaternion correctionQuaternion = CreateHostRotationCorrection(correction);
            Quaternion inverseCorrectionQuaternion = Quaternion.Conjugate(correctionQuaternion);
            return Quaternion.Normalize(baselineQuaternion * inverseCorrectionQuaternion);
        }

        /// <summary>
        /// 将"宿主语义 X/Y/Z"的角度修正重映射为当前真实物体本地 X/Y/Z 的角度修正。
        /// </summary>
        public static LocalEulerRotationCorrection RemapHostSemanticCorrectionToLocalAxes(
            LocalEulerRotationCorrection hostCorrection,
            LocalAxisDirection hostSemanticXAxisLocalAxis,
            LocalAxisDirection hostSemanticYAxisLocalAxis,
            LocalAxisDirection hostSemanticZAxisLocalAxis)
        {
            if (hostCorrection.IsZero)
            {
                return LocalEulerRotationCorrection.Zero;
            }

            double localXDegrees = 0.0;
            double localYDegrees = 0.0;
            double localZDegrees = 0.0;

            ApplyMappedAngle(ref localXDegrees, ref localYDegrees, ref localZDegrees, hostSemanticXAxisLocalAxis, hostCorrection.XDegrees);
            ApplyMappedAngle(ref localXDegrees, ref localYDegrees, ref localZDegrees, hostSemanticYAxisLocalAxis, hostCorrection.YDegrees);
            ApplyMappedAngle(ref localXDegrees, ref localYDegrees, ref localZDegrees, hostSemanticZAxisLocalAxis, hostCorrection.ZDegrees);

            return new LocalEulerRotationCorrection(localXDegrees, localYDegrees, localZDegrees);
        }

        /// <summary>
        /// 将宿主 X/Y/Z 三轴旋转角转换为内部 Canonical Space 中的旋转四元数。
        ///
        /// 关键约束:
        /// - XDegrees 永远表示绕宿主世界 X 轴旋转
        /// - YDegrees 永远表示绕宿主世界 Y 轴旋转(up轴)
        /// - ZDegrees 永远表示绕宿主世界 Z 轴旋转(non-up轴)
        /// - 乘法顺序和 MoveItemCombinedAxisRotationAndTranslation 一致:
        ///   up * nonUp * X,即先X、再nonUp、最后up
        ///
        /// 通过 Canonical Space 的轴映射自动兼容 YUp 和 ZUp,不引入 HostType 分支。
        /// </summary>
        public Quaternion CreateCanonicalRotationCorrection(LocalEulerRotationCorrection correction)
        {
            if (correction.IsZero)
            {
                return Quaternion.Identity;
            }

            Vector3 canonicalForward = Vector3.Normalize(ToCanonicalVector3(Vector3.UnitX));
            Vector3 canonicalUp = CanonicalUpVector3;
            Vector3 canonicalNonUp = Vector3.Normalize(Vector3.Cross(canonicalForward, canonicalUp));

            Quaternion qx = Quaternion.CreateFromAxisAngle(canonicalForward, DegreesToRadians(correction.XDegrees));
            Quaternion qnonUp = Quaternion.CreateFromAxisAngle(canonicalNonUp, DegreesToRadians(correction.ZDegrees));
            Quaternion qup = Quaternion.CreateFromAxisAngle(canonicalUp, DegreesToRadians(correction.YDegrees));
            return Quaternion.Normalize(qup * qnonUp * qx);
        }

        private static Rotation3D CreateRotationFromLinearComponents(
            double m00, double m01, double m02,
            double m10, double m11, double m12,
            double m20, double m21, double m22)
        {
            double qx;
            double qy;
            double qz;
            double qw;

            double trace = m00 + m11 + m22;
            if (trace > 0.0)
            {
                double s = Math.Sqrt(trace + 1.0) * 2.0;
                qw = 0.25 * s;
                qx = (m21 - m12) / s;
                qy = (m02 - m20) / s;
                qz = (m10 - m01) / s;
            }
            else if (m00 > m11 && m00 > m22)
            {
                double s = Math.Sqrt(1.0 + m00 - m11 - m22) * 2.0;
                qw = (m21 - m12) / s;
                qx = 0.25 * s;
                qy = (m01 + m10) / s;
                qz = (m02 + m20) / s;
            }
            else if (m11 > m22)
            {
                double s = Math.Sqrt(1.0 + m11 - m00 - m22) * 2.0;
                qw = (m02 - m20) / s;
                qx = (m01 + m10) / s;
                qy = 0.25 * s;
                qz = (m12 + m21) / s;
            }
            else
            {
                double s = Math.Sqrt(1.0 + m22 - m00 - m11) * 2.0;
                qw = (m10 - m01) / s;
                qx = (m02 + m20) / s;
                qy = (m12 + m21) / s;
                qz = 0.25 * s;
            }

            return new Rotation3D(qx, qy, qz, qw);
        }

        public CanonicalBounds3 ToCanonicalBounds3(CanonicalBounds3 hostBounds)
        {
            return RebuildBounds3(hostBounds, ToCanonicalPoint3);
        }

        public CanonicalBounds3 FromCanonicalBounds3(CanonicalBounds3 canonicalBounds)
        {
            return RebuildBounds3(canonicalBounds, FromCanonicalPoint3);
        }

        private static BoundingBox3D RebuildBounds(BoundingBox3D sourceBounds, Func<Point3D, Point3D> pointTransform)
        {
            var corners = GetCorners(sourceBounds);
            var transformedCorners = new List<Point3D>(corners.Count);

            foreach (var corner in corners)
            {
                transformedCorners.Add(pointTransform(corner));
            }

            double minX = double.MaxValue;
            double minY = double.MaxValue;
            double minZ = double.MaxValue;
            double maxX = double.MinValue;
            double maxY = double.MinValue;
            double maxZ = double.MinValue;

            foreach (var point in transformedCorners)
            {
                minX = Math.Min(minX, point.X);
                minY = Math.Min(minY, point.Y);
                minZ = Math.Min(minZ, point.Z);
                maxX = Math.Max(maxX, point.X);
                maxY = Math.Max(maxY, point.Y);
                maxZ = Math.Max(maxZ, point.Z);
            }

            return new BoundingBox3D(new Point3D(minX, minY, minZ), new Point3D(maxX, maxY, maxZ));
        }

        private static CanonicalBounds3 RebuildBounds3(CanonicalBounds3 sourceBounds, Func<Vector3, Vector3> pointTransform)
        {
            var corners = GetCorners3(sourceBounds);
            Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
            Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);

            foreach (var corner in corners)
            {
                Vector3 point = pointTransform(corner);
                min = Vector3.Min(min, point);
                max = Vector3.Max(max, point);
            }

            return new CanonicalBounds3(min, max);
        }

        private static List<Point3D> GetCorners(BoundingBox3D bounds)
        {
            return new List<Point3D>
            {
                new Point3D(bounds.Min.X, bounds.Min.Y, bounds.Min.Z),
                new Point3D(bounds.Max.X, bounds.Min.Y, bounds.Min.Z),
                new Point3D(bounds.Min.X, bounds.Max.Y, bounds.Min.Z),
                new Point3D(bounds.Max.X, bounds.Max.Y, bounds.Min.Z),
                new Point3D(bounds.Min.X, bounds.Min.Y, bounds.Max.Z),
                new Point3D(bounds.Max.X, bounds.Min.Y, bounds.Max.Z),
                new Point3D(bounds.Min.X, bounds.Max.Y, bounds.Max.Z),
                new Point3D(bounds.Max.X, bounds.Max.Y, bounds.Max.Z)
            };
        }

        private static List<Vector3> GetCorners3(CanonicalBounds3 bounds)
        {
            return new List<Vector3>
            {
                new Vector3(bounds.Min.X, bounds.Min.Y, bounds.Min.Z),
                new Vector3(bounds.Max.X, bounds.Min.Y, bounds.Min.Z),
                new Vector3(bounds.Min.X, bounds.Max.Y, bounds.Min.Z),
                new Vector3(bounds.Max.X, bounds.Max.Y, bounds.Min.Z),
                new Vector3(bounds.Min.X, bounds.Min.Y, bounds.Max.Z),
                new Vector3(bounds.Max.X, bounds.Min.Y, bounds.Max.Z),
                new Vector3(bounds.Min.X, bounds.Max.Y, bounds.Max.Z),
                new Vector3(bounds.Max.X, bounds.Max.Y, bounds.Max.Z)
            };
        }

        private static Vector3 ToVector3(Point3D point)
        {
            return new Vector3((float)point.X, (float)point.Y, (float)point.Z);
        }

        private static Vector3 ToNumericsVector(Vector3D vector)
        {
            return new Vector3((float)vector.X, (float)vector.Y, (float)vector.Z);
        }

        private static Point3D ToPoint3D(Vector3 point)
        {
            return new Point3D(point.X, point.Y, point.Z);
        }

        private static Vector3D ToVector3D(Vector3 vector)
        {
            return new Vector3D(vector.X, vector.Y, vector.Z);
        }

        private static float DegreesToRadians(double degrees)
        {
            return (float)(degrees * Math.PI / 180.0);
        }

        private static void ApplyMappedAngle(
            ref double localXDegrees,
            ref double localYDegrees,
            ref double localZDegrees,
            LocalAxisDirection mappedLocalAxis,
            double hostDegrees)
        {
            switch (mappedLocalAxis)
            {
                case LocalAxisDirection.PositiveX:
                    localXDegrees += hostDegrees;
                    break;
                case LocalAxisDirection.NegativeX:
                    localXDegrees -= hostDegrees;
                    break;
                case LocalAxisDirection.PositiveY:
                    localYDegrees += hostDegrees;
                    break;
                case LocalAxisDirection.NegativeY:
                    localYDegrees -= hostDegrees;
                    break;
                case LocalAxisDirection.PositiveZ:
                    localZDegrees += hostDegrees;
                    break;
                case LocalAxisDirection.NegativeZ:
                    localZDegrees -= hostDegrees;
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(mappedLocalAxis), mappedLocalAxis, null);
            }
        }
    }
}
