
#ifndef __WFX_MATH_VECTOR3_H__
#define __WFX_MATH_VECTOR3_H__

#include <WFX/WFXIncludes.h>
#include <WFX/math/Math.h>

#include <WFX/math/Vector2.h>

namespace WFX {

    namespace math {

        /**
         * standard 3 element vector
         * does not extend object because we want the data to be packed tightly
         */
        class Vector3 {
            public:
                /** static members */
                static const Vector3 UNITX;
                static const Vector3 UNITY;
                static const Vector3 UNITZ;
                static const Vector3 ZERO;

                /**
                 * elements
                 */
                Real X,Y,Z;

                /**
                 * Ctor/Dtor
                 */
                Vector3();
                Vector3(Real x, Real y, Real z);
                Vector3(const Vector3& v);
                Vector3(const Vector2& v);
                ~Vector3() {};

                void init(Real x, Real y, Real z);

                /**
                 * prints some debug info...
                 */
                const void printDebug() const {
                    LOG(" |" << X << "| |" << Y << "| |" << Z << "|");
                }
                
                /** 
                 * takes a string and tries to parse it
                 * values are whitespace seperated
                 */
                void parseStr(std::string str);

                inline Real operator [](UInt i ) const {
                    assert( i < 3 );
                    return *(&X+i);
                }

                inline Real& operator [](UInt i) {
                    assert( i < 3 );
                    return *(&X+i);
                }

                inline Vector3& operator =(const Vector3& v) {
                    X = v.X;
                    Y = v.Y;
                    Z = v.Z;
                    return *this;
                }

                inline bool operator ==(const Vector3& v) const {
                    return (X == v.X && Y == v.Y && Z == v.Z);
                }

                inline bool operator !=(const Vector3& v) const {
                    return (X != v.X || Y != v.Y || Z != v.Z);
                }

                inline Vector3 operator +(const Vector3& v) const {
                    Vector3 kSum;
                    kSum.X = X + v.X;
                    kSum.Y = Y + v.Y;
                    kSum.Z = Z + v.Z;
                    return kSum;
                }

                inline Vector3 operator -(const Vector3& v) const {
                    Vector3 kDiff;
                    kDiff.X = X - v.X;
                    kDiff.Y = Y - v.Y;
                    kDiff.Z = Z - v.Z;
                    return kDiff;
                }

                inline Vector3 operator *(const Vector3& v) const {
                    Vector3 kProd;
                    kProd.X = v.X * X;
                    kProd.Y = v.Y * Y;
                    kProd.Z = v.Z * Z;
                    return kProd;
                }

                inline Vector3 operator /(Real s) const {
                    assert( s != 0.0 );
                    Vector3 kDiv;
                    Real fInv = 1.0 / s;
                    kDiv.X = X * fInv;
                    kDiv.Y = Y * fInv;
                    kDiv.Z = Z * fInv;
                    return kDiv;
                }

                inline Vector3 operator -() const {
                    Vector3 kNeg;
                    kNeg.X = -X;
                    kNeg.Y = -Y;
                    kNeg.Z = -Z;
                    return kNeg;
                }

                inline friend Vector3 operator *(Real s, const Vector3& v) {
                    Vector3 kProd;
                    kProd.X = s * v.X;
                    kProd.Y = s * v.Y;
                    kProd.Z = s * v.Z;
                    return kProd;
                }

                // arithmetic updates
                inline Vector3& operator +=(const Vector3& v) {
                    X += v.X;
                    Y += v.Y;
                    Z += v.Z;
                    return *this;
                }

                inline Vector3& operator -=(const Vector3& v) {
                    X -= v.X;
                    Y -= v.Y;
                    Z -= v.Z;

                    return *this;
                }

                inline Vector3 operator *(Real s) {
                    Vector3 r;
                    r.X = X*s;
                    r.Y = Y*s;
                    r.Z = Z*s;
                    return r;
                }
                
                inline Vector3 operator /(Real s) {
                    Vector3 r;
                    r.X = X/s;
                    r.Y = Y/s;
                    r.Z = Z/s;
                    return r;
                }
                
                inline Vector3& operator *=(Real s) {
                    X *= s;
                    Y *= s;
                    Z *= s;
                    return *this;
                }

                inline Vector3& operator /=(Real s) {
                    assert(s != 0.0 );
                    Real fInv = 1.0 / s;
                    X *= fInv;
                    Y *= fInv;
                    Z *= fInv;
                    return *this;
                }

                /** calls Math::abs on all Members */
                inline void abs() {
                    X = WFX::math::Math::abs(X);
                    Y = WFX::math::Math::abs(Y);
                    Z = WFX::math::Math::abs(Z);
                }
                
                /** returns the length of this vector */
                inline Real length() const {
                    return WFX::math::Math::sqrt(X*X + Y*Y + Z*Z);
                }
                /** returns the squared length of the vector */
                inline Real squaredLength() const {
                    return (X*X + Y*Y + Z*Z);
                }
                
                /** 
                 * returns the squared length of this vector
                 * comes in handy for length comparisons for it doesn't require 
                 * a square root to be computed
                 */
                inline Real squaredRoot() const {
                    return (X*X + Y*Y + Z*Z);
                }

                /**
                 * dot product of this vector with the given one
                 */
                inline Real dot(const Vector3& v) const {
                    return X*v.X + Y*v.Y + Z*v.Z;
                }

                /**
                 * normalize this vector
                 * returns the previous length of this vector
                 */
                inline Real normalize() {
                    Real l = length();
                    if (l > 1e-06 ) {
                        Real infl = 1.0 / l;
                        X *= infl;
                        Y *= infl;
                        Z *= infl;
                    }
                    return l;
                }

                /**
                 * returns a normalized copy of this vector
                 */
                inline Vector3 normalizedCopy() const {
                    Vector3 nc = *this;
                    nc.normalize();
                    return nc;
                }

                /**
                 * calculates the cross product of two vectors
                 * returns the perpenticular vector to this and the given vector
                 */
                inline Vector3 cross(const Vector3& v) const {
                    Vector3 cross;
                    cross.X = Y*v.Z - Z*v.Y;
                    cross.Y = Z*v.X - X*v.Z;
                    cross.Z = X*v.Y - Y*v.X;
                    return cross;
                }



               
            protected:
                
                
        };

    }

}


#endif //include guard


