//------------------------------------------------------------------------------
// Author:		Gero Mueller <gero.mueller@cloo.de>
// Copyright:	(c) 2006 Gero Mueller
// License:		MIT License
//------------------------------------------------------------------------------

#ifndef BLUECORE_VECTOR_H
#define BLUECORE_VECTOR_H

// system includes
#include <cmath>
#include <ostream>

// project includes
#include "Scalar.h"

namespace BlueCore
{
	template <class T>
	class Vector3Template
	{
	public:
	
		T x, y, z;

		/**
		 * Default Contructor
		 */
		inline Vector3Template()
		{
			x = static_cast<T>(0);
			y = static_cast<T>(0);
			z = static_cast<T>(0);
		}
		
		/**
		 * Constructor from three values
		 */
		template <class S>
		inline Vector3Template( S x, S y, S z )
		{
			this->x = static_cast<T>(x);
			this->y = static_cast<T>(y);
			this->z = static_cast<T>(z);
		}

		/**
		 * copy contructor
		 */
		template <class S>
		inline Vector3Template( Vector3Template<S> &a )
		{
			x = static_cast<T>(a.x);
			y = static_cast<T>(a.y);
			z = static_cast<T>(a.z);
		}
		

		/**
		 * add operataor
		 */
		template <class S>
		inline Vector3Template<T> operator + (
			const Vector3Template<S> &a ) const
		{
			return Vector3Template<T>(
				x + static_cast<T>(a.x),
				y + static_cast<T>(a.y),
				z + static_cast<T>(a.z) );
		}


		/**
		 * zero the vector
		 */
		inline void zero()
		{
			x = static_cast<T>(0);
			y = static_cast<T>(0);
			z = static_cast<T>(0);
		}

		/**
		 * zero the vector
		 */
		inline bool isZero() const
		{
			if( x != 0.0 )
				return false; 

			if( y != 0.0 )
				return false; 

			if( z != 0.0 )
				return false;
				
			return true; 
		}


		/**
		 * add-assign operator
		 */
		template <class S>
		inline Vector3Template<T> &operator += (
			const Vector3Template<S> &a )
		{
			x += static_cast<T>(a.x);
			y += static_cast<T>(a.y);
			z += static_cast<T>(a.z);
	
			return *this;
		}	


		/**
		 * sub operator
		 */
		template <class S>
		inline Vector3Template<T> operator - (
			const Vector3Template<S> &a ) const
		{
			return Vector3Template<T>(
				x - static_cast<T>(a.x),
				y - static_cast<T>(a.y),
				z - static_cast<T>(a.z)
			);
		}


		/**
		 * sub-assign operator
		 */
		template <class S>
		inline Vector3Template<T> &operator -= (
			const Vector3Template<S> &a )
		{
			x -= static_cast<T>(a.x);
			y -= static_cast<T>(a.y);
			z -= static_cast<T>(a.z);
	
			return *this;
		}	


		/**
		 * multiply by scalar operator
		 */
		template <class S>
		inline Vector3Template<T> operator * ( const S a ) const
		{
			return Vector3Template<T>(
				x * static_cast<T>(a),
				y * static_cast<T>(a),
				z * static_cast<T>(a)
			);
		}


		/**
		 * multiply-assign by scalar operator
		 */
		template <class S>
		inline Vector3Template<T> &operator *= ( const S a )
		{
			x *= static_cast<T>(a);
			y *= static_cast<T>(a);
			z *= static_cast<T>(a);
	
			return *this;
		}	


		/**
		 * divide by scalar operator
		 */
		template <class S>
		inline Vector3Template<T> operator / ( const S a ) const
		{
			return Vector3Template<T>(
				x / static_cast<T>(a),
				y / static_cast<T>(a),
				z / static_cast<T>(a)
			);
		}


		/**
		 * divide-assign by scalar operator
		 */
		template <class S>
		inline Vector3Template<T> &operator /= ( const S a )
		{
			x /= static_cast<T>(a);
			y /= static_cast<T>(a);
			z /= static_cast<T>(a);
	
			return *this;
		}	


		/**
		 * assign operator
		 */
		template <class S>
		inline Vector3Template<T> operator = (
			const Vector3Template<S> &a )
		{
			x = static_cast<T>(a.x);
			y = static_cast<T>(a.y);
			z = static_cast<T>(a.z);
	
			return *this;
		}

		inline Vector3Template<T> operator - (void) const
		{
			return Vector3Template<T>( -x, -y, -z );
		}

