using System;
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;

namespace MagmaEngine.Math;

public struct SFixPointVector3 : IEquatable<SFixPointVector3>
{
    /// <summary>
    ///     Initializes a new instance of FixPointVector3
    /// </summary>
    /// <param name="x">Initial value for the x-component of the vector.</param>
    /// <param name="y">Initial value for the y-component of the vector.</param>
    /// <param name="z">Initial value for the z-component of the vector.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public SFixPointVector3(int x, int y, int z)
    {
        m_X = x;
        m_Y = y;
        m_Z = z;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public SFixPointVector3(FixPoint16 x, FixPoint16 y, FixPoint16 z)
    {
        m_X = x;
        m_Y = y;
        m_Z = z;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public SFixPointVector3(FixPoint16 x)
    {
        m_X = x;
        m_Y = x;
        m_Z = x;
    }

    public SFixPointVector3(float x, float y, float z)
    {
        m_X = new(x);
        m_Y = new(y);
        m_Z = new(z);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public SFixPointVector3(Vector3 coord)
    {
        m_X = new(coord.X);
        m_Y = new(coord.Y);
        m_Z = new(coord.Z);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator SFixPointVector3(Vector3 coord)
    {
        return new(new(coord.X), new(coord.Y), new FixPoint16(coord.Z));
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector3(SFixPointVector3 coord)
    {
        return new(coord.m_X.ToFloat(), coord.m_Y.ToFloat(), coord.m_Z.ToFloat());
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public Vector3 ToVector3()
    {
        return new(m_X.ToFloat(), m_Y.ToFloat(), m_Z.ToFloat());
    }

#region -- base overrides ---------------------------------------------

    [Pure]
    public override string ToString()
    {
        return $"({m_X.ToString()}, {m_Y.ToString()}, {m_Z.ToString()})";
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public bool Equals(SFixPointVector3 other)
    {
        return m_X == other.m_X && m_Y == other.m_Y && m_Z == other.m_Z;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public override bool Equals(object? obj)
    {
        if (obj is SFixPointVector3 fixPointVector3)
            return Equals(fixPointVector3);

        return false;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public override int GetHashCode()
    {
        return m_X.GetHashCode() + m_Y.GetHashCode() + m_Z.GetHashCode();
    }

#endregion

#region -- static properties ------------------------------------------

    public static readonly SFixPointVector3 s_Zero = new(0, 0, 0);
    public static readonly SFixPointVector3 s_One = new(1, 1, 1);
    public static readonly SFixPointVector3 s_UnitX = new(1, 0, 0);
    public static readonly SFixPointVector3 s_UnitY = new(0, 1, 0);
    public static readonly SFixPointVector3 s_UnitZ = new(0, 0, 1);
    public static readonly SFixPointVector3 s_MaxValue = new(FixPoint16.MaxValue, FixPoint16.MaxValue, FixPoint16.MaxValue);
    public static readonly SFixPointVector3 s_MinValue = new(FixPoint16.MinValue, FixPoint16.MinValue, FixPoint16.MinValue);

#endregion

#region -- public properties  -----------------------------------------

    public FixPoint16 this[int i]
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        [Pure]
        get
        {
            switch (i)
            {
                case 0:  return m_X;
                case 1:  return m_Y;
                case 2:  return m_Z;
                default: throw new ArgumentOutOfRangeException(nameof(i));
            }
        }
        set
        {
            switch (i)
            {
                case 0:
                    m_X = value;
                    break;
                case 1:
                    m_Y = value;
                    break;
                case 2:
                    m_Z = value;
                    break;
                default: throw new ArgumentOutOfRangeException(nameof(i));
            }
        }
    }

    /// <summary>
    ///     Returns a new normalized FixPointVector3 from the current vector.
    /// </summary>
    public SFixPointVector3 Normalized
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        [Pure]
        get
        {
            var length = Length();

            if (!length.IsZero())
            {
                SFixPointVector3 result = new(m_X / length, m_Y / length, m_Z / length);
                return result;
            }

            throw new InvalidOperationException("Error: can not normalize vector, the vector length is zero.");
        }
    }

    public FixPointVector2 XY
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        [Pure]
        get => new(m_X, m_Y);
    }

    public FixPointVector2 XZ
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        [Pure]
        get => new(m_X, m_Z);
    }

#endregion

    #region -- public methods ---------------------------------------------

    /// <summary>
    ///     Calculates the length of the current vector.
    /// </summary>
    /// <returns>The Length of the current vector.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public FixPoint16 Length()
    {
        return FixPoint16.Length(m_X, m_Y, m_Z);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public int CompareLength(FixPoint16 length)
    {
        unchecked
        {
            return ((m_X.m_Value * (long)m_X.m_Value) + (m_Y.m_Value * (long)m_Y.m_Value) + (m_Z.m_Value * (long)m_Z.m_Value)).CompareTo(length.m_Value * (long)length.m_Value);
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public long LengthSquaredLong()
    {
        unchecked
        {
            return (m_X.m_Value * (long)m_X.m_Value) + (m_Y.m_Value * (long)m_Y.m_Value) + (m_Z.m_Value * (long)m_Z.m_Value);
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public int CompareLength(SFixPointVector3 other)
    {
        unchecked
        {
            return ((m_X.m_Value * (long)m_X.m_Value) + (m_Y.m_Value * (long)m_Y.m_Value) + (m_Z.m_Value * (long)m_Z.m_Value)).CompareTo(
                (other.m_X.m_Value * (long)other.m_X.m_Value) + (other.m_Y.m_Value * (long)other.m_Y.m_Value) + (other.m_Z.m_Value * (long)other.m_Z.m_Value));
        }
    }

    /// <summary>
    ///     Normalizes the current vector
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Normalize()
    {
        unchecked
        {
            var length = Length();

            if (!length.IsZero())
            {
                m_X = m_X / length;
                m_Y = m_Y / length;
                m_Z = m_Z / length;
            }
#if DEBUG
            else
            {
                throw new("Error: can not normalize vector, the vector length is zero.");
            }
#endif
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public bool IsZero()
    {
        return m_X.IsZero() && m_Y.IsZero() && m_Z.IsZero();
    }

    /// <summary>
    ///     Adds a given scalar value to each component of the current FixPointVector3.
    /// </summary>
    /// <param name="value">The scalar value</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Add(FixPoint16 value)
    {
        unchecked
        {
            m_X = m_X + value;
            m_Y = m_Y + value;
            m_Z = m_Z + value;
        }
    }

    /// <summary>
    ///     Adds a given FixPointVector3 to the current FixPointVector3.
    /// </summary>
    /// <param name="other">The vector to be added.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Add(SFixPointVector3 other)
    {
        unchecked
        {
            m_X = m_X + other.m_X;
            m_Y = m_Y + other.m_Y;
            m_Z = m_Z + other.m_Z;
        }
    }

    /// <summary>
    ///     Subtracts a scalar value from each component of the current FixPointVector3.
    /// </summary>
    /// <param name="value">The scalar value.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Subtract(FixPoint16 value)
    {
        unchecked
        {
            m_X = m_X - value;
            m_Y = m_Y - value;
            m_Z = m_Z - value;
        }
    }

    /// <summary>
    ///     Subtracts a given FixPointVector3 from the current FixPointVector3.
    /// </summary>
    /// <param name="other">The vector to be subtracted.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Subtract(SFixPointVector3 other)
    {
        unchecked
        {
            m_X = m_X - other.m_X;
            m_Y = m_Y - other.m_Y;
            m_Z = m_Z - other.m_Z;
        }
    }

    /// <summary>
    ///     Multiplies each component of the current FixPointVector3 by a given scalar value.
    /// </summary>
    /// <param name="scalar">The scalar value.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Multiply(FixPoint16 scalar)
    {
        unchecked
        {
            m_X = m_X * scalar;
            m_Y = m_Y * scalar;
            m_Z = m_Z * scalar;
        }
    }

    /// <summary>
    ///     Multiplies the current FixPointVector3 by another FixPointVector3.
    /// </summary>
    /// <param name="other">The source vector.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Multiply(SFixPointVector3 other)
    {
        unchecked
        {
            m_X = m_X * other.m_X;
            m_Y = m_Y * other.m_Y;
            m_Z = m_Z * other.m_Z;
        }
    }

    /// <summary>
    ///     Divides each component of the current FixPointVector3 by a given scalar value.
    /// </summary>
    /// <param name="divider">The scalar divider.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Divide(FixPoint16 divider)
    {
#if DEBUG
        if (divider.IsZero())
            throw new("Error: divider is zero (FixPointVector3.Divide).");
#endif
        unchecked
        {
            m_X = m_X / divider;
            m_Y = m_Y / divider;
            m_Z = m_Z / divider;
        }
    }

    /// <summary>
    ///     Divides the current FixPointVector3 by another FixPointVector3.
    /// </summary>
    /// <param name="other">The vector divider.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Divide(SFixPointVector3 other)
    {
        unchecked
        {
            if (!other.m_X.IsZero() && !other.m_Y.IsZero() && !other.m_Z.IsZero())
            {
                m_X = m_X / other.m_X;
                m_Y = m_Y / other.m_Y;
                m_Z = m_Z / other.m_Z;
            }
#if DEBUG
            else
            {
                throw new("Error: divider vector contains zero (FixPointVector3.Divide).");
            }
#endif
        }
    }

    /// <summary>
    ///     Transforms a FixPointVector3.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    public static SFixPointVector3 Transform(SFixPointVector3 v, SFixPointQuaternionTransform t)
    {
        return Transform(v * t.m_Size, t.m_Orientation) + t.m_Position;
    }

    /// <summary>
    ///     Transforms a vector by the given Quaternion rotation value.
    /// </summary>
    /// <param name="value">The source vector to be rotated.</param>
    /// <param name="rotation">The rotation to apply.</param>
    /// <returns>The transformed vector.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Transform(SFixPointVector3 value, SFixPointQuaternion rotation)
    {
        var x2 = rotation.m_X + rotation.m_X;
        var y2 = rotation.m_Y + rotation.m_Y;
        var z2 = rotation.m_Z + rotation.m_Z;

        var wx2 = rotation.m_W * x2;
        var wy2 = rotation.m_W * y2;
        var wz2 = rotation.m_W * z2;
        var xx2 = rotation.m_X * x2;
        var xy2 = rotation.m_X * y2;
        var xz2 = rotation.m_X * z2;
        var yy2 = rotation.m_Y * y2;
        var yz2 = rotation.m_Y * z2;
        var zz2 = rotation.m_Z * z2;

        return new((value.m_X * (1 - yy2 - zz2)) + (value.m_Y * (xy2 - wz2)) + (value.m_Z * (xz2 + wy2)), (value.m_X * (xy2 + wz2)) + (value.m_Y * (1 - xx2 - zz2)) + (value.m_Z * (yz2 - wx2)),
                   (value.m_X * (xz2 - wy2)) + (value.m_Y * (yz2 + wx2)) + (value.m_Z * (1 - xx2 - yy2)));
    }

    #endregion

    #region -- public static methods --------------------------------------

    /// <summary>
    ///     Calculates the length of the given vector.
    /// </summary>
    /// <returns>The Length of the given vector.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FixPoint16 Length(SFixPointVector3 value)
    {
        return value.Length();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int CompareLength(SFixPointVector3 value, FixPoint16 fLength)
    {
        unchecked
        {
            return ((value.m_X.m_Value * (long)value.m_X.m_Value) + (value.m_Y.m_Value * (long)value.m_Y.m_Value) + (value.m_Z.m_Value * (long)value.m_Z.m_Value)).CompareTo(
                fLength.m_Value * (long)fLength.m_Value);
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int CompareLength(SFixPointVector3 value1, SFixPointVector3 value2)
    {
        unchecked
        {
            return ((value1.m_X.m_Value * (long)value1.m_X.m_Value) + (value1.m_Y.m_Value * (long)value1.m_Y.m_Value) + (value1.m_Z.m_Value * (long)value1.m_Z.m_Value)).CompareTo(
                (value2.m_X.m_Value * (long)value2.m_X.m_Value) + (value2.m_Y.m_Value * (long)value2.m_Y.m_Value) + (value2.m_Z.m_Value * (long)value2.m_Z.m_Value));
        }
    }

    /// <summary>
    ///     Computes the cross product of two vectors.
    /// </summary>
    /// <param name="vector1">The first vector.</param>
    /// <param name="vector2">The second vector.</param>
    /// <returns>The cross product.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Cross(SFixPointVector3 vector1, SFixPointVector3 vector2)
    {
        return new((vector1.m_Y * vector2.m_Z) - (vector1.m_Z * vector2.m_Y), (vector1.m_Z * vector2.m_X) - (vector1.m_X * vector2.m_Z),
                   (vector1.m_X * vector2.m_Y) - (vector1.m_Y * vector2.m_X));
    }

    /// <summary>
    ///     Calculates the distance between two vectors. (manhatten/taxi-cab metrix)
    /// </summary>
    /// <param name="left">The source vector</param>
    /// <param name="right">The source vector</param>
    /// <returns>Distance between the two vectors.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FixPoint16 DistanceManhattan(SFixPointVector3 left, SFixPointVector3 right)
    {
        return FixPoint16.Abs(left.m_X - right.m_X) + FixPoint16.Abs(left.m_Y - right.m_Y) + FixPoint16.Abs(left.m_Z - right.m_Z);
    }

    /// <summary>
    ///     Calculates the distance between two vectors.
    /// </summary>
    /// <param name="left">The source vector</param>
    /// <param name="right">The source vector</param>
    /// <returns>Distance between the two vectors.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FixPoint16 Distance(SFixPointVector3 left, SFixPointVector3 right)
    {
        var dx = left.m_X - right.m_X;
        var dy = left.m_Y - right.m_Y;
        var dz = left.m_Z - right.m_Z;
        return FixPoint16.Length(dx, dy, dz);
    }

    /// <summary>
    ///     Calculates the dot product of two vectors.
    /// </summary>
    /// <param name="left">The source vector.</param>
    /// <param name="right">The source vector.</param>
    /// <returns>The dot product of the two vectors.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FixPoint16 Dot(SFixPointVector3 left, SFixPointVector3 right)
    {
        return (left.m_X * right.m_X) + (left.m_Y * right.m_Y) + (left.m_Z * right.m_Z);
    }

    /// <summary>
    ///     Calculates the dot product of two vectors.
    /// </summary>
    /// <param name="left">The source vector.</param>
    /// <param name="right">The source vector.</param>
    /// <returns>The dot product of the two vectors.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FixPoint16Long DotLong(SFixPointVector3 left, SFixPointVector3 right)
    {
        return new() { m_Value = (left.m_X.m_Value * (long)right.m_X.m_Value) + (left.m_Y.m_Value * (long)right.m_Y.m_Value) + (left.m_Z.m_Value * (long)right.m_Z.m_Value) };
    }

    /// <summary>
    ///     Calculates the sign of the dot product of two vectors.
    /// </summary>
    /// <param name="left">The source vector.</param>
    /// <param name="right">The source vector.</param>
    /// <returns>The sign of the dot product of the two vectors.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int DotSign(SFixPointVector3 left, SFixPointVector3 right)
    {
        unchecked
        {
            return ((left.m_X.m_Value * (long)right.m_X.m_Value) + (left.m_Y.m_Value * (long)right.m_Y.m_Value) + (left.m_Z.m_Value * (long)right.m_Z.m_Value)).CompareTo(0);
        }
    }

    /// <summary>
    ///     Creates a unit vector from the specified vector.
    /// </summary>
    /// <param name="value">The source vector.</param>
    /// <returns>The created unit vector.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Normalize(SFixPointVector3 value)
    {
        return value.Normalized;
    }

    /// <summary>
    ///     Returns a vector that contains the lowest value from each matching pair of components.
    /// </summary>
    /// <param name="value1">The source vector.</param>
    /// <param name="value2">The source vector.</param>
    /// <returns>The minimized vector.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Min(SFixPointVector3 value1, SFixPointVector3 value2)
    {
        SFixPointVector3 result;
        result.m_X = FixPoint16.Min(value1.m_X, value2.m_X);
        result.m_Y = FixPoint16.Min(value1.m_Y, value2.m_Y);
        result.m_Z = FixPoint16.Min(value1.m_Z, value2.m_Z);
        return result;
    }

    /// <summary>
    ///     Returns a vector that contains the highest value from each matching pair of components.
    /// </summary>
    /// <param name="value1">The source vector.</param>
    /// <param name="value2">The source vector.</param>
    /// <returns>The maximized vector.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Max(SFixPointVector3 value1, SFixPointVector3 value2)
    {
        SFixPointVector3 result;
        result.m_X = FixPoint16.Max(value1.m_X, value2.m_X);
        result.m_Y = FixPoint16.Max(value1.m_Y, value2.m_Y);
        result.m_Z = FixPoint16.Max(value1.m_Z, value2.m_Z);
        return result;
    }

    /// <summary>
    ///     Returns a vector pointing in the opposite direction.
    /// </summary>
    /// <param name="value">The source vector.</param>
    /// <returns>A new vector pointing in the opposite direction.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Negate(SFixPointVector3 value)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = -value.m_X;
            result.m_Y = -value.m_Y;
            result.m_Z = -value.m_Z;
            return result;
        }
    }

    /// <summary>
    ///     Adds two vectors
    /// </summary>
    /// <param name="value1">The source vector.</param>
    /// <param name="value2">The source vector.</param>
    /// <returns>A new vector representing the sum of the source vectors.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Add(SFixPointVector3 value1, SFixPointVector3 value2)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value1.m_X + value2.m_X;
            result.m_Y = value1.m_Y + value2.m_Y;
            result.m_Z = value1.m_Z + value2.m_Z;
            return result;
        }
    }

    /// <summary>
    ///     Adds a given scalar value to each component of a given vector.
    /// </summary>
    /// <param name="value1">The source vector.</param>
    /// <param name="value2">The scalar value to be added to the vector.</param>
    /// <returns>A new FixPointVector3 representing the sum of the given vector and scalar.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Add(SFixPointVector3 value1, FixPoint16 value2)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value1.m_X + value2;
            result.m_Y = value1.m_Y + value2;
            result.m_Z = value1.m_Z + value2;
            return result;
        }
    }

    /// <summary>
    ///     Subtracts a vector from another vector.
    /// </summary>
    /// <param name="value1">The vector to be subtracted from.</param>
    /// <param name="value2">The vector to be subtracted.</param>
    /// <returns>A new vector representing the result of the subtraction.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Subtract(SFixPointVector3 value1, SFixPointVector3 value2)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value1.m_X - value2.m_X;
            result.m_Y = value1.m_Y - value2.m_Y;
            result.m_Z = value1.m_Z - value2.m_Z;
            return result;
        }
    }

    /// <summary>
    ///     Subtracts a given scalar value from each component of a given vector.
    /// </summary>
    /// <param name="value1">The vector to be subtracted from.</param>
    /// <param name="value2">The scalar value to subtracted.</param>
    /// <returns>A new FixPointVector3 representing the result of the subtraction.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Subtract(SFixPointVector3 value1, FixPoint16 value2)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value1.m_X - value2;
            result.m_Y = value1.m_Y - value2;
            result.m_Z = value1.m_Z - value2;
            return result;
        }
    }

    /// <summary>
    ///     Multiplies the components of two vectors by each other.
    /// </summary>
    /// <param name="value1">The source vector.</param>
    /// <param name="value2">The source vector.</param>
    /// <returns>A new vector representing the result of the mulitiplication.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Multiply(SFixPointVector3 value1, SFixPointVector3 value2)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value1.m_X * value2.m_X;
            result.m_Y = value1.m_Y * value2.m_Y;
            result.m_Z = value1.m_Z * value2.m_Z;
            return result;
        }
    }

    /// <summary>
    ///     Multiplies a vector by a scalar value.
    /// </summary>
    /// <param name="value">The source vector.</param>
    /// <param name="scalar">The scalar value.</param>
    /// <returns>A new vector representing the result of the mulitiplication.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Multiply(SFixPointVector3 value, FixPoint16 scalar)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value.m_X * scalar;
            result.m_Y = value.m_Y * scalar;
            result.m_Z = value.m_Z * scalar;
            return result;
        }
    }

    /// <summary>
    ///     Divides the components of a vector by the components of another vector.
    /// </summary>
    /// <param name="value1">The source vector.</param>
    /// <param name="value2">The divisor vector.</param>
    /// <returns>A new vector representing the result of the division.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Divide(SFixPointVector3 value1, SFixPointVector3 value2)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value1.m_X / value2.m_X;
            result.m_Y = value1.m_Y / value2.m_Y;
            result.m_Z = value1.m_Z / value2.m_Z;
            return result;
        }
    }

    /// <summary>
    ///     Projects a vector onto another vector.
    /// </summary>
    /// <param name="projected">The projected vector.</param>
    /// <param name="projectionTarget">The vector the projected vector is being projected on.</param>
    /// <returns>A new vector representing the result of the division.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Project(SFixPointVector3 projected, SFixPointVector3 projectionTarget)
    {
        unchecked
        {
            SFixPointVector3 result;
            var fpDot = Dot(projected, projectionTarget);
            var fpProjectionTargetLength = projectionTarget.Length();
            var fpScalar = fpDot / (fpProjectionTargetLength * fpProjectionTargetLength);
            result = projectionTarget * fpScalar;
            return result;
        }
    }

    /// <summary>
    ///     Divides a vector by a scalar value.
    /// </summary>
    /// <param name="value">The source vector.</param>
    /// <param name="divider">The divider</param>
    /// <returns>A new vector representing the result of the division.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Divide(SFixPointVector3 value, FixPoint16 divider)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value.m_X / divider;
            result.m_Y = value.m_Y / divider;
            result.m_Z = value.m_Z / divider;
            return result;
        }
    }

    /// <summary>
    ///     Creates a new FixPointVector3 with each component being the result of dividing a scalar value by the corresponding
    ///     component of a vector.
    /// </summary>
    /// <param name="value">The scalar value to be divided.</param>
    /// <param name="divider">The divider vector</param>
    /// <returns>
    ///     A new vector with each component being the result of dividing the scalar value by the corresponding component
    ///     of the vector.
    /// </returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Divide(FixPoint16 value, SFixPointVector3 divider)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value / divider.m_X;
            result.m_Y = value / divider.m_Y;
            result.m_Z = value / divider.m_Z;
            return result;
        }
    }

    /// <summary>
    ///     Performs a linear interpolation between two vectors.
    /// </summary>
    /// <param name="value1">The source vector.</param>
    /// <param name="value2">The source vector.</param>
    /// <param name="amount">
    ///     The value between 0 and 1 indicating the weight of _value2. '0.0' will cause _value1 to be
    ///     returned; '1.0' will cause _value2 to be returned.
    /// </param>
    /// <returns>The linear interpolation of the two vectors.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 Lerp(SFixPointVector3 value1, SFixPointVector3 value2, FixPoint16 amount)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = value1.m_X + (amount * (value2.m_X - value1.m_X));
            result.m_Y = value1.m_Y + (amount * (value2.m_Y - value1.m_Y));
            result.m_Z = value1.m_Z + (amount * (value2.m_Z - value1.m_Z));
            return result;
        }
    }

    /// <summary>
    ///     Interpolates between two vectors using a cubic equation.
    /// </summary>
    /// <param name="value1">The source value.</param>
    /// <param name="value2">The source value.</param>
    /// <param name="amount">The weighting value.</param>
    /// <returns>The interpolated value.</returns>
    public static SFixPointVector3 SmoothStep(SFixPointVector3 value1, SFixPointVector3 value2, FixPoint16 amount)
    {
        var smootstep = FixPoint16.Min(0, FixPoint16.Max(1, amount));
        smootstep = smootstep * smootstep * (3 - (2 * smootstep));
        return Lerp(value1, value2, smootstep);
    }

    /// <summary>
    ///     Returns a FixPointVector3 containing the 2D Cartesian coordinates of a point specified in barycentric (areal)
    ///     coordinates relative to a 2D triangle.
    /// </summary>
    /// <param name="value1">A FixPointVector3 containing the 2D Cartesian coordinates of vertex 1 of the triangle.</param>
    /// <param name="value2">A FixPointVector3 containing the 2D Cartesian coordinates of vertex 2 of the triangle.</param>
    /// <param name="value3">A FixPointVector3 containing the 2D Cartesian coordinates of vertex 3 of the triangle.</param>
    /// <param name="amount1">
    ///     Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in
    ///     _value2).
    /// </param>
    /// <param name="amount2">
    ///     Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in
    ///     _value3).
    /// </param>
    /// <returns>A new FixPointVector3 containing the 2D Cartesian coordinates of the specified point.</returns>
    public static SFixPointVector3 Barycentric(SFixPointVector3 value1, SFixPointVector3 value2, SFixPointVector3 value3, FixPoint16 amount1, FixPoint16 amount2)
    {
        SFixPointVector3 result;
        result.m_X = value1.m_X + (amount1 * (value2.m_X - value1.m_X)) + (amount2 * (value3.m_X - value1.m_X));
        result.m_Y = value1.m_Y + (amount1 * (value2.m_Y - value1.m_Y)) + (amount2 * (value3.m_Y - value1.m_Y));
        result.m_Z = value1.m_Z + (amount1 * (value2.m_Z - value1.m_Z)) + (amount2 * (value3.m_Z - value1.m_Z));
        return result;
    }

    public static SFixPointVector3 Fract(SFixPointVector3 p)
    {
        return new(FixPoint16.Fract(p.m_X), FixPoint16.Fract(p.m_Y), FixPoint16.Fract(p.m_Z));
    }

