/*
 * Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
 * Copyright (C) 2003-2004  Eran Kampf	eran@ekampf.com	http://www.ekampf.com
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace Framework.GameMath
{
    /// <summary>
    /// Represents a double-precision floating-point quaternion.
    /// </summary>
    /// <remarks>
    /// <para>
    /// A quaternion can be thought of as a 4-Dimentional vector of form:
    /// q = [w, x, y, z] = w + xi + yj +zk.
    /// </para>
    /// <para>
    /// A Quaternion is often written as q = s + V where S represents
    /// the scalar part (w component) and V is a 3D vector representing
    /// the imaginery coefficients (x,y,z components).
    /// </para>
    /// <para>
    /// Check out http://mathworld.wolfram.com/Quaternion.html for further details.
    /// </para>
    /// </remarks>
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct Quaternion : ICloneable
    {
        #region Private Fields
        private double _w;
        private double _x;
        private double _y;
        private double _z;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="Quaternion"/> class with the specified coordinates.
        /// </summary>
        /// <param name="x">The quaternions's X coordinate.</param>
        /// <param name="y">The quaternions's Y coordinate.</param>
        /// <param name="z">The quaternions's Z coordinate.</param>
        /// /// <param name="w">The quaternions's W coordinate.</param>
        public Quaternion(double x, double y, double z, double w)
        {
            _w = w;
            _x = x;
            _y = y;
            _z = z;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="Quaternion"/> class using coordinates from a given <see cref="Quaternion"/> instance.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance to copy the coordinates from.</param>
        public Quaternion(Quaternion quaternion)
        {
            _x = quaternion.X;
            _y = quaternion.Y;
            _z = quaternion.Z;
            _w = quaternion.W;
        }
        public Quaternion(Matrix3 rot) : this(Zero)
        {
            int[] plus1mod3 = { 1, 2, 0 };

            // Find the index of the largest diagonal component
            // These ? operations hopefully compile to conditional
            // move instructions instead of branches.
            int i = (rot[1, 1] > rot[0, 0]) ? 1 : 0;
            i = (rot[2, 2] > rot[i, i]) ? 2 : i;

            // Find the indices of the other elements
            int j = plus1mod3[i];
            int k = plus1mod3[j];

            // If we attempted to pre-normalize and trusted the matrix to be
            // perfectly orthonormal, the result would be:
            //
            //   double c = sqrt((rot[i][i] - (rot[j][j] + rot[k][k])) + 1.0)
            //   v[i] = -c * 0.5
            //   v[j] = -(rot[i][j] + rot[j][i]) * 0.5 / c
            //   v[k] = -(rot[i][k] + rot[k][i]) * 0.5 / c
            //   w    =  (rot[j][k] - rot[k][j]) * 0.5 / c
            //
            // Since we're going to pay the sqrt anyway, we perform a post normalization, which also
            // fixes any poorly normalized input.  Multiply all elements by 2*c in the above, giving:

            // nc2 = -c^2
            double nc2 = ((rot[j, j] + rot[k, k]) - rot[i, i]) - 1.0;
            this[i] = (float)nc2;
            W = (rot[j, k] - rot[k, j]);
            this[j] = -(rot[i, j] + rot[j, i]);
            this[k] = -(rot[i, k] + rot[k, i]);

            // We now have the correct result with the wrong magnitude, so normalize it:
            float s = (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
            if (s > 0.00001f)
            {
                s = 1.0f / s;
                X *= s;
                Y *= s;
                Z *= s;
                W *= s;
            }
            else
            {
                // The quaternion is nearly zero.  Make it 0 0 0 1
                X = 0.0f;
                Y = 0.0f;
                Z = 0.0f;
                W = 1.0f;
            }
        }
        #endregion

        #region Constants
        /// <summary>
        /// Double-precision floating point zero quaternion.
        /// </summary>
        public static readonly Quaternion Zero = new Quaternion(0, 0, 0, 0);
        /// <summary>
        /// Double-precision floating point identity quaternion.
        /// </summary>
        public static readonly Quaternion Identity = new Quaternion(0, 0, 0, 1);
        /// <summary>
        /// Double-precision floating point X-Axis quaternion.
        /// </summary>
        public static readonly Quaternion XAxis = new Quaternion(1, 0, 0, 0);
        /// <summary>
        /// Double-precision floating point Y-Axis quaternion.
        /// </summary>
        public static readonly Quaternion YAxis = new Quaternion(0, 1, 0, 0);
        /// <summary>
        /// Double-precision floating point Z-Axis quaternion.
        /// </summary>
        public static readonly Quaternion ZAxis = new Quaternion(0, 0, 1, 0);
        /// <summary>
        /// Double-precision floating point W-Axis quaternion.
        /// </summary>
        public static readonly Quaternion WAxis = new Quaternion(0, 0, 0, 1);
        #endregion

        #region Public Properties
        /// <summery>
        /// Gets or sets the x-coordinate of this quaternion.
        /// </summery>
        /// <value>The x-coordinate of this quaternion.</value>
        public double X
        {
            get { return _x; }
            set { _x = value; }
        }
        /// <summery>
        /// Gets or sets the y-coordinate of this quaternion.
        /// </summery>
        /// <value>The y-coordinate of this quaternion.</value>
        public double Y
        {
            get { return _y; }
            set { _y = value; }
        }
        /// <summery>
        /// Gets or sets the z-coordinate of this quaternion.
        /// </summery>
        /// <value>The z-coordinate of this quaternion.</value>
        public double Z
        {
            get { return _z; }
            set { _z = value; }
        }
        /// <summery>
        /// Gets or sets the w-coordinate of this quaternion.
        /// </summery>
        /// <value>The w-coordinate of this quaternion.</value>
        public double W
        {
            get { return _w; }
            set { _w = value; }
        }

        /// <summary>
        /// Gets the the modulus of the quaternion.
        /// </summary>
        /// <value>A double-precision floating-point number.</value>
        public double Modulus
        {
            get
            {
                return System.Math.Sqrt(_w * _w + _x * _x + _y * _y + _z * _z);
            }
        }
        /// <summary>
        /// Gets the the squared modulus of the quaternion.
        /// </summary>
        /// <value>A double-precision floating-point number.</value>
        public double ModulusSquared
        {
            get
            {
                return (_w * _w + _x * _x + _y * _y + _z * _z);
            }
        }
        /// <summary>
        /// Gets or sets the conjugate of the quaternion.
        /// </summary>
        /// <value>A <see cref="Quaternion"/> instance.</value>
        public Quaternion Conjugate
        {
            get
            {
                return new Quaternion(-_x, -_y, -_z, _w);
            }
            set
            {
                this = value.Conjugate;
            }
        }
        #endregion

        #region ICloneable Members
        /// <summary>
        /// Creates an exact copy of this <see cref="Quaternion"/> object.
        /// </summary>
        /// <returns>The <see cref="Quaternion"/> object this method creates, cast as an object.</returns>
        object ICloneable.Clone()
        {
            return new Quaternion(this);
        }
        /// <summary>
        /// Creates an exact copy of this <see cref="Quaternion"/> object.
        /// </summary>
        /// <returns>The <see cref="Quaternion"/> object this method creates.</returns>
        public Quaternion Clone()
        {
            return new Quaternion(this);
        }
        #endregion

        #region Public Static Parse Methods
        /// <summary>
        /// Converts the specified string to its <see cref="Quaternion"/> equivalent.
        /// </summary>
        /// <param name="value">A string representation of a <see cref="Quaternion"/></param>
        /// <returns>A <see cref="Quaternion"/> that represents the vector specified by the <paramref name="s"/> parameter.</returns>
        public static Quaternion Parse(string value)
        {
            Regex r = new Regex(@"\((?<w>.*),(?<x>.*),(?<y>.*),(?<z>.*)\)", RegexOptions.None);
            Match m = r.Match(value);
            if (m.Success)
            {
                return new Quaternion(
                    double.Parse(m.Result("${x}")),
                    double.Parse(m.Result("${y}")),
                    double.Parse(m.Result("${z}")),
                    double.Parse(m.Result("${w}"))
                    );
            }
            else
            {
                throw new Exception("Unsuccessful Match.");
            }
        }
        /// <summary>
        /// Converts the specified string to its <see cref="Quaternion"/> equivalent.
        /// A return value indicates whether the conversion succeeded or failed.
        /// </summary>
        /// <param name="value">A string representation of a <see cref="Quaternion"/>.</param>
        /// <param name="result">
        /// When this method returns, if the conversion succeeded,
        /// contains a <see cref="Quaternion"/> representing the vector specified by <paramref name="value"/>.
        /// </param>
        /// <returns><see langword="true"/> if value was converted successfully; otherwise, <see langword="false"/>.</returns>
        public static bool TryParse(string value, out Quaternion result)
        {
            Regex r = new Regex(@"\((?<x>.*),(?<y>.*),(?<z>.*),(?<w>.*)\)", RegexOptions.None);
            Match m = r.Match(value);
            if (m.Success)
            {
                result = new Quaternion(
                    double.Parse(m.Result("${x}")),
                    double.Parse(m.Result("${y}")),
                    double.Parse(m.Result("${z}")),
                    double.Parse(m.Result("${w}"))
                    );

                return true;
            }

            result = Quaternion.Zero;
            return false;
        }
        #endregion

        #region Public Static Quaternion Arithmetics
        /// <summary>
        /// Adds two quaternions.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <returns>A new <see cref="Quaternion"/> instance containing the sum.</returns>
        public static Quaternion Add(Quaternion left, Quaternion right)
        {
            return new Quaternion(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
        }
        /// <summary>
        /// Adds two quaternions and put the result in the third quaternion.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <param name="result">A <see cref="Quaternion"/> instance to hold the result.</param>
        public static void Add(Quaternion left, Quaternion right, ref Quaternion result)
        {
            result.X = left.X + right.X;
            result.Y = left.Y + right.Y;
            result.Z = left.Z + right.Z;
            result.W = left.W + right.W;
        }

        /// <summary>
        /// Subtracts a quaternion from a quaternion.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <returns>A new <see cref="Quaternion"/> instance containing the difference.</returns>
        public static Quaternion Subtract(Quaternion left, Quaternion right)
        {
            return new Quaternion(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
        }
        /// <summary>
        /// Subtracts a quaternion from a quaternion and puts the result into a third quaternion.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <param name="result">A <see cref="Quaternion"/> instance to hold the result.</param>
        public static void Subtract(Quaternion left, Quaternion right, ref Quaternion result)
        {
            result.X = left.X - right.X;
            result.Y = left.Y - right.Y;
            result.Z = left.Z - right.Z;
            result.W = left.W - right.W;
        }

        /// <summary>
        /// Multiplies quaternion <paramref name="a"/> by quaternion <paramref name="b"/>.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <returns>A new <see cref="Quaternion"/> containing the result.</returns>
        public static Quaternion Multiply(Quaternion left, Quaternion right)
        {
            Quaternion result = new Quaternion();
            result.X = left.W * right.X + left.X * right.W + left.Y * right.Z - left.Z * right.Y;
            result.Y = left.W * right.Y + left.Y * right.W + left.Z * right.X - left.X * right.Z;
            result.Z = left.W * right.Z + left.Z * right.W + left.X * right.Y - left.Y * right.X;
            result.W = left.W * right.W - left.X * right.X - left.Y * right.Y - left.Z * right.Z;

            return result;
        }
        /// <summary>
        /// Multiplies quaternion <paramref name="a"/> by quaternion <paramref name="b"/> and put the result in a third quaternion.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <param name="result">A <see cref="Quaternion"/> instance to hold the result.</param>
        public static void Multiply(Quaternion left, Quaternion right, ref Quaternion result)
        {
            result.X = left.W * right.X + left.X * right.W + left.Y * right.Z - left.Z * right.Y;
            result.Y = left.W * right.Y + left.Y * right.W + left.Z * right.X - left.X * right.Z;
            result.Z = left.W * right.Z + left.Z * right.W + left.X * right.Y - left.Y * right.X;
            result.W = left.W * right.W - left.X * right.X - left.Y * right.Y - left.Z * right.Z;
        }
        /// <summary>
        /// Multiplies a quaternion by a scalar.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <param name="scalar">A scalar.</param>
        /// <returns>A <see cref="Quaternion"/> instance to hold the result.</returns>
        public static Quaternion Multiply(Quaternion quaternion, double scalar)
        {
            Quaternion result = new Quaternion(quaternion);
            result.X *= scalar;
            result.Y *= scalar;
            result.Z *= scalar;
            result.W *= scalar;

            return result;
        }
        /// <summary>
        /// Multiplies a quaternion by a scalar and put the result in a third quaternion.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <param name="scalar">A scalar.</param>
        /// <param name="result">A <see cref="Quaternion"/> instance to hold the result.</param>
        public static void Multiply(Quaternion quaternion, double scalar, ref Quaternion result)
        {
            result.X = quaternion.X * scalar;
            result.Y = quaternion.Y * scalar;
            result.Z = quaternion.Z * scalar;
            result.W = quaternion.W * scalar;
        }

        /// <summary>
        /// Divides a quaternion by a scalar.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <param name="scalar">A scalar.</param>
        /// <returns>A <see cref="Quaternion"/> instance to hold the result.</returns>
        public static Quaternion Divide(Quaternion quaternion, double scalar)
        {
            if (scalar == 0)
            {
                throw new DivideByZeroException("Dividing quaternion by zero");
            }

            Quaternion result = new Quaternion(quaternion);
            result.X /= scalar;
            result.Y /= scalar;
            result.Z /= scalar;
            result.W /= scalar;

            return result;
        }
        /// <summary>
        /// Divides a quaternion by a scalar and put the result in a third quaternion.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <param name="scalar">A scalar.</param>
        /// <param name="result">A <see cref="Quaternion"/> instance to hold the result.</param>
        public static void Divide(Quaternion quaternion, double scalar, ref Quaternion result)
        {
            if (scalar == 0)
            {
                throw new DivideByZeroException("Dividing quaternion by zero");
            }

            result.X = quaternion.X / scalar;
            result.Y = quaternion.Y / scalar;
            result.Z = quaternion.Z / scalar;
            result.W = quaternion.W / scalar;
        }

        /// <summary>
        /// Calculates the dot product of two quaternions.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <returns>The dot product value.</returns>
        public static double DotProduct(Quaternion left, Quaternion right)
        {
            return left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
        }
        #endregion

        public bool isUnit(double tolerance = 1e-5)
        {
            return Math.Abs(dot(this) - 1.0f) < tolerance;
        }

        public Quaternion ToUnit()
        {
            Quaternion copyOfThis = this;
            copyOfThis *= rsq(dot(this));
            return copyOfThis;
        }

        float dot(Quaternion other)
        {
            return (float)((X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W));
        }

        float rsq(float x)
        {
            return 1.0f / (float)Math.Sqrt(x);
        }

        #region Public Static Complex Special Functions
        /// <summary>
        /// Calculates the logarithm of a given quaternion.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <returns>The quaternion's logarithm.</returns>
        public static Quaternion Log(Quaternion quaternion)
        {
            Quaternion result = new Quaternion(0, 0, 0, 0);

            if (Math.Abs(quaternion.W) < 1.0)
            {
                double angle = System.Math.Acos(quaternion.W);
                double sin = System.Math.Sin(angle);

                if (Math.Abs(sin) >= 0)
                {
                    double coeff = angle / sin;
                    result.X = coeff * quaternion.X;
                    result.Y = coeff * quaternion.Y;
                    result.Z = coeff * quaternion.Z;
                }
                else
                {
                    result.X = quaternion.X;
                    result.Y = quaternion.Y;
                    result.Z = quaternion.Z;
                }
            }

            return result;
        }
        /// <summary>
        /// Calculates the exponent of a quaternion.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <returns>The quaternion's exponent.</returns>
        public Quaternion Exp(Quaternion quaternion)
        {
            Quaternion result = new Quaternion(0, 0, 0, 0);

            double angle = System.Math.Sqrt(quaternion.X * quaternion.X + quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z);
            double sin = System.Math.Sin(angle);

            if (Math.Abs(sin) > 0)
            {
                double coeff = angle / sin;
                result.X = coeff * quaternion.X;
                result.Y = coeff * quaternion.Y;
                result.Z = coeff * quaternion.Z;
            }
            else
            {
                result.X = quaternion.X;
                result.Y = quaternion.Y;
                result.Z = quaternion.Z;
            }

            return result;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Inverts the quaternion.
        /// </summary>
        public void Inverse()
        {
            double norm = ModulusSquared;
            if (norm > 0)
            {
                double invNorm = 1.0 / norm;
                _w *= invNorm;
                _x *= -invNorm;
                _y *= -invNorm;
                _z *= -invNorm;
            }
            else
            {
                throw new Exception("Quaternion " + ToString() + " is not invertable");
            }
        }
        /// <summary>
        /// Normelizes the quaternion.
        /// </summary>
        public void Normalize()
        {
            double norm = Modulus;
            if (norm == 0)
            {
                throw new DivideByZeroException("Trying to normalize a quaternion with modulus of zero.");
            }

            _w /= norm;
            _x /= norm;
            _y /= norm;
            _z /= norm;
        }
        /// <summary>
        /// Clamps quaternion values to zero using a given tolerance value.
        /// </summary>
        /// <param name="tolerance">The tolerance to use.</param>
        /// <remarks>
        /// The quaternion values that are close to zero within the given tolerance are set to zero.
        /// </remarks>
        public void ClampZero(double tolerance)
        {
            _x = MathFunctions.Clamp(_x, 0, tolerance);
            _y = MathFunctions.Clamp(_y, 0, tolerance);
            _z = MathFunctions.Clamp(_z, 0, tolerance);
            _w = MathFunctions.Clamp(_w, 0, tolerance);
        }
        /// <summary>
        /// Clamps quaternion values to zero using the default tolerance value.
        /// </summary>
        /// <remarks>
        /// The quaternion values that are close to zero within the given tolerance are set to zero.
        /// The tolerance value used is <see cref="MathFunctions.EpsilonD"/>
        /// </remarks>
        public void ClampZero()
        {
            _x = MathFunctions.Clamp(_x, 0);
            _y = MathFunctions.Clamp(_y, 0);
            _z = MathFunctions.Clamp(_z, 0);
            _w = MathFunctions.Clamp(_w, 0);
        }
        #endregion

        #region System.Object Overrides
        /// <summary>
        /// Returns the hashcode for this instance.
        /// </summary>
        /// <returns>A 32-bit signed integer hash code.</returns>
        public override int GetHashCode()
        {
            return _w.GetHashCode() ^ _x.GetHashCode() ^ _y.GetHashCode() ^ _z.GetHashCode();
        }
        /// <summary>
        /// Returns a value indicating whether this instance is equal to
        /// the specified object.
        /// </summary>
        /// <param name="obj">An object to compare to this instance.</param>
        /// <returns><see langword="true"/> if <paramref name="obj"/> is a <see cref="Quaternion"/> and has the same values as this instance; otherwise, <see langword="false"/>.</returns>
        public override bool Equals(object obj)
        {
            if (obj is Quaternion)
            {
                Quaternion quaternion = (Quaternion)obj;
                return (_w == quaternion.W) && (_x == quaternion.X) && (_y == quaternion.Y) && (_z == quaternion.Z);
            }
            return false;
        }
        /// <summary>
        /// Returns a string representation of this object.
        /// </summary>
        /// <returns>A string representation of this object.</returns>
        public override string ToString()
        {
            return string.Format("({0}, {1}, {2}, {3})", _w, _x, _y, _z);
        }
        #endregion

        #region Comparison Operators
        /// <summary>
        /// Tests whether two specified quaternions are equal.
        /// </summary>
        /// <param name="left">The left-hand quaternion.</param>
        /// <param name="right">The right-hand quaternion.</param>
        /// <returns><see langword="true"/> if the two quaternions are equal; otherwise, <see langword="false"/>.</returns>
        public static bool operator ==(Quaternion left, Quaternion right)
        {
            return ValueType.Equals(left, right);
        }
        /// <summary>
        /// Tests whether two specified quaternions are not equal.
        /// </summary>
        /// <param name="left">The left-hand quaternion.</param>
        /// <param name="right">The right-hand quaternion.</param>
        /// <returns><see langword="true"/> if the two quaternions are not equal; otherwise, <see langword="false"/>.</returns>
        public static bool operator !=(Quaternion left, Quaternion right)
        {
            return !ValueType.Equals(left, right);
        }
        #endregion

        #region Binary Operators
        /// <summary>
        /// Adds two quaternions.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <returns>A new <see cref="Quaternion"/> instance containing the sum.</returns>
        public static Quaternion operator +(Quaternion left, Quaternion right)
        {
            return Quaternion.Add(left, right);
        }
        /// <summary>
        /// Subtracts a quaternion from a quaternion.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <returns>A new <see cref="Quaternion"/> instance containing the difference.</returns>
        public static Quaternion operator -(Quaternion left, Quaternion right)
        {
            return Quaternion.Subtract(left, right);
        }
        /// <summary>
        /// Multiplies quaternion <paramref name="a"/> by quaternion <paramref name="b"/>.
        /// </summary>
        /// <param name="left">A <see cref="Quaternion"/> instance.</param>
        /// <param name="right">A <see cref="Quaternion"/> instance.</param>
        /// <returns>A new <see cref="Quaternion"/> containing the result.</returns>
        public static Quaternion operator *(Quaternion left, Quaternion right)
        {
            return Quaternion.Multiply(left, right);
        }
        /// <summary>
        /// Multiplies a quaternion by a scalar.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <param name="scalar">A scalar.</param>
        /// <returns>A <see cref="Quaternion"/> instance to hold the result.</returns>
        public static Quaternion operator *(Quaternion quaternion, double scalar)
        {
            return Quaternion.Multiply(quaternion, scalar);
        }
        /// <summary>
        /// Multiplies a quaternion by a scalar.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <param name="scalar">A scalar.</param>
        /// <returns>A <see cref="Quaternion"/> instance to hold the result.</returns>
        public static Quaternion operator *(double scalar, Quaternion quaternion)
        {
            return Quaternion.Multiply(quaternion, scalar);
        }
        /// <summary>
        /// Divides a quaternion by a scalar.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <param name="scalar">A scalar.</param>
        /// <returns>A <see cref="Quaternion"/> instance to hold the result.</returns>
        public static Quaternion operator /(Quaternion quaternion, double scalar)
        {
            return Quaternion.Divide(quaternion, scalar);
        }
        /// <summary>
        /// Divides a scalar by a quaternion.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <param name="scalar">A scalar.</param>
        /// <returns>A <see cref="Quaternion"/> instance to hold the result.</returns>
        public static Quaternion operator /(double scalar, Quaternion quaternion)
        {
            return Quaternion.Multiply(quaternion, (1.0 / scalar));
        }
        #endregion

        #region Array Indexing Operator
        /// <summary>
        /// Indexer ( [w, x, y, z] ).
        /// </summary>
        public double this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return _x;
                    case 1:
                        return _y;
                    case 2:
                        return _z;
                    case 3:
                        return _w;
                    default:
                        throw new IndexOutOfRangeException();
                }
            }
            set
            {
                switch (index)
                {
                    case 0:
                        _x = value;
                        break;
                    case 1:
                        _y = value;
                        break;
                    case 2:
                        _z = value;
                        break;
                    case 3:
                        _w = value;
                        break;
                    default:
                        throw new IndexOutOfRangeException();
                }
                return;
            }
        }
        #endregion

        #region Conversion Operators
        /// <summary>
        /// Converts the quaternion to an array of double-precision floating point numbers.
        /// </summary>
        /// <param name="quaternion">A <see cref="Quaternion"/> instance.</param>
        /// <returns>An array of double-precision floating point numbers.</returns>
        /// <remarks>The array is [w, x, y, z].</remarks>
        public static explicit operator double[] (Quaternion quaternion)
        {
            double[] doubles = new double[4];
            doubles[1] = quaternion.X;
            doubles[2] = quaternion.Y;
            doubles[3] = quaternion.Z;
            doubles[0] = quaternion.W;
            return doubles;
        }
        #endregion
    }
}
