using System;
using System.Numerics;

namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// 计算物体本地预旋转后，在 forward/side/up 语义下的投影尺寸。
    /// </summary>
    public static class RotatedObjectExtentHelper
    {
        public static (double forwardExtent, double sideExtent, double upExtent) CalculateGroundSemanticExtents(
            CoordinateSystemType hostType,
            double forwardSize,
            double sideSize,
            double upSize,
            LocalEulerRotationCorrection hostCorrection)
        {
            var convention = ModelAxisConvention.CreateDefaultForHost(hostType);
            var adapter = new HostCoordinateAdapter(hostType);
            Quaternion correctionQuaternion = adapter.CreateHostRotationCorrection(hostCorrection);
            return CalculateProjectedSemanticExtents(
                convention,
                forwardSize,
                sideSize,
                upSize,
                correctionQuaternion);
        }

        public static (double forwardExtent, double sideExtent, double upExtent) CalculateProjectedSemanticExtents(
            ModelAxisConvention convention,
            double forwardSize,
            double sideSize,
            double upSize,
            Quaternion correctionQuaternion)
        {
            if (convention == null)
            {
                throw new ArgumentNullException(nameof(convention));
            }

            if (Math.Abs(correctionQuaternion.X) < 1e-9f &&
                Math.Abs(correctionQuaternion.Y) < 1e-9f &&
                Math.Abs(correctionQuaternion.Z) < 1e-9f &&
                Math.Abs(correctionQuaternion.W - 1.0f) < 1e-9f)
            {
                return (forwardSize, sideSize, upSize);
            }

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

            Vector3 semanticForward = Vector3.Normalize(convention.ForwardUnitVector);
            Vector3 semanticUp = Vector3.Normalize(convention.UpUnitVector);
            Vector3 semanticSide = Vector3.Normalize(Vector3.Cross(semanticForward, semanticUp));

            return (
                ProjectExtent(localSize, rotatedLocalX, rotatedLocalY, rotatedLocalZ, semanticForward),
                ProjectExtent(localSize, rotatedLocalX, rotatedLocalY, rotatedLocalZ, semanticSide),
                ProjectExtent(localSize, rotatedLocalX, rotatedLocalY, rotatedLocalZ, semanticUp));
        }

        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;
        }
    }
}