#endregion

#region -- operators --------------------------------------------------

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(SFixPointVector3 left, SFixPointVector3 right)
    {
        return left.Equals(right);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(SFixPointVector3 left, SFixPointVector3 right)
    {
        return !left.Equals(right);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator -(SFixPointVector3 value)
    {
        return Negate(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator +(SFixPointVector3 left, SFixPointVector3 right)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = left.m_X + right.m_X;
            result.m_Y = left.m_Y + right.m_Y;
            result.m_Z = left.m_Z + right.m_Z;
            return result;
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator +(SFixPointVector3 left, FixPoint16 right)
    {
        return Add(left, right);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator -(SFixPointVector3 left, SFixPointVector3 right)
    {
        unchecked
        {
            SFixPointVector3 result;
            result.m_X = left.m_X - right.m_X;
            result.m_Y = left.m_Y - right.m_Y;
            result.m_Z = left.m_Z - right.m_Z;
            return result;
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator -(SFixPointVector3 left, FixPoint16 right)
    {
        return Subtract(left, right);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator *(SFixPointVector3 left, SFixPointVector3 right)
    {
        return Multiply(left, right);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator *(SFixPointVector3 left, FixPoint16 right)
    {
        return new() {
            m_X = left.m_X * right,
            m_Y = left.m_Y * right,
            m_Z = left.m_Z * right
        };
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator *(FixPoint16 left, SFixPointVector3 right)
    {
        return Multiply(right, left);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator /(SFixPointVector3 left, SFixPointVector3 right)
    {
        return Divide(left, right);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator /(SFixPointVector3 left, FixPoint16 right)
    {
        return new() {
            m_X = left.m_X / right,
            m_Y = left.m_Y / right,
            m_Z = left.m_Z / right
        };
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SFixPointVector3 operator /(FixPoint16 left, SFixPointVector3 right)
    {
        return Divide(left, right);
    }

#endregion

    /// <summary>
    ///     The x-component of the vector.
    /// </summary>
    public FixPoint16 m_X;

    /// <summary>
    ///     The y-component of the vector.
    /// </summary>
    public FixPoint16 m_Y;

    /// <summary>
    ///     The z-component of the vector.
    /// </summary>
    public FixPoint16 m_Z;
}