using System;

namespace Icarus.Logic.Physics 
{
    
	/// <summary>
	/// I contain the vectors used in Physics Calculations
	///  
	/// Don't mess with me.
	/// </summary>
	public class Vector 
	{
        
		#region Declarations
        
		private float ival;
		private float jval;
		private float kval;

		#endregion

		#region Fields

		/// <summary>
		/// i direction value
		/// </summary>
		public float i 
		{
			get { return ival; }
			set { ival = value;}
		}

		/// <summary>
		/// j direction value
		/// </summary>
		public float j 
		{
			get { return jval; } 
			set { jval = value;}
		}

		/// <summary>
		/// k direction value
		/// </summary>
		public float k 
		{
			get { return kval; }
			set { kval = value;}
		}

		/// <summary>
		/// Magnitude of Vector
		/// </summary>
		public float magnitude 
		{
			get { return getMagnitude(); }
		}

		#endregion

		#region Methods

		/// <summary>
		/// Print Vector Information
		/// </summary>
		public void PrintVector() 
		{   
			Console.WriteLine(this.ToString());
		}

		/// <summary>
		/// Returns a string containing the vector information
		/// </summary>
		/// <returns>A string with the vector information</returns>
		public override string ToString() 
		{
			return ("Vector:\n" +
				"  i = " + this.i +
				"  j = " + this.j +
				"  k = " + this.k);      
		}

		/// <summary>
		/// Calculate the magnitude of this vector
		/// </summary>
		private float getMagnitude() 
		{
			return (float) Math.Sqrt(
				Math.Pow(this.i,2) +
				Math.Pow(this.j,2) +
				Math.Pow(this.k,2));
		}   

		#endregion

		#region Vector Algebra Methods

		/// <summary>
		/// Negates the vector (i.e. -1 * v)
		/// </summary>
		/// <param name="v">the vector to be negated</param>
		/// <returns>Negated Vector of v</returns>
		public static Vector operator -(Vector v) 
		{
			Vector dest = new Vector();
            
			dest.i = -v.i;
			dest.j = -v.j;
			dest.k = -v.k;

			return dest;
		}

		/// <summary>
		/// Returns true if magnitude(v1) >= magnitude(v2)
		/// </summary>
		/// <param name="v1">v1</param>
		/// <param name="v2">v2</param>
		/// <returns>magnitude(v1) >= magnitude(v2)</returns>
		public static bool operator >= (Vector v1, Vector v2) 
		{
			return ( v1.magnitude >= v2.magnitude );
		}

		/// <summary>
		/// Returns true if magnitude(v1) &lt;= magnitude(v2)
		/// </summary>
		/// <param name="v1">v1</param>
		/// <param name="v2">v2</param>
		/// <returns>magnitude(v1) &lt;= magnitude(v2)</returns>
		public static bool operator <= (Vector v1, Vector v2) 
		{
			return ( (v1.i <= v2.i) || (v1.j <= v2.j) );
		}
        
		/// <summary>
		/// Returns true if magnitude(v1) &lt; magnitude(v2)
		/// </summary>
		/// <param name="v1">v1</param>
		/// <param name="v2">v2</param>
		/// <returns>magnitude(v1) &lt; magnitude(v2)</returns>
		public static bool operator < (Vector v1, Vector v2) 
		{
			return ( (v1.i < v2.i) || (v1.j < v2.j) );
		}
    
		/// <summary>
		/// Returns true if magnitude(v1) > magnitude(v2)
		/// </summary>
		/// <param name="v1">v1</param>
		/// <param name="v2">v2</param>
		/// <returns>magnitude(v1) > magnitude(v2)</returns>
		public static bool operator > (Vector v1, Vector v2) 
		{
			return ( (v1.i > v2.i) || (v1.j > v2.j) );
		}


        /// <summary>
        /// Performs cross multiplication from vec1 to vec2
        /// </summary>
        /// <param name="vec1">First Vector</param>
        /// <param name="vec2">Second Vector</param>
        /// <param name="cross">Normal vector of first two vectors</param>
        public static void CrossMultVector(Vector vec1, Vector vec2, Vector cross) 
        {
    
            cross.i = ( (vec1.j)*(vec2.k) ) - ( (vec1.k)*(vec2.j) ) ; 
            cross.j = ( (vec1.k)*(vec2.i) ) - ( (vec1.i)*(vec2.k) ) ; 
            cross.k = ( (vec1.i)*(vec2.j) ) - ( (vec1.j)*(vec2.i) ) ; 

        }

		/// <summary>
		/// Scalar Multiply a Vector
		/// dest = (src.i * scalar)i + (src.j * scalar)j + ...
		/// </summary>
		/// <param name="dest">The Vector in which the result will be stored</param>
		/// <param name="src">The Vector that will be multiplied</param>
		/// <param name="scalar">The scalar value to multiply the Vector with</param>
		public static void ScalarMultVector(Vector dest, Vector src, float scalar) 
		{
			dest.i = src.i * scalar;
			dest.j = src.j * scalar;
			dest.k = src.k * scalar;
		}

		/// <summary>
		/// Vector Addition
		/// </summary>
		/// <param name="src1">src1</param>
		/// <param name="src2">src2</param>
		/// <returns>src1+src2 = (src1.i + src2.i) * i + ...</returns>
		public static Vector operator + (Vector src1, Vector src2) 
		{
			Vector dest = new Vector();
			AddVectors(dest,src1,src2);
			return dest;
		}
        
		/// <summary>
		/// Vector Subtraction
		/// </summary>
		/// <param name="src1">src1</param>
		/// <param name="src2">src2</param>
		/// <returns>src1+src2 = (src1.i + src2.i) * i + ...</returns>
		public static Vector operator - (Vector src1, Vector src2) 
		{
			Vector dest = new Vector();
			SubVectors(dest,src1,src2);
			return dest;
		}            

		/// <summary>
		/// Vector Addition, fastest method to use if dest = src1 or src2
		/// </summary>
		/// <param name="dest">dest = (src1.i + src2.i) * i + ...</param>
		/// <param name="src1">src1</param>
		/// <param name="src2">src2</param>
		public static void AddVectors(Vector dest, Vector src1, Vector src2) 
		{
			dest.i = src1.i + src2.i;
			dest.j = src1.j + src2.j;
			dest.k = src1.k + src2.k;
		}

		/// <summary>
		/// Vector Subtraction, fastest method to use if dest = src1 or src2
		/// </summary>
		/// <param name="dest">dest = (src1.i - src2.i) * i + ...</param>
		/// <param name="src1">src1</param>
		/// <param name="src2">src2</param>
		public static void SubVectors(Vector dest, Vector src1, Vector src2) 
		{
			dest.i = src1.i - src2.i;
			dest.j = src1.j - src2.j;
			dest.k = src1.k - src2.k;
		}

		/// <summary>
		/// Normalize the vector src (src / magnitude(src))
		/// </summary>
		/// <param name="src">Vector to normalize, this is unaffected</param>
		/// <returns>Normalized Vector</returns>
		public static Vector Normalize(Vector src) 
		{
			Vector dest = new Vector();
			float mag = src.magnitude;

			dest.i = src.i / mag;
			dest.j = src.j / mag;
			dest.k = src.k / mag;

			return dest;
		}

		#endregion
                   
	}
}
