using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils;
using NavisApplication = Autodesk.Navisworks.Api.Application;

namespace NavisworksTransport.Core
{
    /// <summary>
    /// 剖面盒导出器 - 导出剖面盒内的对象包围盒信息到JSON
    /// </summary>
    public class SectionBoxExporter
    {
        private SectionBoxTraversalResult _lastTraversal;

        /// <summary>
        /// 导出结果
        /// </summary>
        public class ExportSectionBoxResult
        {
            public bool Success { get; set; }
            public string FilePath { get; set; }
            public int ObjectCount { get; set; }
            public int HiddenNodeCount { get; set; }
            public long FileSize { get; set; }
            public string ErrorMessage { get; set; }
        }

        /// <summary>
        /// 验证剖面盒并遍历模型树（不含导出，适合后台线程执行）
        /// </summary>
        public ExportSectionBoxResult ValidateAndTraverse(Document document)
        {
            var result = new ExportSectionBoxResult();

            if (document == null)
            {
                result.ErrorMessage = "没有活动的文档！";
                return result;
            }

            LogManager.Debug($"[剖面盒导出] 开始验证剖面盒，Models={document.Models.Count}");

            var viewpoint = document.CurrentViewpoint.Value;
            var clipPlanes = viewpoint.ClipPlanes;

            if (clipPlanes == null || !clipPlanes.Enabled)
            {
                result.ErrorMessage = "当前没有活动的剖面盒！";
                return result;
            }

            if (clipPlanes.Mode != ClipPlaneSetMode.Box)
            {
                result.ErrorMessage = "请切换为剖面盒模式（Box 模式）。";
                return result;
            }

            if (!GetSectionBoxBounds(clipPlanes, out BoundingBox3D bounds))
            {
                result.ErrorMessage = "无法获取剖面盒边界。";
                return result;
            }

            LogManager.Debug("[剖面盒导出] 开始遍历模型树...");
            var sw = System.Diagnostics.Stopwatch.StartNew();
            var traversal = GetObjectsAndHiddenItems(document, bounds);
            sw.Stop();
            _lastTraversal = traversal;
            result.ObjectCount = traversal.ObjectsInBox.Count;
            result.HiddenNodeCount = traversal.ItemsToHide.Count;

            LogManager.Debug($"[剖面盒导出] 遍历完成: 耗时={sw.Elapsed.TotalSeconds:F1}s, 盒内对象={result.ObjectCount}, 隐藏节点={result.HiddenNodeCount}");

            if (traversal.ObjectsInBox.Count == 0)
            {
                result.ErrorMessage = "剖面盒内没有找到对象！";
                return result;
            }

            result.Success = true;
            return result;
        }

        /// <summary>
        /// 导出遍历结果到 NWD（必须在 UI 线程调用）
        /// </summary>
        public ExportSectionBoxResult ExportTraversalToNwd(Document document, string filePath)
        {
            var result = new ExportSectionBoxResult();

            if (_lastTraversal == null || _lastTraversal.ObjectsInBox.Count == 0)
            {
                result.ErrorMessage = "没有可导出的遍历结果，请先调用 ValidateAndTraverse。";
                return result;
            }

            result.ObjectCount = _lastTraversal.ObjectsInBox.Count;
            result.HiddenNodeCount = _lastTraversal.ItemsToHide.Count;

            LogManager.Debug($"[剖面盒导出] 开始导出: 对象={result.ObjectCount}, 隐藏={result.HiddenNodeCount}, 路径={filePath}");
            var sw = System.Diagnostics.Stopwatch.StartNew();

            LogManager.Info($"[SectionBoxExporter] 剖面盒内找到 {result.ObjectCount} 个对象，可隐藏 {result.HiddenNodeCount} 个节点");

            try
            {
                var exportedPath = NwdExportHelper.ExportToNwdWithPrecomputedHiddenItems(
                    _lastTraversal.ObjectsInBox,
                    _lastTraversal.ItemsToHide,
                    filePath,
                    "剖面盒导出");

                if (string.IsNullOrEmpty(exportedPath) || !File.Exists(exportedPath))
                {
                    result.ErrorMessage = "导出失败：未生成文件。";
                    return result;
                }

                result.Success = true;
                result.FilePath = exportedPath;
                result.FileSize = new FileInfo(exportedPath).Length;
                sw.Stop();
                LogManager.Debug($"[剖面盒导出] 完成: 耗时={sw.Elapsed.TotalSeconds:F1}s, 文件大小={result.FileSize / 1024} KB");
            }
            catch (Exception ex)
            {
                result.ErrorMessage = $"导出失败：{ex.Message}";
            }

            return result;
        }

