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

namespace NavisworksTransport.Utils
{
    /// <summary>
    /// 包围盒几何计算工具类
    /// 提供包围盒之间的距离计算、中心点计算、相交检测等几何运算方法
    /// </summary>
    public static class BoundingBoxGeometryUtils
    {
        /// <summary>
        /// 计算两个包围盒之间的最短距离（真实碰撞距离）
        /// </summary>
        /// <param name="box1">第一个包围盒</param>
        /// <param name="box2">第二个包围盒</param>
        /// <returns>两个包围盒之间的最短距离，如果相交则返回0</returns>
        public static double CalculateDistance(BoundingBox3D box1, BoundingBox3D box2)
        {
            // 计算每个轴上的最短距离
            double xDistance = 0;
            if (box1.Max.X < box2.Min.X)
                xDistance = box2.Min.X - box1.Max.X;
            else if (box2.Max.X < box1.Min.X)
                xDistance = box1.Min.X - box2.Max.X;

            double yDistance = 0;
            if (box1.Max.Y < box2.Min.Y)
                yDistance = box2.Min.Y - box1.Max.Y;
            else if (box2.Max.Y < box1.Min.Y)
                yDistance = box1.Min.Y - box2.Max.Y;

            double zDistance = 0;
            if (box1.Max.Z < box2.Min.Z)
                zDistance = box2.Min.Z - box1.Max.Z;
            else if (box2.Max.Z < box1.Min.Z)
                zDistance = box1.Min.Z - box2.Max.Z;

            // 如果包围盒相交，返回0；否则返回3D空间中的最短距离
            if (xDistance == 0 && yDistance == 0 && zDistance == 0)
                return 0.0; // 真正相交

            return Math.Sqrt(xDistance * xDistance + yDistance * yDistance + zDistance * zDistance);
        }

        /// <summary>
        /// 计算两个包围盒中心点之间的距离
        /// </summary>
        /// <param name="box1">第一个包围盒</param>
        /// <param name="box2">第二个包围盒</param>
        /// <returns>两个包围盒中心点之间的距离</returns>
        public static double CalculateCenterDistance(BoundingBox3D box1, BoundingBox3D box2)
        {
            var center1 = GetBoundingBoxCenter(box1);
            var center2 = GetBoundingBoxCenter(box2);

            return GeometryHelper.CalculatePointDistance(center1, center2);
        }

        /// <summary>
        /// 计算两个包围盒之间的中心点
        /// </summary>
        /// <param name="box1">第一个包围盒</param>
        /// <param name="box2">第二个包围盒</param>
        /// <returns>两个包围盒中心点的中点</returns>
        public static Point3D CalculateCenter(BoundingBox3D box1, BoundingBox3D box2)
        {
            var center1 = new Point3D(
                (box1.Min.X + box1.Max.X) / 2,
                (box1.Min.Y + box1.Max.Y) / 2,
                (box1.Min.Z + box1.Max.Z) / 2
            );

            var center2 = new Point3D(
                (box2.Min.X + box2.Max.X) / 2,
                (box2.Min.Y + box2.Max.Y) / 2,
                (box2.Min.Z + box2.Max.Z) / 2
            );

            return new Point3D(
                (center1.X + center2.X) / 2,
                (center1.Y + center2.Y) / 2,
                (center1.Z + center2.Z) / 2
            );
        }

        /// <summary>
        /// 检查两个包围盒是否相交（带容差）
        /// </summary>
        /// <param name="box1">第一个包围盒</param>
        /// <param name="box2">第二个包围盒</param>
        /// <param name="tolerance">容差值</param>
        /// <returns>如果包围盒在容差范围内相交则返回true</returns>
        public static bool BoundingBoxesIntersectWithTolerance(BoundingBox3D box1, BoundingBox3D box2, double tolerance)
        {
            // 🔥 修改：计算每个方向的最小距离
            // 只有当所有方向的最小距离都小于检测间隙时，才判定为碰撞
            // 这样可以避免"一个方向相交，另一个方向很远"的情况

            // 计算X方向的最小距离
            double distanceX;
            if (box1.Max.X < box2.Min.X)
            {
                // box1在box2左侧
                distanceX = box2.Min.X - box1.Max.X;
            }
            else if (box2.Max.X < box1.Min.X)
            {
                // box2在box1左侧
                distanceX = box1.Min.X - box2.Max.X;
            }
            else
            {
                // X方向相交
                distanceX = 0;
            }

            // 计算Y方向的最小距离
            double distanceY;
            if (box1.Max.Y < box2.Min.Y)
            {
                // box1在box2下方
                distanceY = box2.Min.Y - box1.Max.Y;
            }
            else if (box2.Max.Y < box1.Min.Y)
            {
                // box2在box1下方
                distanceY = box1.Min.Y - box2.Max.Y;
            }
            else
            {
                // Y方向相交
                distanceY = 0;
            }

            // 计算Z方向的最小距离
            double distanceZ;
            if (box1.Max.Z < box2.Min.Z)
            {
                // box1在box2下方
                distanceZ = box2.Min.Z - box1.Max.Z;
            }
            else if (box2.Max.Z < box1.Min.Z)
            {
                // box2在box1下方
                distanceZ = box1.Min.Z - box2.Max.Z;
            }
            else
            {
                // Z方向相交
                distanceZ = 0;
            }

            // 计算最大距离（最接近的方向）
            double maxDistance = Math.Max(distanceX, Math.Max(distanceY, distanceZ));

            // 只有当最大距离小于检测间隙时，才判定为碰撞
            return maxDistance <= tolerance;
        }

