using System;
using System.Runtime.InteropServices;
using MTV3D65;

namespace OctreeRendering
{
    [StructLayout(LayoutKind.Sequential)]
    struct Vector2f : IEquatable<Vector2f>
    {
        public float X, Y;

        public Vector2f(float c)
        {
            X = Y = c;
        }
        public Vector2f(float x, float y)
        {
            X = x;
            Y = y;
        }

        #region Constants

        public static readonly Vector2f One = new Vector2f(1, 1);
        public static readonly Vector2f Zero = new Vector2f(0, 0);

        public static readonly Vector2f UnitX = new Vector2f(1, 0);
        public static readonly Vector2f UnitY = new Vector2f(0, 1);

        public static readonly Vector2f Up = new Vector2f(0, 1);
        public static readonly Vector2f Down = new Vector2f(0, -1);
        public static readonly Vector2f Left = new Vector2f(-1, 0);
        public static readonly Vector2f Right = new Vector2f(1, 0);

        #endregion

        #region Operators

        public static Vector2f operator +(Vector2f a, Vector2f b)
        {
            return new Vector2f(a.X + b.X, a.Y + b.Y);
        }
        public static Vector2f operator -(Vector2f a, Vector2f b)
        {
            return new Vector2f(a.X - b.X, a.Y - b.Y);
        }
        public static Vector2f operator -(Vector2f vector)
        {
            return new Vector2f(-vector.X, -vector.Y);
        }
        public static Vector2f operator /(Vector2f a, Vector2f b)
        {
            return new Vector2f(a.X / b.X, a.Y / b.Y);
        }
        public static Vector2f operator /(Vector2f a, float b)
        {
            return new Vector2f(a.X / b, a.Y / b);
        }
        public static Vector2f operator *(Vector2f a, Vector2f b)
        {
            return new Vector2f(a.X * b.X, a.Y * b.Y);
        }
        public static Vector2f operator *(Vector2f a, float b)
        {
            return new Vector2f(a.X * b, a.Y * b);
        }
        public static Vector2f operator *(float a, Vector2f b)
        {
            return new Vector2f(a * b.X, a * b.Y);
        }
        public static implicit operator TV_2DVECTOR(Vector2f vector)
        {
            return new TV_2DVECTOR(vector.X, vector.Y);
        }
        public static implicit operator Vector2f(TV_2DVECTOR vector)
        {
            return new Vector2f(vector.x, vector.y);
        }
        public static bool operator ==(Vector2f a, Vector2f b)
        {
            return a.X == b.X && a.Y == b.Y;
        }
        public static bool operator !=(Vector2f a, Vector2f b)
        {
            return a.X != b.X || a.Y != b.Y;
        }

        #endregion

        #region Object Overrides

        public override int GetHashCode()
        {
            return X.GetHashCode() + Y.GetHashCode() << 16;
        }

        public override bool Equals(object obj)
        {
            return obj != null && obj is Vector2f && this == (Vector2f)obj;
        }
        public bool Equals(Vector2f other)
        {
            return this == other;
        }

        public override string ToString()
        {
            return string.Format("{{X:{0} Y:{1}}", X, Y);
        }

        #endregion

        #region Static Methods

        public static float Dot(Vector2f a, Vector2f b)
        {
            return a.X * b.X + a.Y * b.Y;
        }

        public static Vector2f Normalize(Vector2f vector)
        {
            float length = vector.Length();
            return length == 0 ? Zero : vector / length;
        }

        public static Vector2f Clamp(Vector2f vector, float minimum, float maximum)
        {
            return new Vector2f(
                MathHelper.Clamp(vector.X, minimum, maximum),
                MathHelper.Clamp(vector.Y, minimum, maximum));
        }
        public static Vector2f Clamp(Vector2f vector, Vector2f minimum, Vector2f maximum)
        {
            return new Vector2f(
                MathHelper.Clamp(vector.X, minimum.X, maximum.X),
                MathHelper.Clamp(vector.Y, minimum.Y, maximum.Y));
        }

        public static Vector2f Lerp(Vector2f from, Vector2f to, float step)
        {
            return new Vector2f(MathHelper.Lerp(from.X, to.X, step),
                MathHelper.Lerp(from.Y, to.Y, step));
        }

        public static Vector2f Abs(Vector2f vector)
        {
            return new Vector2f(Math.Abs(vector.X), Math.Abs(vector.Y));
        }

        public static Vector2f Saturate(Vector2f vector)
        {
            return Clamp(vector, Zero, One);
        }

        public static Vector2f Sign(Vector2f vector)
        {
            return new Vector2f(Math.Sign(vector.X), Math.Sign(vector.Y));
        }

        public static Vector2f Min(Vector2f a, Vector2f b)
        {
            return new Vector2f(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y));
        }

        public static Vector2f Max(Vector2f a, Vector2f b)
        {
            return new Vector2f(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
        }

        #endregion

        #region Instance Methods

        public float Dot(Vector2f other)
        {
            return X * other.X + Y * other.Y;
        }

        public void Normalize()
        {
            float length = Length();
            if (length == 0)
                X = Y = 0;
            else
            {
                X /= length;
                Y /= length;
            }
        }

        public float Length()
        {
            return (float)Math.Sqrt(X * X + Y * Y);
        }

        #endregion

        #region Swizzlers

        public Vector2f YX
        {
            get { return new Vector2f(Y, X); }
        }
        public Vector2f XX
        {
            get { return new Vector2f(X, X); }
        }
        public Vector2f YY
        {
            get { return new Vector2f(Y, Y); }
        }
        public Vector3f XXY
        {
            get { return new Vector3f(X, X, Y); }
        }
        public Vector3f XYX
        {
            get { return new Vector3f(X, Y, X); }
        }
        public Vector3f XXX
        {
            get { return new Vector3f(X); }
        }

        #endregion
    }
}