        /// <summary>
        /// 从当前视点导出剖面盒到 NWD（完整业务流程：验证→遍历→导出）
        /// </summary>
        /// <param name="document">Navisworks文档</param>
        /// <param name="filePath">目标文件路径</param>
        /// <returns>导出结果</returns>
        public ExportSectionBoxResult ExportSectionBoxFromActiveView(Document document, string filePath)
        {
            var result = new ExportSectionBoxResult();

            if (document == null)
            {
                result.ErrorMessage = "没有活动的文档！请先打开一个模型。";
                return result;
            }

            // 验证剖面盒
            var viewpoint = document.CurrentViewpoint.Value;
            var clipPlanes = viewpoint.ClipPlanes;

            if (clipPlanes == null || !clipPlanes.Enabled)
            {
                result.ErrorMessage = "当前没有活动的剖面盒！\n请先使用 Navisworks 的剖面工具创建剖面盒。";
                return result;
            }

            if (clipPlanes.Mode != ClipPlaneSetMode.Box)
            {
                result.ErrorMessage = "当前是平面裁剪模式，请切换为剖面盒模式！\n在 Navisworks 剖面工具中选择'Box'模式。";
                return result;
            }

            // 提取边界
            if (!GetSectionBoxBounds(clipPlanes, out BoundingBox3D sectionBoxBounds))
            {
                result.ErrorMessage = "无法获取剖面盒边界！\n请确保剖面盒已正确设置。";
                return result;
            }

            // 遍历模型树
            var traversalResult = GetObjectsAndHiddenItems(document, sectionBoxBounds);
            result.ObjectCount = traversalResult.ObjectsInBox.Count;
            result.HiddenNodeCount = traversalResult.ItemsToHide.Count;

            if (traversalResult.ObjectsInBox.Count == 0)
            {
                result.ErrorMessage = "剖面盒内没有找到对象！";
                return result;
            }

            LogManager.Info($"[SectionBoxExporter] 剖面盒内找到 {result.ObjectCount} 个对象，可隐藏 {result.HiddenNodeCount} 个节点");

            // 导出
            try
            {
                var exportedPath = NwdExportHelper.ExportToNwdWithPrecomputedHiddenItems(
                    traversalResult.ObjectsInBox,
                    traversalResult.ItemsToHide,
                    filePath,
                    "剖面盒导出");

                if (string.IsNullOrEmpty(exportedPath) || !File.Exists(exportedPath))
                {
                    result.ErrorMessage = "导出失败：未生成文件。";
                    return result;
                }

                result.Success = true;
                result.FilePath = exportedPath;
                result.FileSize = new FileInfo(exportedPath).Length;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[SectionBoxExporter] 导出失败: {ex.Message}");
                result.ErrorMessage = $"导出失败：{ex.Message}";
            }

            return result;
        }