        /// <summary>
        /// 检查两个包围盒是否相交
        /// </summary>
        /// <param name="box1">第一个包围盒</param>
        /// <param name="box2">第二个包围盒</param>
        /// <returns>如果包围盒相交则返回true</returns>
        public static bool BoundingBoxesIntersect(BoundingBox3D box1, BoundingBox3D box2)
        {
            return box1.Min.X <= box2.Max.X && box1.Max.X >= box2.Min.X &&
                   box1.Min.Y <= box2.Max.Y && box1.Max.Y >= box2.Min.Y &&
                   box1.Min.Z <= box2.Max.Z && box1.Max.Z >= box2.Min.Z;
        }

        /// <summary>
        /// 获取包围盒的中心点
        /// </summary>
        /// <param name="boundingBox">包围盒</param>
        /// <returns>包围盒的中心点</returns>
        public static Point3D GetBoundingBoxCenter(BoundingBox3D boundingBox)
        {
            return new Point3D(
                (boundingBox.Min.X + boundingBox.Max.X) / 2,
                (boundingBox.Min.Y + boundingBox.Max.Y) / 2,
                (boundingBox.Min.Z + boundingBox.Max.Z) / 2
            );
        }

        /// <summary>
        /// 获取包围盒的体积
        /// </summary>
        /// <param name="boundingBox">包围盒</param>
        /// <returns>包围盒的体积</returns>
        public static double GetBoundingBoxVolume(BoundingBox3D boundingBox)
        {
            var width = boundingBox.Max.X - boundingBox.Min.X;
            var height = boundingBox.Max.Y - boundingBox.Min.Y;
            var depth = boundingBox.Max.Z - boundingBox.Min.Z;
            return width * height * depth;
        }

        /// <summary>
        /// 扩展包围盒（在各个方向上增加指定的边距）
        /// </summary>
        /// <param name="boundingBox">原始包围盒</param>
        /// <param name="margin">边距值</param>
        /// <returns>扩展后的包围盒</returns>
        public static BoundingBox3D ExpandBoundingBox(BoundingBox3D boundingBox, double margin)
        {
            return new BoundingBox3D(
                new Point3D(
                    boundingBox.Min.X - margin,
                    boundingBox.Min.Y - margin,
                    boundingBox.Min.Z - margin
                ),
                new Point3D(
                    boundingBox.Max.X + margin,
                    boundingBox.Max.Y + margin,
                    boundingBox.Max.Z + margin
                )
            );
        }

        /// <summary>
        /// 检查点是否在包围盒内
        /// </summary>
        /// <param name="point">要检查的点</param>
        /// <param name="boundingBox">包围盒</param>
        /// <param name="tolerance">容差值</param>
        /// <returns>如果点在包围盒内则返回true</returns>
        public static bool IsPointInBoundingBox(Point3D point, BoundingBox3D boundingBox, double tolerance = 0.0)
        {
            return point.X >= boundingBox.Min.X - tolerance && point.X <= boundingBox.Max.X + tolerance &&
                   point.Y >= boundingBox.Min.Y - tolerance && point.Y <= boundingBox.Max.Y + tolerance &&
                   point.Z >= boundingBox.Min.Z - tolerance && point.Z <= boundingBox.Max.Z + tolerance;
        }

