/*
 * =====================================================================================
 *
 *       Filename:  jMatrix.h
 *
 *    Description:  Various matrix classes
 *
 *        Version:  1.0
 *        Created:  31/05/2011 22:00:42
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  John Ryland (jryland), jryland@xiaofrog.com
 *        Company:  InvertedLogic
 *
 * =====================================================================================
 */

#ifndef __J_MATRIX_H__
#define __J_MATRIX_H__


#include <jTypes.h>
#include <jMath.h>
#include <stdlib.h>
#include <string.h>


template <typename T, size_t dimensions>
class jVector
{
public:
    /// default constructor
    jVector();
    /// copy construtor
    jVector(const jVector<T, dimensions>& a_vector);

    /// comparison operators
    bool operator==(const jVector<T, dimensions> &a_vector) const;
    bool operator!=(const jVector<T, dimensions> &a_vector) const;

    /// assignment operator
    jVector<T, dimensions>& operator =(const jVector<T, dimensions>& a_vector);

    /// operators with objects of the same type
    jVector<T, dimensions>& operator+=(const jVector<T, dimensions>& a_vector);
    jVector<T, dimensions>& operator-=(const jVector<T, dimensions>& a_vector);
    jVector<T, dimensions>& operator*=(const jVector<T, dimensions>& a_vector);
    jVector<T, dimensions>& operator/=(const jVector<T, dimensions>& a_vector);
    const jVector<T, dimensions> operator+(const jVector<T, dimensions> &a_vector) const;
    const jVector<T, dimensions> operator-(const jVector<T, dimensions> &a_vector) const;
    const jVector<T, dimensions> operator*(const jVector<T, dimensions> &a_vector) const;
    const jVector<T, dimensions> operator/(const jVector<T, dimensions> &a_vector) const;

    /// operators with objects of the type of the components
    jVector<T, dimensions>& operator+=(const T& a_val);
    jVector<T, dimensions>& operator-=(const T& a_val);
    jVector<T, dimensions>& operator*=(const T& a_val);
    jVector<T, dimensions>& operator/=(const T& a_val);
    const jVector<T, dimensions> operator+(const T &a_val) const;
    const jVector<T, dimensions> operator-(const T &a_val) const;
    const jVector<T, dimensions> operator*(const T &a_val) const;
    const jVector<T, dimensions> operator/(const T &a_val) const;

    T sumComponents() const;
    T dotProduct(const jVector<T, dimensions> &a_vector) const;
    T length() const;
    void normalize();
    jVector<T, dimensions>& normalized() const;

    T components[dimensions];
};


typedef jVector<jInt32,2>       jVector2i;
typedef jVector<jFloat32,3>     jVector3f;
typedef jVector<jVector3f,3>    jMatrix3x3f;