        /// <summary>
        /// 获取剖面盒的包围盒
        /// </summary>
        private bool GetSectionBoxBounds(ClipPlaneSet clipPlanes, out BoundingBox3D result)
        {
            result = default(BoundingBox3D);
            
            try
            {
                // 直接获取 Box 属性（长方体模式）
                var box = clipPlanes.Box;
                if (box == null)
                {
                    LogManager.Warning("ClipPlanes.Box 为 null");
                    return false;
                }

                // 转换为 BoundingBox3D
                result = new BoundingBox3D(
                    new Point3D(box.Min.X, box.Min.Y, box.Min.Z),
                    new Point3D(box.Max.X, box.Max.Y, box.Max.Z)
                );
                return true;
            }
            catch (Exception ex)
            {
                LogManager.Warning($"获取剖面盒边界失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 剖面盒遍历结果：同时包含盒内对象和需要隐藏的节点
        /// </summary>
        public class SectionBoxTraversalResult
        {
            /// <summary>
            /// 剖面盒内的对象
            /// </summary>
            public List<ModelItem> ObjectsInBox { get; set; } = new List<ModelItem>();
            
            /// <summary>
            /// 需要隐藏的节点（子树完全不在盒内的根节点）
            /// </summary>
            public ModelItemCollection ItemsToHide { get; set; } = new ModelItemCollection();
        }

        /// <summary>
        /// 【优化】一次遍历同时获取剖面盒内对象和可见节点集合
        /// 然后使用原来的兄弟节点算法计算隐藏节点
        /// </summary>
        public SectionBoxTraversalResult GetObjectsAndHiddenItems(Document document, BoundingBox3D sectionBox)
        {
            var result = new SectionBoxTraversalResult();
            var visibleSet = new HashSet<ModelItem>(); // 收集需要保留的节点（对象+祖先）
            
            // 第一遍：遍历整个模型树，收集盒内对象和可见节点
            foreach (var model in document.Models)
            {
                var rootItem = model.RootItem;
                if (rootItem != null)
                {
                    TraverseAndCollect(model.RootItem, sectionBox, result.ObjectsInBox, visibleSet);
                }
            }

            // 第二遍（局部）：使用原来的兄弟节点算法计算隐藏节点
            // 只遍历 uniqueParents 的子节点，不是整个模型
            CalculateHiddenItems(visibleSet, result.ItemsToHide);

            // 处理空白顶级子树：模型树无统一根节点时，完全无命中的子树不会被
            // CalculateHiddenItems 覆盖，需显式隐藏其 RootItem
            foreach (var model in document.Models)
            {
                if (model.RootItem == null) continue;
                if (!visibleSet.Contains(model.RootItem))
                    result.ItemsToHide.Add(model.RootItem);
            }

            return result;
        }

        /// <summary>
        /// 递归遍历收集盒内对象和可见节点
        /// </summary>
        private void TraverseAndCollect(ModelItem item, BoundingBox3D sectionBox, 
            List<ModelItem> objectsInBox, HashSet<ModelItem> visibleSet)
        {
            if (item == null || item.IsHidden) return;

            // 如果有几何体，检查是否在盒内
            if (item.HasGeometry)
            {
                var bbox = item.BoundingBox();
                
                // 检查是否有体积（三个方向尺寸都大于0）
                double sizeX = bbox.Max.X - bbox.Min.X;
                double sizeY = bbox.Max.Y - bbox.Min.Y;
                double sizeZ = bbox.Max.Z - bbox.Min.Z;
                bool hasVolume = sizeX > 0.0001 && sizeY > 0.0001 && sizeZ > 0.0001;
                
                // 有体积且与剖面盒相交 → 是目标对象
                if (hasVolume && BoundingBoxesIntersect(bbox, sectionBox))
                {
                    objectsInBox.Add(item);
                    
                    // 将该对象及其所有祖先标记为可见
                    foreach (var ancestor in item.AncestorsAndSelf)
                    {
                        visibleSet.Add(ancestor);
                    }
                }
                
                // 找到几何体，停止向下遍历（原逻辑）
                return;
            }

            // 没有几何体，继续遍历子节点
            foreach (var child in item.Children)
            {
                TraverseAndCollect(child, sectionBox, objectsInBox, visibleSet);
            }
        }

        /// <summary>
        /// 【使用原来的兄弟节点算法】计算需要隐藏的节点
        /// </summary>
        private void CalculateHiddenItems(HashSet<ModelItem> visibleSet, ModelItemCollection itemsToHide)
        {
            // 1. 收集唯一的父节点
            var uniqueParents = new HashSet<ModelItem>();
            foreach (var visibleItem in visibleSet)
            {
                if (visibleItem.Parent != null)
                {
                    uniqueParents.Add(visibleItem.Parent);
                }
            }

            // 2. 收集这些父节点的所有子节点（兄弟节点）
            var hiddenSet = new HashSet<ModelItem>();
            foreach (var parent in uniqueParents)
            {
                foreach (var sibling in parent.Children)
                {
                    hiddenSet.Add(sibling);
                }
            }

            // 3. 从隐藏集合中移除可见项
            hiddenSet.ExceptWith(visibleSet);

            // 4. 转换为 ModelItemCollection
            foreach (var item in hiddenSet)
            {
                itemsToHide.Add(item);
            }
        }

        /// <summary>
        /// 获取剖面盒内的所有对象（公开方法供外部调用）
        /// 【已优化】内部使用 GetObjectsAndHiddenItems 实现一次遍历
        /// </summary>
        public List<ModelItem> GetObjectsInSectionBox(Document document, BoundingBox3D sectionBox)
        {
            var result = GetObjectsAndHiddenItems(document, sectionBox);
            return result.ObjectsInBox;
        }

        /// <summary>
        /// 检查两个包围盒是否相交
        /// </summary>
        private bool BoundingBoxesIntersect(BoundingBox3D a, BoundingBox3D b)
        {
            return (a.Min.X <= b.Max.X && a.Max.X >= b.Min.X) &&
                   (a.Min.Y <= b.Max.Y && a.Max.Y >= b.Min.Y) &&
                   (a.Min.Z <= b.Max.Z && a.Max.Z >= b.Min.Z);
        }

        /// <summary>
        /// 导出为NWD文件（委托给 NwdExportHelper）
        /// 适用于通用场景（如保存选择项）
        /// </summary>
        /// <param name="document">Navisworks文档</param>
        /// <param name="objects">要导出的对象列表</param>
        /// <param name="filePath">导出文件路径</param>
        /// <returns>导出文件路径，失败返回null</returns>
        public string ExportToNwd(Document document, List<ModelItem> objects, string filePath)
        {
            return NwdExportHelper.ExportToNwd(objects, filePath);
        }

        /// <summary>
        /// 【优化】导出为NWD文件，使用预计算的遍历结果
        /// 适用于剖面盒导出场景，避免重复遍历模型树
        /// </summary>
        /// <param name="document">Navisworks文档</param>
        /// <param name="traversalResult">预计算的遍历结果（包含对象和隐藏节点）</param>
        /// <param name="filePath">导出文件路径</param>
        /// <returns>导出文件路径，失败返回null</returns>
        public string ExportToNwd(Document document, SectionBoxTraversalResult traversalResult, string filePath)
        {
            return NwdExportHelper.ExportToNwdWithPrecomputedHiddenItems(
                traversalResult.ObjectsInBox,
                traversalResult.ItemsToHide,
                filePath,
                "剖面盒导出");
        }

    }
}