        /// <summary>
        /// 计算模型项列表的总边界
        /// </summary>
        /// <param name="modelItems">模型项列表</param>
        /// <returns>总边界框</returns>
        public static BoundingBox3D CalculateTotalBounds(IEnumerable<ModelItem> modelItems)
        {
            var itemList = modelItems?.ToList();
            if (itemList == null || !itemList.Any())
            {
                return new BoundingBox3D(new Point3D(0, 0, 0), new Point3D(0, 0, 0));
            }

            var firstBounds = itemList.First().BoundingBox();
            double minX = firstBounds.Min.X;
            double minY = firstBounds.Min.Y;
            double minZ = firstBounds.Min.Z;
            double maxX = firstBounds.Max.X;
            double maxY = firstBounds.Max.Y;
            double maxZ = firstBounds.Max.Z;

            foreach (var item in itemList.Skip(1))
            {
                var bounds = item.BoundingBox();
                minX = Math.Min(minX, bounds.Min.X);
                minY = Math.Min(minY, bounds.Min.Y);
                minZ = Math.Min(minZ, bounds.Min.Z);
                maxX = Math.Max(maxX, bounds.Max.X);
                maxY = Math.Max(maxY, bounds.Max.Y);
                maxZ = Math.Max(maxZ, bounds.Max.Z);
            }

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

        /// <summary>
        /// 计算包围盒旋转后，新AABB中心相对于原中心的偏移。
        /// 这是解决"旋转后BoundingBox中心漂移"问题的核心数学方法。
        /// 
        /// [备注] 此方法仅供参考，当前代码中未使用。保留作为数学工具方法。
        /// </summary>
        /// <param name="originalBounds">原始包围盒</param>
        /// <param name="rotation">旋转（世界坐标系）</param>
        /// <returns>新AABB中心相对于原中心的偏移量</returns>
        public static Vector3D CalculateRotatedBoundingBoxCenterOffset(BoundingBox3D originalBounds, Rotation3D rotation)
        {
            // 原包围盒中心
            Point3D originalCenter = new Point3D(
                (originalBounds.Min.X + originalBounds.Max.X) / 2.0,
                (originalBounds.Min.Y + originalBounds.Max.Y) / 2.0,
                (originalBounds.Min.Z + originalBounds.Max.Z) / 2.0);

            // 旋转矩阵
            var linear = new Transform3D(rotation).Linear;

            // 原始包围盒的8个角点（相对于原中心）
            var corners = new[]
            {
                new Point3D(originalBounds.Min.X - originalCenter.X, originalBounds.Min.Y - originalCenter.Y, originalBounds.Min.Z - originalCenter.Z),
                new Point3D(originalBounds.Max.X - originalCenter.X, originalBounds.Min.Y - originalCenter.Y, originalBounds.Min.Z - originalCenter.Z),
                new Point3D(originalBounds.Min.X - originalCenter.X, originalBounds.Max.Y - originalCenter.Y, originalBounds.Min.Z - originalCenter.Z),
                new Point3D(originalBounds.Max.X - originalCenter.X, originalBounds.Max.Y - originalCenter.Y, originalBounds.Min.Z - originalCenter.Z),
                new Point3D(originalBounds.Min.X - originalCenter.X, originalBounds.Min.Y - originalCenter.Y, originalBounds.Max.Z - originalCenter.Z),
                new Point3D(originalBounds.Max.X - originalCenter.X, originalBounds.Min.Y - originalCenter.Y, originalBounds.Max.Z - originalCenter.Z),
                new Point3D(originalBounds.Min.X - originalCenter.X, originalBounds.Max.Y - originalCenter.Y, originalBounds.Max.Z - originalCenter.Z),
                new Point3D(originalBounds.Max.X - originalCenter.X, originalBounds.Max.Y - originalCenter.Y, originalBounds.Max.Z - originalCenter.Z)
            };

            // 计算旋转后的角点，并求新AABB
            double minX = double.MaxValue, minY = double.MaxValue, minZ = double.MaxValue;
            double maxX = double.MinValue, maxY = double.MinValue, maxZ = double.MinValue;

            foreach (var corner in corners)
            {
                // 应用旋转
                double rx = linear.Get(0, 0) * corner.X + linear.Get(0, 1) * corner.Y + linear.Get(0, 2) * corner.Z;
                double ry = linear.Get(1, 0) * corner.X + linear.Get(1, 1) * corner.Y + linear.Get(1, 2) * corner.Z;
                double rz = linear.Get(2, 0) * corner.X + linear.Get(2, 1) * corner.Y + linear.Get(2, 2) * corner.Z;

                minX = Math.Min(minX, rx);
                minY = Math.Min(minY, ry);
                minZ = Math.Min(minZ, rz);
                maxX = Math.Max(maxX, rx);
                maxY = Math.Max(maxY, ry);
                maxZ = Math.Max(maxZ, rz);
            }

            // 新AABB中心（相对于原中心）
            Vector3D newCenterOffset = new Vector3D(
                (minX + maxX) / 2.0,
                (minY + maxY) / 2.0,
                (minZ + maxZ) / 2.0);

            return newCenterOffset;
        }
    }
}