using System;
using System.Numerics;

namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// 根据“最终姿态”计算真实物体在目标框架 forward/side/up 三个语义方向上的投影尺寸。
    /// 该层不区分 Ground / Hoisting / Rail；路径差异只体现在 targetForward / targetUp 的来源。
    /// </summary>
    public static class RealObjectProjectedExtentResolver
    {
        public static (double forwardExtent, double sideExtent, double upExtent) CalculateProjectedSemanticExtents(
            ModelAxisConvention convention,
            double forwardSize,
            double sideSize,
            double upSize,
            Quaternion baselineHostQuaternion,
            Quaternion finalHostQuaternion,
            Vector3 targetForward,
            Vector3 targetUp)
        {
            if (convention == null)
            {
                throw new ArgumentNullException(nameof(convention));
            }

            Vector3 localSize = convention.CreateScaleVector3(forwardSize, sideSize, upSize);
            Vector3 rotatedLocalX = Vector3.Normalize(Vector3.Transform(Vector3.UnitX, finalHostQuaternion));
            Vector3 rotatedLocalY = Vector3.Normalize(Vector3.Transform(Vector3.UnitY, finalHostQuaternion));
            Vector3 rotatedLocalZ = Vector3.Normalize(Vector3.Transform(Vector3.UnitZ, finalHostQuaternion));

            Vector3 normalizedTargetForward = Vector3.Normalize(targetForward);
            Vector3 normalizedTargetUp = Vector3.Normalize(targetUp);
            Vector3 normalizedTargetSide = Vector3.Normalize(Vector3.Cross(normalizedTargetForward, normalizedTargetUp));

            return (
                ProjectExtent(localSize, rotatedLocalX, rotatedLocalY, rotatedLocalZ, normalizedTargetForward),
                ProjectExtent(localSize, rotatedLocalX, rotatedLocalY, rotatedLocalZ, normalizedTargetSide),
                ProjectExtent(localSize, rotatedLocalX, rotatedLocalY, rotatedLocalZ, normalizedTargetUp));
        }

        public static (double forwardExtent, double sideExtent, double upExtent) CalculateProjectedDisplayedExtents(
            double displayedAxisXSize,
            double displayedAxisYSize,
            double displayedAxisZSize,
            Vector3 displayedHostAxisX,
            Vector3 displayedHostAxisY,
            Vector3 displayedHostAxisZ,
            Vector3 targetForward,
            Vector3 targetUp)
        {
            Vector3 displayedSize = new Vector3(
                (float)displayedAxisXSize,
                (float)displayedAxisYSize,
                (float)displayedAxisZSize);

            Vector3 normalizedDisplayedHostAxisX = Vector3.Normalize(displayedHostAxisX);
            Vector3 normalizedDisplayedHostAxisY = Vector3.Normalize(displayedHostAxisY);
            Vector3 normalizedDisplayedHostAxisZ = Vector3.Normalize(displayedHostAxisZ);

            Vector3 normalizedTargetForward = Vector3.Normalize(targetForward);
            Vector3 normalizedTargetUp = Vector3.Normalize(targetUp);
            Vector3 normalizedTargetSide = Vector3.Normalize(Vector3.Cross(normalizedTargetForward, normalizedTargetUp));

            return (
                ProjectExtent(
                    displayedSize,
                    normalizedDisplayedHostAxisX,
                    normalizedDisplayedHostAxisY,
                    normalizedDisplayedHostAxisZ,
                    normalizedTargetForward),
                ProjectExtent(
                    displayedSize,
                    normalizedDisplayedHostAxisX,
                    normalizedDisplayedHostAxisY,
                    normalizedDisplayedHostAxisZ,
                    normalizedTargetSide),
                ProjectExtent(
                    displayedSize,
                    normalizedDisplayedHostAxisX,
                    normalizedDisplayedHostAxisY,
                    normalizedDisplayedHostAxisZ,
                    normalizedTargetUp));
        }

        private static double ProjectExtent(
            Vector3 localSize,
            Vector3 rotatedLocalX,
            Vector3 rotatedLocalY,
            Vector3 rotatedLocalZ,
            Vector3 targetAxis)
        {
            return Math.Abs(Vector3.Dot(rotatedLocalX, targetAxis)) * localSize.X +
                   Math.Abs(Vector3.Dot(rotatedLocalY, targetAxis)) * localSize.Y +
                   Math.Abs(Vector3.Dot(rotatedLocalZ, targetAxis)) * localSize.Z;
        }
    }
}
