using System;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;
using NavisworksTransport.Utils;

namespace NavisworksTransport
{
    /// <summary>
    /// 快速取面工具。单点+像素邻域法推断点击位置所在面的法向量。
    /// 与 PathClickToolPlugin 并列，互斥激活。
    /// </summary>
    [Plugin("FaceInferTool", "NavisworksTransport")]
    public class FaceInferToolPlugin : ToolPlugin
    {
        /// <summary>像素偏移递进序列（共线时自动退到下一级）</summary>
        private static readonly int[] PixelOffsets = { 2, 5, 10 };

        /// <summary>最小叉积长度阈值（低于此值视为三点共线）</summary>
        private const double MinCrossLength = 0.001;

        /// <summary>
        /// 面推断结果事件。命中物体时传递 FaceInferResult，未命中时传递 Invalid。
        /// </summary>
        public static event EventHandler<FaceInferResult> FaceInferred;

        /// <summary>
        /// 获取插件的程序集路径（用于动态加载）
        /// </summary>
        public static string AssemblyPath =>
            System.Reflection.Assembly.GetExecutingAssembly().Location;

        /// <summary>
        /// 允许外部代码手动触发 FaceInferred 事件
        /// </summary>
        public static void TriggerFaceInferred(object sender, FaceInferResult result)
        {
            FaceInferred?.Invoke(sender, result);
        }

        /// <summary>
        /// 插件构造函数
        /// </summary>
        public FaceInferToolPlugin()
        {
            LogManager.Debug("[FaceInferTool] FaceInferToolPlugin 实例已创建");
        }

        /// <summary>
        /// 激活取面工具，准备接收用户点击。
        /// </summary>
        public static void Activate()
        {
            var pluginRecord = Autodesk.Navisworks.Api.Application.Plugins.FindPlugin(
                "FaceInferTool.NavisworksTransport");
            if (pluginRecord != null)
            {
                var toolPlugin = pluginRecord.LoadPlugin() as ToolPlugin;
                if (toolPlugin != null)
                    Autodesk.Navisworks.Api.Application.MainDocument.Tool.SetCustomToolPlugin(toolPlugin);
            }
        }

        /// <summary>
        /// 鼠标按下事件 — 单次点击内做三次 PickItemFromPoint 推断面法向量
        /// </summary>
        public override bool MouseDown(View view, KeyModifiers modifiers, ushort button, int x, int y, double timeOffset)
        {
            if (button != 1) return false; // 只响应左键

            LogManager.Debug("[FaceInferTool] ===== 鼠标点击 =====");
            LogManager.Debug($"[FaceInferTool] 屏幕坐标: ({x}, {y})");

            var result = InferFace(view, x, y);

            if (result.IsValid)
            {
                LogManager.Debug($"[FaceInferTool] ✓ 面推断成功");
                LogManager.Debug($"[FaceInferTool]   命中点: ({result.HitPoint.X:F3}, {result.HitPoint.Y:F3}, {result.HitPoint.Z:F3})");
                LogManager.Debug($"[FaceInferTool]   法向量: ({result.Normal.X:F3}, {result.Normal.Y:F3}, {result.Normal.Z:F3})");
                LogManager.Debug($"[FaceInferTool]   构件: {ModelItemAnalysisHelper.GetSafeDisplayName(result.ModelItem)}");
            }
            else
            {
                LogManager.Debug("[FaceInferTool] ✗ 面推断失败（未命中/跨构件/共线）");
            }

            FaceInferred?.Invoke(this, result);
            return false;
        }

        /// <summary>
        /// 鼠标移动事件 — 暂不处理（保持轻量）
        /// </summary>
        public override bool MouseMove(View view, KeyModifiers modifiers, int x, int y, double timeOffset)
        {
            return false;
        }

        /// <summary>
        /// 光标样式 — 使用 MarkupQuickPick 与取点工具的 Measure 形成视觉区分
        /// </summary>
        public override Cursor GetCursor(View view, KeyModifiers modifier)
        {
            return Cursor.MarkupAutoPoly;
        }

        /// <summary>
        /// 核心面推断逻辑。
        /// 在 (x, y) 处做三次 PickItemFromPoint（中心+右下邻域），
        /// 通过叉积计算法向量，γ 递进规避共线场景。
        /// </summary>
        private static FaceInferResult InferFace(View view, int x, int y)
        {
            // 拾取中心点
            var centerPick = view.PickItemFromPoint(x, y);
            if (centerPick == null)
                return FaceInferResult.Failure("未命中任何模型，请点击模型表面。");

            var p0 = centerPick.Point;

            foreach (int offset in PixelOffsets)
            {
                var rightPick = view.PickItemFromPoint(x + offset, y);
                if (rightPick == null) continue;

                var downPick = view.PickItemFromPoint(x, y + offset);
                if (downPick == null) continue;

                // 必须命中同一构件
                if (rightPick.ModelItem != centerPick.ModelItem ||
                    downPick.ModelItem != centerPick.ModelItem)
                    continue;

                var p1 = rightPick.Point;
                var p2 = downPick.Point;

                var v1 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
                var v2 = new Vector3D(p2.X - p0.X, p2.Y - p0.Y, p2.Z - p0.Z);

                var normal = Cross(v1, v2);
                double len = Length(normal);
                if (len < MinCrossLength)
                {
                    LogManager.Debug($"[FaceInferTool] 偏移{offset}px 三点近似共线，尝试更大偏移");
                    continue; // 退到下一级偏移
                }

                // 归一化
                normal = new Vector3D(normal.X / len, normal.Y / len, normal.Z / len);

                // 统一法向量朝向：指向相机一侧
                var camPos = view.Document.CurrentViewpoint.Value.Position;
                var toCam = new Vector3D(camPos.X - p0.X, camPos.Y - p0.Y, camPos.Z - p0.Z);
                if (Dot(normal, toCam) < 0)
                {
                    normal = new Vector3D(-normal.X, -normal.Y, -normal.Z);
                }

                return new FaceInferResult(p0, normal, centerPick.ModelItem);
            }

            LogManager.Debug("[FaceInferTool] 所有偏移量均无法推断平面");
            return FaceInferResult.Failure("点击位置表面过窄或不平整，无法推断平面。\n请点击较大的平整表面。");
        }

        // ---- Vector3D 辅助方法（避免依赖 Navisworks Vector3D 静态方法在不同版本的可用性差异）----

        private static Vector3D Cross(Vector3D a, Vector3D b)
        {
            return new Vector3D(
                a.Y * b.Z - a.Z * b.Y,
                a.Z * b.X - a.X * b.Z,
                a.X * b.Y - a.Y * b.X);
        }

        private static double Length(Vector3D v)
        {
            return Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z);
        }

        private static double Dot(Vector3D a, Vector3D b)
        {
            return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
        }
    }
}
