using System;
using System.Globalization;
using System.Numerics;

namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// 三轴旋转修正角（单位：度）。
    /// 该结构本身只保存 X/Y/Z 三个角度值，不直接绑定宿主/内部/资产语义。
    ///
    /// 关键约束：
    /// - 构造参数顺序固定为 (xDegrees, yDegrees, zDegrees)
    /// - 属性语义固定为 XDegrees / YDegrees / ZDegrees
    /// - 本结构只表示“沿某三根给定轴分别旋转多少度”，不负责决定这些轴属于宿主坐标系、内部坐标系还是资产坐标系
    ///
    /// 因此，调用方必须先明确：
    /// 1. 当前三根轴分别是什么
    /// 2. 这些角度是按哪种坐标语义解释
    /// </summary>
    public struct LocalEulerRotationCorrection : IEquatable<LocalEulerRotationCorrection>
    {
        public static readonly LocalEulerRotationCorrection Zero = new LocalEulerRotationCorrection(0.0, 0.0, 0.0);

        public LocalEulerRotationCorrection(double xDegrees, double yDegrees, double zDegrees)
        {
            XDegrees = xDegrees;
            YDegrees = yDegrees;
            ZDegrees = zDegrees;
        }

        public double XDegrees { get; }
        public double YDegrees { get; }
        public double ZDegrees { get; }

        public bool IsZero =>
            Math.Abs(XDegrees) < 1e-9 &&
            Math.Abs(YDegrees) < 1e-9 &&
            Math.Abs(ZDegrees) < 1e-9;

        /// <summary>
        /// 基于调用方传入的三根轴，按 X -> Y -> Z 的角度顺序构造旋转四元数。
        ///
        /// 注意：
        /// - 参数顺序固定为 (axisX, axisY, axisZ)
        /// - 角度来源固定为 (XDegrees, YDegrees, ZDegrees)
        /// - 组合顺序固定为 qz * qy * qx
        ///
        /// 这里的 qz * qy * qx 只表示“先应用 X，再应用 Y，再应用 Z”这组三轴修正，
        /// 不自动代表宿主世界轴或物体当前姿态轴。轴语义完全由调用方决定。
        /// </summary>
        public Quaternion CreateQuaternion(Vector3 axisX, Vector3 axisY, Vector3 axisZ)
        {
            Quaternion qx = Quaternion.CreateFromAxisAngle(Vector3.Normalize(axisX), DegreesToRadians(XDegrees));
            Quaternion qy = Quaternion.CreateFromAxisAngle(Vector3.Normalize(axisY), DegreesToRadians(YDegrees));
            Quaternion qz = Quaternion.CreateFromAxisAngle(Vector3.Normalize(axisZ), DegreesToRadians(ZDegrees));
            return Quaternion.Normalize(qz * qy * qx);
        }

        public static LocalEulerRotationCorrection FromConventionUpDegrees(ModelAxisConvention convention, double upDegrees)
        {
            if (convention == null)
            {
                throw new ArgumentNullException(nameof(convention));
            }

            switch (convention.UpAxis)
            {
                case LocalAxisDirection.PositiveX:
                case LocalAxisDirection.NegativeX:
                    return new LocalEulerRotationCorrection(upDegrees, 0.0, 0.0);
                case LocalAxisDirection.PositiveY:
                case LocalAxisDirection.NegativeY:
                    return new LocalEulerRotationCorrection(0.0, upDegrees, 0.0);
                case LocalAxisDirection.PositiveZ:
                case LocalAxisDirection.NegativeZ:
                default:
                    return new LocalEulerRotationCorrection(0.0, 0.0, upDegrees);
            }
        }

        public override string ToString()
        {
            return string.Format(
                CultureInfo.InvariantCulture,
                "X={0:F1}°,Y={1:F1}°,Z={2:F1}°",
                XDegrees,
                YDegrees,
                ZDegrees);
        }

        public bool Equals(LocalEulerRotationCorrection other)
        {
            return XDegrees.Equals(other.XDegrees) &&
                   YDegrees.Equals(other.YDegrees) &&
                   ZDegrees.Equals(other.ZDegrees);
        }

        public override bool Equals(object obj)
        {
            return obj is LocalEulerRotationCorrection other && Equals(other);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int hash = 17;
                hash = hash * 31 + XDegrees.GetHashCode();
                hash = hash * 31 + YDegrees.GetHashCode();
                hash = hash * 31 + ZDegrees.GetHashCode();
                return hash;
            }
        }

        public static bool operator ==(LocalEulerRotationCorrection left, LocalEulerRotationCorrection right)
        {
            return left.Equals(right);
        }

        public static bool operator !=(LocalEulerRotationCorrection left, LocalEulerRotationCorrection right)
        {
            return !left.Equals(right);
        }

        private static float DegreesToRadians(double degrees)
        {
            return (float)(degrees * Math.PI / 180.0);
        }

    }
}
