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

namespace NavisworksTransport.Utils
{
    /// <summary>
    /// 剖面盒辅助类 - 用于优化碰撞检测性能
    /// 通过设置视口剖面盒，只处理路径周围的对象
    /// 
    /// 注意：Navisworks 的 ClipPlaneSet API 需要使用 JSON 字符串方式设置
    /// 直接修改 ClipPlaneSet 对象的属性在手工打开剖面功能后会导致 "Object is Read-Only" 错误
    /// </summary>
    public static class SectionClipHelper
    {
        // 默认边距（米）：路径周围保留的空间
        private const double DEFAULT_MARGIN_METERS = 2.0;
        
        // 默认高度范围（米）：上下各延伸的高度
        private const double DEFAULT_HEIGHT_MARGIN_METERS = 1.0;

        /// <summary>
        /// 根据路径点列表设置剖面盒
        /// </summary>
        /// <param name="pathPoints">路径点列表（模型单位）</param>
        /// <param name="marginInMeters">水平边距（米）</param>
        /// <param name="heightMarginInMeters">高度边距（米，上下各延伸）</param>
        /// <returns>是否成功设置</returns>
        public static bool SetClipBoxByPath(List<Point3D> pathPoints, 
            double marginInMeters = DEFAULT_MARGIN_METERS, 
            double heightMarginInMeters = DEFAULT_HEIGHT_MARGIN_METERS)
        {
            try
            {
                if (pathPoints == null || pathPoints.Count == 0)
                {
                    LogManager.Warning("[剖面盒] 路径点为空，无法设置剖面盒");
                    return false;
                }

                // 米转换为模型单位
                double metersToUnits = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
                double margin = marginInMeters * metersToUnits;
                double heightMargin = heightMarginInMeters * metersToUnits;

                // 计算路径的包围盒
                var pathBounds = CalculatePathBoundingBox(pathPoints);
                
                // 扩展边距（使用模型单位）
                var clipBox = ExpandBoundingBox(pathBounds, margin, heightMargin);
                
                // 应用到视口
                ApplyClipBox(clipBox);
                
                LogManager.Info($"[剖面盒] 已设置 - 水平边距: {marginInMeters}m, 高度边距: {heightMarginInMeters}m, " +
                    $"范围: X[{clipBox.Min.X:F2}, {clipBox.Max.X:F2}], Y[{clipBox.Min.Y:F2}, {clipBox.Max.Y:F2}], Z[{clipBox.Min.Z:F2}, {clipBox.Max.Z:F2}]");
                
                return true;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒] 设置失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 计算路径剖面盒包围盒（复用 EnableClipBoxForCurrentRoute 的路径盒语义：
        /// 路径点包围盒 + 水平/高度扩展，米→模型单位）。供 F1 剖面盒自动检测导出使用。
        /// </summary>
        /// <param name="pathPoints">路径点（模型单位，可含虚拟物体高度附加点）</param>
        /// <param name="marginInMeters">水平扩展（米）</param>
        /// <param name="heightMarginInMeters">高度扩展（米）</param>
        public static BoundingBox3D CalculatePathClipBox(
            List<Point3D> pathPoints,
            double marginInMeters = DEFAULT_MARGIN_METERS,
            double heightMarginInMeters = DEFAULT_HEIGHT_MARGIN_METERS)
        {
            double metersToUnits = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
            double margin = marginInMeters * metersToUnits;
            double heightMargin = heightMarginInMeters * metersToUnits;

            var pathBounds = CalculatePathBoundingBox(pathPoints);
            return ExpandBoundingBox(pathBounds, margin, heightMargin);
        }

        /// <summary>
        /// 根据单个点设置剖面盒（用于吊装路径等单点场景）
        /// </summary>
        /// <param name="centerPoint">中心点</param>
        /// <param name="rangeMeters">范围（米）</param>
        /// <param name="heightRangeMeters">高度范围（米）</param>
        /// <returns>是否成功设置</returns>
        public static bool SetClipBoxByPoint(Point3D centerPoint, 
            double rangeMeters = 10.0, 
            double heightRangeMeters = 5.0)
        {
            try
            {
                var min = new Point3D(
                    centerPoint.X - rangeMeters,
                    centerPoint.Y - rangeMeters,
                    centerPoint.Z - heightRangeMeters);
                var max = new Point3D(
                    centerPoint.X + rangeMeters,
                    centerPoint.Y + rangeMeters,
                    centerPoint.Z + heightRangeMeters);
                
                var clipBox = new BoundingBox3D(min, max);
                ApplyClipBox(clipBox);
                
                LogManager.Info($"[剖面盒] 已设置 - 中心: ({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2}), " +
                    $"范围: {rangeMeters}m x {rangeMeters}m x {heightRangeMeters}m");
                
                return true;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒] 设置失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 根据楼层设置剖面盒
        /// </summary>
        /// <param name="floorItem">楼层模型项</param>
        /// <param name="marginMeters">边距（米）</param>
        /// <returns>是否成功设置</returns>
        public static bool SetClipBoxByFloor(ModelItem floorItem, double marginMeters = 1.0)
        {
            try
            {
                if (floorItem == null)
                {
                    LogManager.Warning("[剖面盒] 楼层对象为空");
                    return false;
                }

                BoundingBox3D floorBounds = floorItem.BoundingBox();
                BoundingBox3D clipBox = ExpandBoundingBox(floorBounds, marginMeters, marginMeters);
                ApplyClipBox(clipBox);
                
                LogManager.Info($"[剖面盒] 已设置到楼层: {floorItem.DisplayName}");
                return true;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒] 设置失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 根据模型项集合设置剖面盒
        /// </summary>
        /// <param name="modelItems">模型项集合</param>
        /// <param name="marginInMeters">水平边距（米）</param>
        /// <param name="heightMarginInMeters">高度边距（米，上下各延伸）</param>
        /// <returns>是否成功设置</returns>
        public static bool SetClipBoxByModelItems(
            IEnumerable<ModelItem> modelItems,
            double marginInMeters = 2.0,
            double heightMarginInMeters = 2.0)
        {
            try
            {
                if (modelItems == null)
                {
                    LogManager.Warning("[剖面盒] 模型项集合为空");
                    return false;
                }

                var validItems = modelItems.Where(item => item != null).ToList();
                if (validItems.Count == 0)
                {
                    LogManager.Warning("[剖面盒] 没有可用的模型项，无法设置剖面盒");
                    return false;
                }

                bool hasBounds = false;
                double minX = double.MaxValue, minY = double.MaxValue, minZ = double.MaxValue;
                double maxX = double.MinValue, maxY = double.MinValue, maxZ = double.MinValue;

                foreach (var item in validItems)
                {
                    BoundingBox3D 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);
                    hasBounds = true;
                }

                if (!hasBounds)
                {
                    LogManager.Warning("[剖面盒] 未能从模型项中计算有效包围盒");
                    return false;
                }

                double metersToUnits = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
                double margin = marginInMeters * metersToUnits;
                double heightMargin = heightMarginInMeters * metersToUnits;

                var selectionBounds = new BoundingBox3D(
                    new Point3D(minX, minY, minZ),
                    new Point3D(maxX, maxY, maxZ));
                var clipBox = ExpandBoundingBox(selectionBounds, margin, heightMargin);

                ApplyClipBox(clipBox);

                LogManager.Info($"[剖面盒] 已按选择对象设置 - 对象数: {validItems.Count}, 水平边距: {marginInMeters}m, 高度边距: {heightMarginInMeters}m, " +
                    $"范围: X[{clipBox.Min.X:F2}, {clipBox.Max.X:F2}], Y[{clipBox.Min.Y:F2}, {clipBox.Max.Y:F2}], Z[{clipBox.Min.Z:F2}, {clipBox.Max.Z:F2}]");

                return true;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒] 按选择对象设置失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 清除剖面盒（显示全部模型）
        /// 使用 JSON 字符串方式设置 Enabled = false
        /// </summary>
        public static void ClearClipBox()
        {
            try
            {
                // 构建禁用剖面的 JSON
                string json = BuildClipPlaneSetJson(
                    new BoundingBox3D(new Point3D(0, 0, 0), new Point3D(0, 0, 0)), 
                    false);
                
                var view = Application.ActiveDocument.ActiveView;
                view.TrySetClippingPlanes(json);
                
                LogManager.Info("[剖面盒] 已清除");
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒] 清除失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 保存当前剖面盒的完整状态（包括 Box/Plane 模式）。
        /// 返回 JSON 字符串，可通过 RestoreClipState 恢复。
        /// </summary>
        public static string SaveClipState()
        {
            try
            {
                return Application.ActiveDocument.ActiveView.GetClippingPlanes();
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒] 保存状态失败: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// 恢复剖面盒的完整状态（包括 Box/Plane 模式）。
        /// 使用 SaveClipState 返回的 JSON 字符串。
        /// </summary>
        public static void RestoreClipState(string savedJson)
        {
            if (string.IsNullOrEmpty(savedJson))
                return;
            try
            {
                var view = Application.ActiveDocument.ActiveView;
                view.TrySetClippingPlanes(savedJson);
                LogManager.Info("[剖面盒] 已恢复保存的剖面状态");
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒] 恢复状态失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 重建剖面状态，同时确保剖面 UI 指示器显示。
        /// 解析保存的 JSON，自动识别 Box/Planes 模式，
        /// 用各自的方式重建剖面几何并刷新 UI 指示器。
        /// </summary>
        /// <param name="savedJson">由 SaveClipState 返回的 JSON</param>
        /// <returns>是否成功重建</returns>
        public static bool ReconstructClipStateWithIndicator(string savedJson)
        {
            if (string.IsNullOrEmpty(savedJson))
                return false;

            try
            {
                var jObj = JObject.Parse(savedJson);

                // === Box 模式：提取包围盒，用 ApplyClipBox 重建（已知能显示指示器） ===
                if (jObj["OrientedBox"] != null)
                {
                    LogManager.Debug($"[剖面盒] 重建: 检测到 Box 模式，OrientedBox 内容: {jObj["OrientedBox"].ToString()}");
                    var boxArr = jObj["OrientedBox"]?["Box"];
                    if (boxArr != null && boxArr.Count() >= 2)
                    {
                        Point3D min, max;
                        if (TryParseJArrayToPoint3D(boxArr[0], out min) &&
                            TryParseJArrayToPoint3D(boxArr[1], out max))
                        {
                            LogManager.Debug($"[剖面盒] 重建: 提取 Box min=({min.X}, {min.Y}, {min.Z}), max=({max.X}, {max.Y}, {max.Z})");
                            ApplyClipBox(new BoundingBox3D(min, max));
                            LogManager.Info("[剖面盒] 已通过 Box 模式重建剖面状态（含指示器）");
                            return true;
                        }
                        else
                        {
                            LogManager.Warning("[剖面盒] 重建: Box 数组解析失败");
                        }
                    }
                    else
                    {
                        LogManager.Warning("[剖面盒] 重建: OrientedBox 中缺少 Box 数组");
                    }
                }

                // === Planes 模式：先清除再恢复，确保 UI 指示器刷新 ===
                if (jObj["Planes"] != null)
                {
                    bool enabled = jObj["Enabled"]?.Value<bool>() ?? true;
                    double range = jObj["Range"]?.Value<double>() ?? 0.0;
                    bool linked = jObj["Linked"]?.Value<bool>() ?? false;

                    // 先发 Planes 模式 + Enabled:false（保持模式不变，仅禁用）
                    string planesDisabledJson = BuildClipPlaneSetPlanesJson(jObj["Planes"] as JArray, false, range, linked);
                    var view = Application.ActiveDocument.ActiveView;
                    view.TrySetClippingPlanes(planesDisabledJson);

                    // 再发 Planes 模式 + Enabled:true（仅切换 Enabled，模式没变）
                    string planesEnabledJson = BuildClipPlaneSetPlanesJson(jObj["Planes"] as JArray, enabled, range, linked);
                    view.TrySetClippingPlanes(planesEnabledJson);

                    LogManager.Info("[剖面盒] 已通过 Planes 模式重建剖面状态（含指示器）");
                    return true;
                }

                LogManager.Warning($"[剖面盒] 无法识别保存的剖面 JSON 结构。完整 JSON: {savedJson}");
                return false;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒] 重建剖面状态失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 从 JToken 解析 Point3D（数组格式 [x, y, z]），返回是否解析成功
        /// </summary>
        private static bool TryParseJArrayToPoint3D(JToken token, out Point3D point)
        {
            point = new Point3D();
            try
            {
                if (token is JArray arr && arr.Count >= 3)
                {
                    point = new Point3D(
                        arr[0].Value<double>(),
                        arr[1].Value<double>(),
                        arr[2].Value<double>());
                    return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 构建 Planes 模式的 ClipPlaneSet JSON，
        /// 保留原始 JSON 的所有字段（含 Linked），确保与 Navisworks 期望的格式一致。
        /// </summary>
        private static string BuildClipPlaneSetPlanesJson(JArray planesArray, bool enabled, double range, bool linked = false)
        {
            // 逐个提取平面数据
            var planeJsons = new List<string>();
            foreach (var planeToken in planesArray)
            {
                double? nx = null, ny = null, nz = null, distance = null;
                bool planeEnabled = true;

                // 支持 Normal 为数组格式 [x, y, z] 或对象格式 {X, Y, Z}
                var normal = planeToken["Normal"];
                if (normal is JArray normalArr && normalArr.Count >= 3)
                {
                    nx = normalArr[0].Value<double>();
                    ny = normalArr[1].Value<double>();
                    nz = normalArr[2].Value<double>();
                }
                else if (normal is JObject normalObj)
                {
                    nx = normalObj["X"]?.Value<double>();
                    ny = normalObj["Y"]?.Value<double>();
                    nz = normalObj["Z"]?.Value<double>();
                }

                distance = planeToken["Distance"]?.Value<double>();
                planeEnabled = planeToken["Enabled"]?.Value<bool>() ?? true;

                if (nx.HasValue && ny.HasValue && nz.HasValue && distance.HasValue)
                {
                    planeJsons.Add(string.Format(
                        "{{\"Type\":\"ClipPlane\",\"Version\":1," +
                        "\"Normal\":[{0},{1},{2}]," +
                        "\"Distance\":{3},\"Enabled\":{4}}}",
                        nx.Value.ToString("G17"),
                        ny.Value.ToString("G17"),
                        nz.Value.ToString("G17"),
                        distance.Value.ToString("G17"),
                        planeEnabled.ToString().ToLowerInvariant()));
                }
            }

            string planesArrayStr = string.Join(",", planeJsons);
            string rangeStr = range > 0 ? $",\"Range\":{range.ToString("G17")}" : "";
            string linkedStr = $",\"Linked\":{linked.ToString().ToLowerInvariant()}";

            return string.Format(
                "{{\"Type\":\"ClipPlaneSet\",\"Version\":1," +
                "\"Planes\":[{0}]{1}{2},\"Enabled\":{3}}}",
                planesArrayStr, rangeStr, linkedStr, enabled.ToString().ToLowerInvariant());
        }

        /// <summary>
        /// 检查剖面盒是否已启用
        /// </summary>
        public static bool IsClipBoxEnabled
        {
            get
            {
                try
                {
                    return Application.ActiveDocument.CurrentViewpoint.Value.ClipPlanes.Enabled;
                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// 恢复剖面盒（用于被 Navisworks ExportToNwd 内部关闭后重新启用）
        /// </summary>
        public static void RestoreClipBox(BoundingBox3D box)
        {
            ApplyClipBox(box);
        }

        /// <summary>
        /// 获取当前剖面盒
        /// </summary>
        /// <param name="clipBox">输出剖面盒</param>
        /// <returns>是否成功获取</returns>
        public static bool TryGetCurrentClipBox(out BoundingBox3D clipBox)
        {
            clipBox = new BoundingBox3D();
            try
            {
                var clipPlanes = Application.ActiveDocument.CurrentViewpoint.Value.ClipPlanes;
                if (!clipPlanes.Enabled || clipPlanes.Mode != ClipPlaneSetMode.Box)
                    return false;
                
                clipBox = clipPlanes.Box;
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 测试点是否在剖面盒内
        /// </summary>
        public static bool IsPointInClipBox(Point3D point)
        {
            if (!TryGetCurrentClipBox(out var clipBox)) return true; // 无剖面盒时默认全部包含
            
            return clipBox.Contains(point);
        }

        /// <summary>
        /// 测试包围盒是否与剖面盒相交
        /// </summary>
        public static bool IntersectsClipBox(BoundingBox3D box)
        {
            if (!TryGetCurrentClipBox(out var clipBox)) return true; // 无剖面盒时默认全部相交
            
            return clipBox.Intersects(box);
        }

        /// <summary>
        /// 统计剖面盒内/外的对象数量（用于测试）
        /// </summary>
        /// <param name="totalCount">总对象数</param>
        /// <param name="insideCount">在剖面盒内的对象数</param>
        /// <param name="outsideCount">在剖面盒外的对象数</param>
        public static void CountObjectsInClipBox(out int totalCount, out int insideCount, out int outsideCount)
        {
            totalCount = 0;
            insideCount = 0;
            outsideCount = 0;

            try
            {
                if (!TryGetCurrentClipBox(out var clipBox))
                {
                    LogManager.Warning("[剖面盒统计] 剖面盒未启用");
                    return;
                }

                var stack = new Stack<ModelItem>();
                foreach (var model in Application.ActiveDocument.Models)
                {
                    stack.Push(model.RootItem);
                }

                while (stack.Count > 0)
                {
                    var item = stack.Pop();
                    totalCount++;

                    // 测试包围盒是否相交
                    BoundingBox3D itemBox = item.BoundingBox();
                    if (clipBox.Intersects(itemBox))
                    {
                        insideCount++;
                    }
                    else
                    {
                        outsideCount++;
                        continue; // 跳过子节点（剪枝）
                    }

                    // 继续遍历子节点
                    foreach (var child in item.Children)
                    {
                        stack.Push(child);
                    }
                }

                LogManager.Info($"[剖面盒统计] 总数: {totalCount}, 盒内: {insideCount}, 盒外: {outsideCount}, " +
                    $"过滤率: {(outsideCount * 100.0 / totalCount):F1}%");
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒统计] 失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 使用采样点（8个角点）检测对象是否真正与剖面盒相交
        /// 用于排除大包围盒但实际几何体远离剖面盒的对象（如地基）
        /// </summary>
        public static bool IntersectsByCornerPoints(BoundingBox3D itemBox, BoundingBox3D clipBox)
        {
            // 获取对象包围盒的8个角点
            var corners = new Point3D[]
            {
                new Point3D(itemBox.Min.X, itemBox.Min.Y, itemBox.Min.Z),
                new Point3D(itemBox.Min.X, itemBox.Min.Y, itemBox.Max.Z),
                new Point3D(itemBox.Min.X, itemBox.Max.Y, itemBox.Min.Z),
                new Point3D(itemBox.Min.X, itemBox.Max.Y, itemBox.Max.Z),
                new Point3D(itemBox.Max.X, itemBox.Min.Y, itemBox.Min.Z),
                new Point3D(itemBox.Max.X, itemBox.Min.Y, itemBox.Max.Z),
                new Point3D(itemBox.Max.X, itemBox.Max.Y, itemBox.Min.Z),
                new Point3D(itemBox.Max.X, itemBox.Max.Y, itemBox.Max.Z)
            };

            // 检查是否有任何角点在剖面盒内
            foreach (var corner in corners)
            {
                if (clipBox.Contains(corner))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 使用棱上自适应采样检测对象是否与剖面盒相交
        /// 根据物体大小和剖面盒尺寸动态计算采样点数，确保长物体（如管道）不会漏检
        /// 采样间距小于剖面盒最小边长，确保任何穿过剖面盒的物体都能被检测到
        /// </summary>
        /// <param name="itemBox">对象包围盒</param>
        /// <param name="clipBox">剖面盒</param>
        /// <param name="minSamplesPerEdge">每条棱的最小采样点数（默认3个）</param>
        /// <returns>是否有采样点在剖面盒内</returns>
        public static bool IntersectsByEdgeSampling(BoundingBox3D itemBox, BoundingBox3D clipBox, int minSamplesPerEdge = 3)
        {
            // 计算剖面盒的最小边长（作为采样间距上限）
            double clipBoxMinSize = Math.Min(
                Math.Min(clipBox.Max.X - clipBox.Min.X, clipBox.Max.Y - clipBox.Min.Y),
                clipBox.Max.Z - clipBox.Min.Z
            );
            
            // 计算物体包围盒的三边长度
            double itemSizeX = itemBox.Max.X - itemBox.Min.X;
            double itemSizeY = itemBox.Max.Y - itemBox.Min.Y;
            double itemSizeZ = itemBox.Max.Z - itemBox.Min.Z;
            
            // 根据边长动态计算每条棱的采样点数
            // 采样间距 = 边长 / (采样点数 - 1)，确保间距 < 剖面盒最小边长
            int GetSampleCount(double edgeLength)
            {
                if (edgeLength <= 0) return minSamplesPerEdge;
                // 需要的采样点数 = 边长 / 剖面盒尺寸 + 1，至少minSamplesPerEdge个
                int count = (int)Math.Ceiling(edgeLength / clipBoxMinSize) + 1;
                return Math.Max(count, minSamplesPerEdge);
            }
            
            int samplesX = GetSampleCount(itemSizeX);
            int samplesY = GetSampleCount(itemSizeY);
            int samplesZ = GetSampleCount(itemSizeZ);
            
            // 12条棱的端点定义（索引对应8个角点）和采样点数
            // 0: (0,0,0)  1: (0,0,1)  2: (0,1,0)  3: (0,1,1)
            // 4: (1,0,0)  5: (1,0,1)  6: (1,1,0)  7: (1,1,1)
            var edges = new (int start, int end, int samples)[]
            {
                // X方向棱（4条），用samplesX
                (0, 4, samplesX), (1, 5, samplesX), (2, 6, samplesX), (3, 7, samplesX),
                // Y方向棱（4条），用samplesY
                (0, 2, samplesY), (1, 3, samplesY), (4, 6, samplesY), (5, 7, samplesY),
                // Z方向棱（4条），用samplesZ
                (0, 1, samplesZ), (2, 3, samplesZ), (4, 5, samplesZ), (6, 7, samplesZ)
            };
            
            // 8个角点的坐标
            var cornerPoints = new Point3D[]
            {
                new Point3D(itemBox.Min.X, itemBox.Min.Y, itemBox.Min.Z),
                new Point3D(itemBox.Min.X, itemBox.Min.Y, itemBox.Max.Z),
                new Point3D(itemBox.Min.X, itemBox.Max.Y, itemBox.Min.Z),
                new Point3D(itemBox.Min.X, itemBox.Max.Y, itemBox.Max.Z),
                new Point3D(itemBox.Max.X, itemBox.Min.Y, itemBox.Min.Z),
                new Point3D(itemBox.Max.X, itemBox.Min.Y, itemBox.Max.Z),
                new Point3D(itemBox.Max.X, itemBox.Max.Y, itemBox.Min.Z),
                new Point3D(itemBox.Max.X, itemBox.Max.Y, itemBox.Max.Z)
            };
            
            // 在每条棱上采样
            foreach (var edge in edges)
            {
                var start = cornerPoints[edge.start];
                var end = cornerPoints[edge.end];
                int samples = edge.samples;
                
                for (int i = 0; i < samples; i++)
                {
                    double t = (double)i / (samples - 1); // 0.0 到 1.0
                    var samplePoint = new Point3D(
                        start.X + t * (end.X - start.X),
                        start.Y + t * (end.Y - start.Y),
                        start.Z + t * (end.Z - start.Z)
                    );
                    
                    if (clipBox.Contains(samplePoint))
                    {
                        return true;
                    }
                }
            }
            
            return false;
        }

        /// <summary>
        /// 统计对象数量（使用采样点检测）
        /// 与 CountObjectsInClipBox 的区别：使用8个角点检测，排除大包围盒假阳性
        /// </summary>
        public static void CountObjectsInClipBoxByCorners(out int totalCount, out int insideCount, out int outsideCount, out int largeBoxFiltered, List<string> debugDetails = null)
        {
            totalCount = 0;
            insideCount = 0;
            outsideCount = 0;
            largeBoxFiltered = 0;

            try
            {
                if (!TryGetCurrentClipBox(out var clipBox))
                {
                    LogManager.Warning("[剖面盒角点统计] 剖面盒未启用");
                    return;
                }

                var stack = new Stack<ModelItem>();
                foreach (var model in Application.ActiveDocument.Models)
                {
                    stack.Push(model.RootItem);
                }

                while (stack.Count > 0)
                {
                    var item = stack.Pop();
                    totalCount++;

                    BoundingBox3D itemBox = item.BoundingBox();
                    
                    // 先检查包围盒是否相交（快速排除）
                    if (!clipBox.Intersects(itemBox))
                    {
                        outsideCount++;
                        continue; // 跳过子节点（剪枝）
                    }

                    // 相交：再用角点检测确认
                    bool hasCornerInside = IntersectsByCornerPoints(itemBox, clipBox);
                    
                    if (hasCornerInside)
                    {
                        insideCount++;
                    }
                    else
                    {
                        outsideCount++;
                        largeBoxFiltered++;
                        
                        // 记录被过滤的大对象
                        if (item.HasGeometry)
                        {
                            LogManager.Debug($"[剖面盒角点过滤] 大包围盒但无角点在盒内: {item.DisplayName}, 包围盒: X[{itemBox.Min.X:F2},{itemBox.Max.X:F2}], Y[{itemBox.Min.Y:F2},{itemBox.Max.Y:F2}], Z[{itemBox.Min.Z:F2},{itemBox.Max.Z:F2}]");
                        }
                        
                        // 注意：这里继续遍历子节点，因为父对象可能只是外壳，子对象可能在盒内
                    }

                    // 继续遍历子节点
                    foreach (var child in item.Children)
                    {
                        stack.Push(child);
                    }
                }

                LogManager.Info($"[剖面盒角点统计] 总数: {totalCount}, 盒内(角点): {insideCount}, 盒外: {outsideCount}, 大包围盒过滤: {largeBoxFiltered}, " +
                    $"过滤率: {(outsideCount * 100.0 / totalCount):F1}%");
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒角点统计] 失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 测试包围盒相交检测（用于验证）
        /// </summary>
        public static void TestIntersection()
        {
            try
            {
                if (!TryGetCurrentClipBox(out var clipBox))
                {
                    LogManager.Warning("[剖面盒测试] 请先设置剖面盒");
                    return;
                }

                // 测试点
                var center = clipBox.Center;
                var testPointInside = new Point3D(center.X, center.Y, center.Z);
                var testPointOutside = new Point3D(
                    center.X + 1000, 
                    center.Y + 1000, 
                    center.Z + 1000);

                bool inside1 = IsPointInClipBox(testPointInside);
                bool inside2 = IsPointInClipBox(testPointOutside);

                LogManager.Info($"[剖面盒测试] 中心点: {inside1} (应为 True)");
                LogManager.Info($"[剖面盒测试] 远点: {inside2} (应为 False)");

                // 测试包围盒
                var boxInside = new BoundingBox3D(
                    new Point3D(center.X - 1, center.Y - 1, center.Z - 1),
                    new Point3D(center.X + 1, center.Y + 1, center.Z + 1));
                var boxOutside = new BoundingBox3D(
                    new Point3D(center.X + 100, center.Y + 100, center.Z + 100),
                    new Point3D(center.X + 200, center.Y + 200, center.Z + 200));

                bool intersect1 = IntersectsClipBox(boxInside);
                bool intersect2 = IntersectsClipBox(boxOutside);

                LogManager.Info($"[剖面盒测试] 内部包围盒相交: {intersect1} (应为 True)");
                LogManager.Info($"[剖面盒测试] 外部包围盒相交: {intersect2} (应为 False)");
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒测试] 失败: {ex.Message}");
            }
        }

        #region 私有方法

        /// <summary>
        /// 计算路径点的包围盒
        /// </summary>
        private static BoundingBox3D CalculatePathBoundingBox(List<Point3D> points)
        {
            if (points.Count == 1)
            {
                return new BoundingBox3D(points[0], points[0]);
            }

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

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

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

        /// <summary>
        /// 扩展包围盒
        /// </summary>
        private static BoundingBox3D ExpandBoundingBox(BoundingBox3D box, 
            double horizontalMargin, double verticalMargin)
        {
            return ExpandBoundingBox(box, horizontalMargin, verticalMargin, verticalMargin);
        }

        /// <summary>
        /// 扩展包围盒（支持分别设置上下边距）
        /// </summary>
        private static BoundingBox3D ExpandBoundingBox(BoundingBox3D box, 
            double horizontalMargin, double bottomMargin, double topMargin)
        {
            var min = new Point3D(
                box.Min.X - horizontalMargin,
                box.Min.Y - horizontalMargin,
                box.Min.Z - bottomMargin);
            var max = new Point3D(
                box.Max.X + horizontalMargin,
                box.Max.Y + horizontalMargin,
                box.Max.Z + topMargin);
            
            return new BoundingBox3D(min, max);
        }

        /// <summary>
        /// 根据路径点列表设置剖面盒（支持分别设置上下边距）
        /// </summary>
        /// <param name="pathPoints">路径点列表（模型单位）</param>
        /// <param name="marginInMeters">水平边距（米）</param>
        /// <param name="heightMarginBottomInMeters">底部高度边距（米）</param>
        /// <param name="heightMarginTopInMeters">顶部高度边距（米）</param>
        public static bool SetClipBoxByPathWithMargins(List<Point3D> pathPoints, 
            double marginInMeters = DEFAULT_MARGIN_METERS, 
            double heightMarginBottomInMeters = DEFAULT_HEIGHT_MARGIN_METERS,
            double heightMarginTopInMeters = DEFAULT_HEIGHT_MARGIN_METERS)
        {
            try
            {
                if (pathPoints == null || pathPoints.Count == 0)
                {
                    LogManager.Warning("[剖面盒] 路径点为空，无法设置剖面盒");
                    return false;
                }

                // 米转换为模型单位
                double metersToUnits = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
                double margin = marginInMeters * metersToUnits;
                double bottomMargin = heightMarginBottomInMeters * metersToUnits;
                double topMargin = heightMarginTopInMeters * metersToUnits;

                // 计算路径的包围盒
                var pathBounds = CalculatePathBoundingBox(pathPoints);
                
                // 扩展边距（分别设置上下，使用模型单位）
                var clipBox = ExpandBoundingBox(pathBounds, margin, bottomMargin, topMargin);
                
                // 应用到视口
                ApplyClipBox(clipBox);
                
                LogManager.Info($"[剖面盒] 已设置 - 水平边距: {marginInMeters}m, 底部边距: {heightMarginBottomInMeters}m, 顶部边距: {heightMarginTopInMeters}m, " +
                    $"范围: X[{clipBox.Min.X:F2}, {clipBox.Max.X:F2}], Y[{clipBox.Min.Y:F2}, {clipBox.Max.Y:F2}], Z[{clipBox.Min.Z:F2}, {clipBox.Max.Z:F2}]");
                
                return true;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[剖面盒] 设置失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 应用剖面盒到视口
        /// 使用 JSON 字符串方式设置，避免 "Object is Read-Only" 错误
        /// </summary>
        private static void ApplyClipBox(BoundingBox3D box)
        {
            // 构建 JSON 格式的 ClipPlaneSet
            string json = BuildClipPlaneSetJson(box, true);
            ApplyClipJson(json);
        }

        /// <summary>
        /// 向视口应用剪裁 JSON（通用方法，Box 和 Planes 共用）
        /// </summary>
        private static void ApplyClipJson(string json)
        {
            var view = Application.ActiveDocument.ActiveView;

            // TrySetClippingPlanes 返回 bool 表示是否成功，不会抛出异常
            bool success = view.TrySetClippingPlanes(json);

            if (!success)
            {
                // 如果 Try 方法失败，尝试使用 SetClippingPlanes（会抛出异常）
                view.SetClippingPlanes(json);
            }
        }

        /// <summary>
        /// 构建 ClipPlaneSet 的 JSON 字符串
        /// </summary>
        /// <param name="box">包围盒</param>
        /// <param name="enabled">是否启用</param>
        /// <returns>JSON 字符串</returns>
        private static string BuildClipPlaneSetJson(BoundingBox3D box, bool enabled)
        {
            // Navisworks ClipPlaneSet JSON 格式：
            // {
            //   "Type": "ClipPlaneSet",
            //   "Version": 1,
            //   "OrientedBox": {
            //     "Type": "OrientedBox3D",
            //     "Version": 1,
            //     "Box": [[minX, minY, minZ], [maxX, maxY, maxZ]],
            //     "Rotation": [0, 0, 0]
            //   },
            //   "Enabled": true/false
            // }
            
            return string.Format(
                "{{\"Type\":\"ClipPlaneSet\",\"Version\":1," +
                "\"OrientedBox\":{{\"Type\":\"OrientedBox3D\",\"Version\":1," +
                "\"Box\":[[{0},{1},{2}],[{3},{4},{5}]]," +
                "\"Rotation\":[0,0,0]}},\"Enabled\":{6}}}",
                box.Min.X.ToString("G17"),
                box.Min.Y.ToString("G17"),
                box.Min.Z.ToString("G17"),
                box.Max.X.ToString("G17"),
                box.Max.Y.ToString("G17"),
                box.Max.Z.ToString("G17"),
                enabled.ToString().ToLowerInvariant());
        }

        #endregion
    }
}
