using System;

namespace Passer.LinearAlgebra
{
    public class Quat32
    {
        public float x;
        public float y;
        public float z;
        public float w;

        public Quat32()
        {
            this.x = 0;
            this.y = 0;
            this.z = 0;
            this.w = 1;
        }

        public Quat32(float x, float y, float z, float w)
        {
            this.x = x;
            this.y = y;
            this.z = z;
            this.w = w;
        }

        public static Quat32 FromSwingTwist(SwingTwist s)
        {
            Quat32 q32 = Quat32.Euler(-s.swing.vertical, s.swing.horizontal, s.twist);
            return q32;
        }

        public static Quat32 Euler(float yaw, float pitch, float roll)
        {
            float rollOver2 = roll * Angle.Deg2Rad * 0.5f;
            float sinRollOver2 = (float)Math.Sin((float)rollOver2);
            float cosRollOver2 = (float)Math.Cos((float)rollOver2);
            float pitchOver2 = pitch * 0.5f;
            float sinPitchOver2 = (float)Math.Sin((float)pitchOver2);
            float cosPitchOver2 = (float)Math.Cos((float)pitchOver2);
            float yawOver2 = yaw * 0.5f;
            float sinYawOver2 = (float)Math.Sin((float)yawOver2);
            float cosYawOver2 = (float)Math.Cos((float)yawOver2);
            Quat32 result = new()
            {
                w = cosYawOver2 * cosPitchOver2 * cosRollOver2 +
                       sinYawOver2 * sinPitchOver2 * sinRollOver2,
                x = sinYawOver2 * cosPitchOver2 * cosRollOver2 +
                       cosYawOver2 * sinPitchOver2 * sinRollOver2,
                y = cosYawOver2 * sinPitchOver2 * cosRollOver2 -
                       sinYawOver2 * cosPitchOver2 * sinRollOver2,
                z = cosYawOver2 * cosPitchOver2 * sinRollOver2 -
                       sinYawOver2 * sinPitchOver2 * cosRollOver2
            };
            return result;
        }

        public void ToAngles(out float right, out float up, out float forward)
        {
            float test = this.x * this.y + this.z * this.w;
            if (test > 0.499f)
            {  // singularity at north pole
                right = 0;
                up = 2 * (float)Math.Atan2(this.x, this.w) * Angle.Rad2Deg;
                forward = 90;
                return;
                //return Vector3(0, 2 * (float)atan2(this.x, this.w) * Angle.Rad2Deg, 90);
            }
            else if (test < -0.499f)
            {  // singularity at south pole
                right = 0;
                up = -2 * (float)Math.Atan2(this.x, this.w) * Angle.Rad2Deg;
                forward = -90;
                return;
                //return Vector3(0, -2 * (float)atan2(this.x, this.w) * Angle.Rad2Deg, -90);
            }
            else
            {
                float sqx = this.x * this.x;
                float sqy = this.y * this.y;
                float sqz = this.z * this.z;

                right = (float)Math.Atan2(2 * this.x * this.w - 2 * this.y * this.z, 1 - 2 * sqx - 2 * sqz) * Angle.Rad2Deg;
                up = (float)Math.Atan2(2 * this.y * this.w - 2 * this.x * this.z, 1 - 2 * sqy - 2 * sqz) * Angle.Rad2Deg;
                forward = (float)Math.Asin(2 * test) * Angle.Rad2Deg;
                return;
                // return Vector3(
                //     atan2f(2 * this.x * this.w - 2 * this.y * this.z, 1 - 2 * sqx - 2 * sqz) *
                //         Rad2Deg,
                //     atan2f(2 * this.y * this.w - 2 * this.x * this.z, 1 - 2 * sqy - 2 * sqz) *
                //         Rad2Deg,
                //     asinf(2 * test) * Angle.Rad2Deg);
            }
        }

    }
}