/*
 * This source file is part of KEOS (Free 3D Engine)
 * For the latest info, see http://www.keosengine.org/
 * E-mails : thierry.vouriot@keosengine.org, yeri@keosengine.org
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA, or go to
 * http://www.gnu.org/copyleft/lesser.txt.
 *
 */

#ifndef KEOS_VECTOR4_H
#define KEOS_VECTOR4_H

#include "KeosPrerequisites.h"

namespace Keos
{
    //-----------------------------------------------------------------------
    /** Standard 4-dimensional vector with customizable type. 
     * 
     */
    template <class T>
    class CVector4
    {
    public :

        /** Default constructor.
         */
        CVector4(T X = 0, T Y = 0, T Z = 0, T W = 1);

        /** Set vector.
         */
        void Set(T X, T Y, T Z, T W);

        /** Returns the length (magnitude) of the vector (sqrt(x² + y²)).
         */
        T Length() const;

        /** Returns the square of the length(magnitude) of the vector (x² + y²).
         */
        T LengthSq() const;

        /** Normalizes the vector.
         */
        void Normalize();

        // arithmetic operations
        CVector4<T> operator +() const;
        CVector4<T> operator -() const;

        CVector4<T> operator +(const CVector4<T>& v) const;
        CVector4<T> operator -(const CVector4<T>& v) const;

        const CVector4<T>& operator +=(const CVector4<T>& v);
        const CVector4<T>& operator -=(const CVector4<T>& v);

        CVector4<T> operator *(T t) const;
        CVector4<T> operator /(T t) const;

        const CVector4<T>& operator *=(T t);
        const CVector4<T>& operator /=(T t);

        bool operator ==(const CVector4<T>& v) const;
        bool operator !=(const CVector4<T>& v) const;

        /** T* cast operator.
         *  @return Pointer on the vector components.
         */
        operator T*();

        // Vector components
        T x, y, z, w;
    };

    // Globals functions
    template <class T> CVector4<T>   operator * (const CVector4<T>& v, T t);
    template <class T> CVector4<T>   operator / (const CVector4<T>& v, T t);
    template <class T> CVector4<T>   operator * (T t, const CVector4<T>& v);
    template <class T> T             VectorDot  (const CVector4<T>& v1, const CVector4<T>& v2);
    template <class T> CVector4<T>   VectorCross(const CVector4<T>& v1, const CVector4<T>& v2);
    template <class T> std::istream& operator >>(std::istream& Stream, CVector4<T>& Vector);
    template <class T> std::ostream& operator <<(std::ostream& Stream, const CVector4<T>& Vector);

    // Usual types of vectors
    typedef CVector4<float> TVector4F;


    //=======================================================================
    // CVector4 inline functions
    //=======================================================================
    
    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T>::CVector4(T X, T Y, T Z, T W) :
    x(X),
    y(Y),
    z(Z),
    w(W)
    {

    }

    //-----------------------------------------------------------------------
    template <class T>
    inline void CVector4<T>::Set(T X, T Y, T Z, T W)
    {
        x = X;
        y = Y;
        z = Z;
        w = W;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline T CVector4<T>::Length() const
    {
        return std::sqrt(LengthSq());
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline T CVector4<T>::LengthSq() const
    {
        return x * x + y * y + z * z + w * w;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline void CVector4<T>::Normalize()
    {
        T Norm = Length();

        if (std::abs(Norm) > std::numeric_limits<T>::epsilon())
        {
            x /= Norm;
            y /= Norm;
            z /= Norm;
            w /= Norm;
        }
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> CVector4<T>::operator +() const
    {
        return this;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> CVector4<T>::operator -() const
    {
        return CVector4<T>(-x, -y, -z, -w);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> CVector4<T>::operator +(const CVector4<T>& v) const
    {
        return CVector4<T>(x + v.x, y + v.y, z + v.z, w + v.w);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> CVector4<T>::operator -(const CVector4<T>& v) const
    {
        return CVector4<T>(x - v.x, y - v.y, z - v.z, w - v.w);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline const CVector4<T>& CVector4<T>::operator +=(const CVector4<T>& v)
    {
        x += v.x;
        y += v.y;
        z += v.z;
        w += v.w;

        return *this;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline const CVector4<T>& CVector4<T>::operator -=(const CVector4<T>& v)
    {
        x -= v.x;
        y -= v.y;
        z -= v.z;
        w -= v.w;

        return *this;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> CVector4<T>::operator *(T t) const
    {
        return CVector4<T>(x * t, y * t, z * t, w * t);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> CVector4<T>::operator /(T t) const
    {
        return CVector4<T>(x / t, y / t, z / t, w / t);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline const CVector4<T>& CVector4<T>::operator *=(T t)
    {
        x *= t;
        y *= t;
        z *= t;
        w *= t;

        return *this;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline const CVector4<T>& CVector4<T>::operator /=(T t)
    {
        x /= t;
        y /= t;
        z /= t;
        w /= t;

        return *this;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline bool CVector4<T>::operator ==(const CVector4<T>& v) const
    {
        return ((std::abs(x - v.x) <= std::numeric_limits<T>::epsilon()) &&
                (std::abs(y - v.y) <= std::numeric_limits<T>::epsilon()) &&
                (std::abs(z - v.z) <= std::numeric_limits<T>::epsilon()) &&
                (std::abs(w - v.w) <= std::numeric_limits<T>::epsilon()));
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline bool CVector4<T>::operator !=(const CVector4<T>& v) const
    {
        return !(*this == v);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T>::operator T*()
    {
        return &x;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> operator *(const CVector4<T>& v, T t)
    {
        return CVector4<T>(v.x * t, v.y * t, v.z * t, v.w * t);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> operator /(const CVector4<T>& v, T t)
    {
        return CVector4<T>(v.x / t, v.y / t, v.z / t, v.w / t);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> operator *(T t, const CVector4<T>& v)
    {
        return v * t;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline T VectorDot(const CVector4<T>& v1, const CVector4<T>& v2)
    {
        return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CVector4<T> VectorCross(const CVector4<T>& v1, const CVector4<T>& v2)
    {
        // ?? Correct ??
        return CVector4<T>(v1.y * v2.z - v1.z * v2.y, v1.z * v2.w - v1.w * v2.z, v1.w * v2.x - v1.x * v2.w, v1.x * v2.y - v1.y * v2.x);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline std::istream& operator >>(std::istream& Stream, CVector4<T>& Vector)
    {
        return Stream >> Vector.x >> Vector.y >> Vector.z >> Vector.w;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline std::ostream& operator <<(std::ostream& Stream, const CVector4<T>& Vector)
    {
        return Stream << Vector.x << " " << Vector.y << " " << Vector.z << " " << Vector.w;
    }


} // namespace Keos


#endif // KEOS_VECTOR4_H