		/**
		 * compare operator
		 */
		template <class S>
		inline bool operator == ( const Vector3Template<S> &a )
		{
			if( x != static_cast<T>(a.x) )
				return false;
				
			if( y != static_cast<T>(a.y) )
				return false;

			if( z != static_cast<T>(a.z) )
				return false;
	
			return true;
		}
		

		/**
		 * anti compare operator
		 */
		template <class S>
		inline bool operator != ( const Vector3Template<S> &a )
		{
			if( x != static_cast<T>(a.x) )
				return true;
				
			if( y != static_cast<T>(a.y) )
				return true;

			if( z != static_cast<T>(a.z) )
				return true;
	
			return false;
		}


		/**
		 * return the length of the vector
		 */
		inline T length() const
		{
			return static_cast<T>( sqrt(x*x + y*y + z*z) );
		}
		

		/**
		 * length squared
		 */
		inline T length2() const
		{
			return x*x + y*y + z*z;
		}
	

		/**
		 * calculate the cross product
		 */
		template <class S>
		inline Vector3Template<T> cross(
			const Vector3Template<S> &a ) const
		{
			return Vector3Template<T>(
				y * static_cast<T>(a.z) - z * static_cast<T>(a.y),
				z * static_cast<T>(a.x) - x * static_cast<T>(a.z),
				x * static_cast<T>(a.y) - y * static_cast<T>(a.x)
			);
		}
	

		/**
		 * calculate dot product
		 */
		template <class S>
		inline T dot( const Vector3Template<S> &a ) const
		{
			return (
				x * static_cast<T>(a.x) +
				y * static_cast<T>(a.y) +
				z * static_cast<T>(a.z) );
		}
	

		/**
		 * check if vectors are nearly equal
		 */
		template <class S>
		inline bool nearlyEquals(
			const Vector3Template<S> &a,
			T epsilon = 0.00001 ) const
		{
			if( fabs(x-static_cast<T>(a.x)) > epsilon )
				return false;

			if( fabs(y-static_cast<T>(a.y)) > epsilon )
				return false;

			if( fabs(z-static_cast<T>(a.z)) > epsilon )
				return false;
			
			return true;
		}
	

		/**
		 * normalize the vector to length 1
		 */
		inline void normalize()
		{
			T a = length();
			if( a == 0 )
				return;
				
			x /= a;
			y /= a;
			z /= a;
		}

	
		/**
		 * get normalized vector
		 */
		inline Vector3Template<T> normalized() const
		{
			T a = length();
			if( a == 0 )
				return Vector3Template<T>( 0.0, 0.0, 1.0 );
			return Vector3Template<T>( x / a, y / a, z / a );
		}

		/**
		 * check if vectors are parallel
		 */
		inline bool parallel( const Vector3Template<T> &a ) const
		{
			return ( (static_cast<T>(a.x) / x) ==
				(static_cast<T>(a.y) / y) ==
				(static_cast<T>(a.z) / z) );
		}


		/**
		 * check if vectors are parallel
		 */
		template <class S>
		inline bool nearlyParallel(
			const Vector3Template<S> &a,
			T epsilon = 0.00001 ) const
		{
			T ax = static_cast<T>(a.x) / x;
			T ay = static_cast<T>(a.y) / y;
			T az = static_cast<T>(a.z) / z;

			if( fabs(ax - ay) > epsilon	)
				return false;

			if( fabs(ax - az) > epsilon	)
				return false;

			if( fabs(ay - az) > epsilon	)
				return false;

			return true;
		}

		/**
		 * check if vectors are orthogonal
		 */
		inline bool orthogonal( const Vector3Template<T> &a ) const
		{
			return ( dot(a) == static_cast<T>(0) );
		}


		/**
		 * check if vectors are orthogonal
		 */
		template <class S>
		inline bool nearlyOrthogonal(
			const Vector3Template<S> &a,
			T epsilon = 0.00001 ) const
		{
			return ( dot(a) < epsilon );
		}		

		T& operator[] (unsigned int i)
		{
			if( i == 0 )
				return x;
			else if( i == 1 )
				return y;
			else if( i == 2 )
				return z;
			else
				throw "index out of bound";
		}

		friend std::ostream &operator << ( std::ostream& os, Vector3Template<T> v )
		{
			os << "( " << v.x << ", " << v.y << ", " << v.z << " )";
			return os;
		}

	};
	
	typedef Vector3Template<float> Vector3Float;
	typedef Vector3Template<double> Vector3Double;
	typedef Vector3Template<Scalar> Vector3;
}

#endif
