using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using Autodesk.Navisworks.Api;
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;

namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// 统一的真实物体参考姿态解析入口。
    /// 负责：
    /// 1. 从 fragment 提取代表姿态
    /// 2. 按 Fragment默认Up 解释成当前宿主语义下的真实姿态
    /// 3. 为 Ground / Hoisting / Rail 提供统一参考姿态来源
    /// </summary>
    public static class RealObjectReferencePoseResolver
    {
        private const float WorldAxisMinimumAlignment = 0.98f;
        private const float SemanticAxisMinimumAlignment = 0.985f;
        private const float MinimumBestToSecondGap = 0.02f;

        public static bool TryResolveFromModelItem(
            ModelItem sourceObject,
            Document doc,
            out RealObjectReferencePose referencePose)
        {
            referencePose = null;

            if (sourceObject == null)
            {
                return false;
            }

            try
            {
                var selection = ComApiBridge.ToInwOpSelection(new ModelItemCollection { sourceObject });
                var fragmentInfos = GeometryHelper.GetAllFragments(selection);
                try
                {
                    var fragmentMatrices = fragmentInfos
                        .Where(fragmentInfo => fragmentInfo.TransformMatrix != null && fragmentInfo.TransformMatrix.Length == 16)
                        .Select(fragmentInfo => fragmentInfo.TransformMatrix)
                        .ToList();

                    return TryResolveFromFragmentMatrices(fragmentMatrices, doc, out referencePose);
                }
                finally
                {
                    if (fragmentInfos != null)
                    {
                        foreach (var fragmentInfo in fragmentInfos)
                        {
                            if (fragmentInfo?.Fragment != null)
                            {
                                try
                                {
                                    Marshal.ReleaseComObject(fragmentInfo.Fragment);
                                }
                                catch
                                {
                                }
                            }
                        }
                    }

                    if (selection != null)
                    {
                        try
                        {
                            Marshal.ReleaseComObject(selection);
                        }
                        catch
                        {
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogManager.Warning($"[真实物体参考姿态] 从 fragment 提取代表姿态失败: {ex.Message}");
                return false;
            }
        }

        public static bool TryResolveFromFragmentMatrices(
            IReadOnlyCollection<double[]> fragmentMatrices,
            Document doc,
            out RealObjectReferencePose referencePose)
        {
            string fragmentDefaultUpAxis = FragmentDefaultUpContext.GetCurrentDocumentFragmentDefaultUpAxis(doc);
            string hostUpAxis = FragmentDefaultUpContext.GetCurrentHostUpAxis(doc);
            return TryResolveFromFragmentMatrices(fragmentMatrices, fragmentDefaultUpAxis, hostUpAxis, out referencePose);
        }

        public static bool TryResolveFromFragmentMatrices(
            IReadOnlyCollection<double[]> fragmentMatrices,
            string fragmentDefaultUpAxis,
            string hostUpAxis,
            out RealObjectReferencePose referencePose)
        {
            referencePose = null;

            if (fragmentMatrices == null || fragmentMatrices.Count == 0)
            {
                return false;
            }

            if (!FragmentRepresentativePoseHelper.TryGetRepresentativeFrame(fragmentMatrices, out var rawFrame))
            {
                return false;
            }

            if (!FragmentRepresentativePoseHelper.TryInterpretRepresentativeFrame(
                    rawFrame,
                    fragmentDefaultUpAxis,
                    hostUpAxis,
                    out var interpretedFrame))
            {
                return false;
            }

            if (!TryResolveSemanticLocalAxes(
                    rawFrame,
                    interpretedFrame,
                    out var hostSemanticXAxisLocalAxis,
                    out var hostSemanticYAxisLocalAxis,
                    out var hostSemanticZAxisLocalAxis))
            {
                return false;
            }

            if (!TryResolveWorldLocalAxes(
                    rawFrame,
                    out var hostWorldXAxisLocalAxis,
                    out var hostWorldYAxisLocalAxis,
                    out var hostWorldZAxisLocalAxis))
            {
                return false;
            }

            referencePose = new RealObjectReferencePose(
                interpretedFrame.Rotation,
                rawFrame.AxisX,
                rawFrame.AxisY,
                rawFrame.AxisZ,
                interpretedFrame.AxisX,
                interpretedFrame.AxisY,
                interpretedFrame.AxisZ,
                hostWorldXAxisLocalAxis,
                hostWorldYAxisLocalAxis,
                hostWorldZAxisLocalAxis,
                hostSemanticXAxisLocalAxis,
                hostSemanticYAxisLocalAxis,
                hostSemanticZAxisLocalAxis,
                string.Equals(hostUpAxis, "Y", StringComparison.OrdinalIgnoreCase)
                    ? hostSemanticYAxisLocalAxis
                    : hostSemanticZAxisLocalAxis,
                fragmentMatrices.Count,
                fragmentDefaultUpAxis,
                hostUpAxis);
            return true;
        }

        private static bool TryResolveWorldLocalAxes(
            FragmentRepresentativePoseHelper.RepresentativeFrame rawFrame,
            out LocalAxisDirection hostWorldXAxisLocalAxis,
            out LocalAxisDirection hostWorldYAxisLocalAxis,
            out LocalAxisDirection hostWorldZAxisLocalAxis)
        {
            hostWorldXAxisLocalAxis = LocalAxisDirection.PositiveX;
            hostWorldYAxisLocalAxis = LocalAxisDirection.PositiveY;
            hostWorldZAxisLocalAxis = LocalAxisDirection.PositiveZ;

            if (!TryMatchRawAxisDirection(
                    Vector3.UnitX,
                    rawFrame.AxisX,
                    rawFrame.AxisY,
                    rawFrame.AxisZ,
                    WorldAxisMinimumAlignment,
                    out hostWorldXAxisLocalAxis))
            {
                return false;
            }

            if (!TryMatchRawAxisDirection(
                    Vector3.UnitY,
                    rawFrame.AxisX,
                    rawFrame.AxisY,
                    rawFrame.AxisZ,
                    WorldAxisMinimumAlignment,
                    out hostWorldYAxisLocalAxis))
            {
                return false;
            }

            if (!TryMatchRawAxisDirection(
                    Vector3.UnitZ,
                    rawFrame.AxisX,
                    rawFrame.AxisY,
                    rawFrame.AxisZ,
                    WorldAxisMinimumAlignment,
                    out hostWorldZAxisLocalAxis))
            {
                return false;
            }

            return true;
        }

        private static bool TryResolveSemanticLocalAxes(
            FragmentRepresentativePoseHelper.RepresentativeFrame rawFrame,
            FragmentRepresentativePoseHelper.RepresentativeFrame interpretedFrame,
            out LocalAxisDirection hostSemanticXAxisLocalAxis,
            out LocalAxisDirection hostSemanticYAxisLocalAxis,
            out LocalAxisDirection hostSemanticZAxisLocalAxis)
        {
            hostSemanticXAxisLocalAxis = LocalAxisDirection.PositiveX;
            hostSemanticYAxisLocalAxis = LocalAxisDirection.PositiveY;
            hostSemanticZAxisLocalAxis = LocalAxisDirection.PositiveZ;

            if (!TryMatchRawAxisDirection(
                interpretedFrame.AxisX,
                rawFrame.AxisX,
                rawFrame.AxisY,
                rawFrame.AxisZ,
                SemanticAxisMinimumAlignment,
                out hostSemanticXAxisLocalAxis))
            {
                return false;
            }

            if (!TryMatchRawAxisDirection(
                interpretedFrame.AxisY,
                rawFrame.AxisX,
                rawFrame.AxisY,
                rawFrame.AxisZ,
                SemanticAxisMinimumAlignment,
                out hostSemanticYAxisLocalAxis))
            {
                return false;
            }

            if (!TryMatchRawAxisDirection(
                interpretedFrame.AxisZ,
                rawFrame.AxisX,
                rawFrame.AxisY,
                rawFrame.AxisZ,
                SemanticAxisMinimumAlignment,
                out hostSemanticZAxisLocalAxis))
            {
                return false;
            }

            return true;
        }

        private static bool TryMatchRawAxisDirection(
            Vector3 targetAxis,
            Vector3 rawAxisX,
            Vector3 rawAxisY,
            Vector3 rawAxisZ,
            float minimumAlignment,
            out LocalAxisDirection axisDirection)
        {
            axisDirection = LocalAxisDirection.PositiveX;
            float bestAlignment = float.NegativeInfinity;
            float secondBestAlignment = float.NegativeInfinity;

            TryUpdateBestMatch(targetAxis, rawAxisX, LocalAxisDirection.PositiveX, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
            TryUpdateBestMatch(targetAxis, -rawAxisX, LocalAxisDirection.NegativeX, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
            TryUpdateBestMatch(targetAxis, rawAxisY, LocalAxisDirection.PositiveY, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
            TryUpdateBestMatch(targetAxis, -rawAxisY, LocalAxisDirection.NegativeY, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
            TryUpdateBestMatch(targetAxis, rawAxisZ, LocalAxisDirection.PositiveZ, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
            TryUpdateBestMatch(targetAxis, -rawAxisZ, LocalAxisDirection.NegativeZ, ref bestAlignment, ref secondBestAlignment, ref axisDirection);

            return bestAlignment >= minimumAlignment
                && bestAlignment - secondBestAlignment >= MinimumBestToSecondGap;
        }

        private static void TryUpdateBestMatch(
            Vector3 targetAxis,
            Vector3 candidateAxis,
            LocalAxisDirection candidateDirection,
            ref float bestAlignment,
            ref float secondBestAlignment,
            ref LocalAxisDirection bestDirection)
        {
            float alignment = Vector3.Dot(Vector3.Normalize(targetAxis), Vector3.Normalize(candidateAxis));
            if (alignment > bestAlignment)
            {
                secondBestAlignment = bestAlignment;
                bestAlignment = alignment;
                bestDirection = candidateDirection;
            }
            else if (alignment > secondBestAlignment)
            {
                secondBestAlignment = alignment;
            }
        }

        public static bool TryDetectDefaultUpAxis(
            IReadOnlyCollection<double[]> fragmentMatrices,
            string hostUpAxis,
            out string detectedAxis,
            out float yAlignment,
            out float zAlignment)
        {
            detectedAxis = "Y";
            yAlignment = 0.0f;
            zAlignment = 0.0f;

            if (fragmentMatrices == null || fragmentMatrices.Count == 0)
            {
                return false;
            }

            if (!FragmentRepresentativePoseHelper.TryGetRepresentativeFrame(fragmentMatrices, out var rawFrame))
            {
                return false;
            }

            Vector3 hostUpVector = string.Equals(hostUpAxis, "Y", StringComparison.OrdinalIgnoreCase)
                ? Vector3.UnitY
                : Vector3.UnitZ;

            yAlignment = Math.Abs(Vector3.Dot(Vector3.Normalize(rawFrame.AxisY), hostUpVector));
            zAlignment = Math.Abs(Vector3.Dot(Vector3.Normalize(rawFrame.AxisZ), hostUpVector));
            detectedAxis = zAlignment > yAlignment ? "Z" : "Y";
            return true;
        }
    }
}