template <typename T, size_t dimensions>
inline jVector<T, dimensions>::jVector()
{
    memset(components, 0, sizeof(T)*dimensions);
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>::jVector(const jVector<T, dimensions>& a_vector)
{
    *this = a_vector;
    /*
    m_x = a_vector.m_x;
    m_y = a_vector.m_y;
    m_z = a_vector.m_z;
    */
}


template <typename T, size_t dimensions>
inline bool jVector<T, dimensions>::operator==(const jVector<T, dimensions> &a_vector) const
{
    for (unsigned int i = 0; i < dimensions; i++)
    {
        if ( components[i] != a_vector.components[i] )
        {
            return false;
        }
    }
    return true;
}


template <typename T, size_t dimensions>
inline bool jVector<T, dimensions>::operator!=(const jVector<T, dimensions> &a_vector) const
{
    return !(*this == a_vector);
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>& jVector<T, dimensions>::operator=(const jVector<T, dimensions>& a_vector)
{
    // don't do assignment if same object
    if (this != &a_vector)
    {
        // Deallocate, allocate new space, copy values...
        for (unsigned int i = 0; i < dimensions; i++)
        {
            components[i] = a_vector.components[i];
        }
    }
    return *this;
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>& jVector<T, dimensions>::operator+=(const jVector<T, dimensions>& a_vector)
{
    for (unsigned int i = 0; i < dimensions; i++)
    {
        components[i] += a_vector.components[i];
    }
    return *this;
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>& jVector<T, dimensions>::operator-=(const jVector<T, dimensions>& a_vector)
{
    for (unsigned int i = 0; i < dimensions; i++)
    {
        components[i] -= a_vector.components[i];
    }
    return *this;
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>& jVector<T, dimensions>::operator*=(const jVector<T, dimensions>& a_vector)
{
    for (unsigned int i = 0; i < dimensions; i++)
    {
        components[i] *= a_vector.components[i];
    }
    return *this;
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>& jVector<T, dimensions>::operator/=(const jVector<T, dimensions>& a_vector)
{
    for (unsigned int i = 0; i < dimensions; i++)
    {
        components[i] /= a_vector.components[i];
    }
    return *this;
}


template <typename T, size_t dimensions>
inline const jVector<T, dimensions> jVector<T, dimensions>::operator+(const jVector<T, dimensions> &a_vector) const
{
    return jVector<T, dimensions>(*this) += a_vector;
}


template <typename T, size_t dimensions>
inline const jVector<T, dimensions> jVector<T, dimensions>::operator-(const jVector<T, dimensions> &a_vector) const
{
    return jVector<T, dimensions>(*this) -= a_vector;
}


template <typename T, size_t dimensions>
inline const jVector<T, dimensions> jVector<T, dimensions>::operator*(const jVector<T, dimensions> &a_vector) const
{
    return jVector<T, dimensions>(*this) *= a_vector;
}


template <typename T, size_t dimensions>
inline const jVector<T, dimensions> jVector<T, dimensions>::operator/(const jVector<T, dimensions> &a_vector) const
{
    return jVector<T, dimensions>(*this) /= a_vector;
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>& jVector<T, dimensions>::operator+=(const T& a_val)
{
    for (unsigned int i = 0; i < dimensions; i++)
    {
        components[i] += a_val;
    }
    return *this;
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>& jVector<T, dimensions>::operator-=(const T& a_val)
{
    for (unsigned int i = 0; i < dimensions; i++)
    {
        components[i] -= a_val;
    }
    return *this;
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>& jVector<T, dimensions>::operator*=(const T& a_val)
{
    for (unsigned int i = 0; i < dimensions; i++)
    {
        components[i] *= a_val;
    }
    return *this;
}


template <typename T, size_t dimensions>
inline jVector<T, dimensions>& jVector<T, dimensions>::operator/=(const T& a_val)
{
    for (unsigned int i = 0; i < dimensions; i++)
    {
        components[i] /= a_val;
    }
    return *this;
}


// Specialization for jVector3f. Would prefer this could be a specialization
// for any dimensions when T is a float
template <>
inline jVector3f& jVector3f::operator/=(const jFloat32& a_val)
{
    jFloat32 one_over_val = 1.0f / a_val;
    for (unsigned int i = 0; i < 3; i++)
    {
        components[i] *= one_over_val;
    }
    return *this;
}


template <typename T, size_t dimensions>
inline const jVector<T, dimensions> jVector<T, dimensions>::operator+(const T &a_val) const
{
    return jVector<T, dimensions>(*this) += a_val;
}


template <typename T, size_t dimensions>
inline const jVector<T, dimensions> jVector<T, dimensions>::operator-(const T &a_val) const
{
    return jVector<T, dimensions>(*this) -= a_val;
}


template <typename T, size_t dimensions>
inline const jVector<T, dimensions> jVector<T, dimensions>::operator*(const T &a_val) const
{
    return jVector<T, dimensions>(*this) *= a_val;
}


template <typename T, size_t dimensions>
inline const jVector<T, dimensions> jVector<T, dimensions>::operator/(const T &a_val) const
{
    return jVector<T, dimensions>(*this) /= a_val;
}


template <typename T, size_t dimensions>
inline T jVector<T, dimensions>::sumComponents() const
{
    T sum;
    for (unsigned int i = 0; i < dimensions; i++)
    {
        sum += components[i];
    }
    return sum;
}


template <typename T, size_t dimensions>
inline T jVector<T, dimensions>::dotProduct(const jVector<T, dimensions> &a_vector) const
{
    jVector<T, dimensions> vec;
    vec = *this * a_vector;
    return vec.sumComponents();
}


template <typename T, size_t dimensions>
inline T jVector<T, dimensions>::length() const
{
    return jMath<T>::squareRoot(dotProduct(*this));
}


template <typename T, size_t dimensions>
void jVector<T, dimensions>::normalize()
{
    jVector<T, dimensions>(*this) /= length();
}


template <typename T, size_t dimensions>
jVector<T, dimensions>& jVector<T, dimensions>::normalized() const
{
    return jVector<T, dimensions>(*this) /= length();
}


#endif // __J_VECTOR_H__

