using System;
using MTV3D65;

namespace OctreeRendering
{
    struct Vector3i : IEquatable<Vector3i>
    {
        public int X, Y, Z;

        public Vector3i(int c)
        {
            X = Y = Z = c;
        }
        public Vector3i(int x, int y, int z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        #region Constants

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

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

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

        #endregion

        #region Operators

        public static Vector3i operator +(Vector3i a, Vector3i b)
        {
            return new Vector3i(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
        }
        public static Vector3i operator -(Vector3i a, Vector3i b)
        {
            return new Vector3i(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
        }
        public static Vector3i operator -(Vector3i vector)
        {
            return new Vector3i(-vector.X, -vector.Y, -vector.Z);
        }
        public static Vector3i operator /(Vector3i a, Vector3i b)
        {
            return new Vector3i(a.X / b.X, a.Y / b.Y, a.Z / b.Z);
        }
        public static Vector3i operator /(Vector3i a, int b)
        {
            return new Vector3i(a.X / b, a.Y / b, a.Z / b);
        }
        public static Vector3i operator *(Vector3i a, Vector3i b)
        {
            return new Vector3i(a.X * b.X, a.Y * b.Y, a.Z * b.Z);
        }
        public static Vector3i operator *(Vector3i a, int b)
        {
            return new Vector3i(a.X * b, a.Y * b, a.Z * b);
        }
        public static Vector3i operator *(int a, Vector3i b)
        {
            return new Vector3i(a * b.X, a * b.Y, a * b.Z);
        }
        public static Vector3f operator *(float a, Vector3i b)
        {
            return new Vector3f(a * b.X, a * b.Y, a * b.Z);
        }
        public static Vector3f operator *(Vector3i a, float b)
        {
            return new Vector3f(a.X * b, a.Y * b, a.Z * b);
        }
        public static implicit operator Vector3f(Vector3i vector)
        {
            return new Vector3f(vector.X, vector.Y, vector.Z);
        }
        public static implicit operator TV_3DVECTOR(Vector3i vector)
        {
            return new TV_3DVECTOR(vector.X, vector.Y, vector.Z);
        }
        public static explicit operator Vector3i(Vector3f vector)
        {
            return new Vector3i((int)vector.X, (int)vector.Y, (int)vector.Z);
        }
        public static bool operator ==(Vector3i a, Vector3i b)
        {
            return a.X == b.X && a.Y == b.Y && a.Z == b.Z;
        }
        public static bool operator !=(Vector3i a, Vector3i b)
        {
            return a.X != b.X || a.Y != b.Y || a.Z != b.Z;
        }

        #endregion

        #region Object Overrides

        public override int GetHashCode()
        {
            return X + Y << 10 + Z << 20;
        }

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

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

        #endregion

        #region Static Methods

        public static int Dot(Vector3i a, Vector3i b)
        {
            return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
        }

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

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

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

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

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

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

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

        #endregion

        #region Instance Methods

        public int Dot(Vector3i other)
        {
            return X * other.X + Y * other.Y + Z * other.Z;
        }

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

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

        #endregion

        #region Swizzlers

        public Vector3i XZY
        {
            get { return new Vector3i(X, Z, Y); }
        }
        public Vector3i YXZ
        {
            get { return new Vector3i(Y, X, Z); }
        }
        public Vector3i YZX
        {
            get { return new Vector3i(Y, Z, X); }
        }
        public Vector3i ZYX
        {
            get { return new Vector3i(Z, Y, X); }
        }
        public Vector3i ZXY
        {
            get { return new Vector3i(Z, X, Y); }
        }

        #endregion
    }
}
