/*
 * Copyright (C) 1999-2017 Hartmut Seichter
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef PW_CORE_VECTOR_HPP
#define PW_CORE_VECTOR_HPP

#include <pw/core/matrix.hpp>

namespace pw {

template <unsigned int components,typename T>
class vector : public matrix_<components,1,T> {
public:

	using typename matrix_<components,1,T>::value_type;
	using matrix_<components,1,T>::operator = ;

	vector() : matrix_<components,1,T>() {}

	vector(const vector& other) : matrix_<components,1,T>(other) {}

	vector(const matrix_<components,1,T>& other) : matrix_<components,1,T>(other) {}

    T& operator()(unsigned int c) { return matrixbase<T>::at(c); }
    const T& operator()(unsigned int c) const { return matrixbase<T>::at(c); }


    const T dot(const vector<components,T>& other) const {
        T res = 0;
        for (unsigned int i = 0; i < components; i++) res += (*this)(i) * other(i);
        return res;
    }

    const T angle_to(const vector<components,T>& other) const {
        using std::acos;
        vector<components,T> nself(*this); vector<components,T> nothr = other;
        nself.normalize(); nothr.normalize();
        return acos( nothr.dot(nself) );
    }


};


// Vec3 -----------------------------------------------------------------------

template <class T>
class vector3 : public vector<3,T> {
public:

    using vector<3,T>::operator=;

    vector3() : vector<3,T>() {}

    vector3(const vector<3,T>& other) : vector<3, T> (other) {}

    vector3(T c1, T c2, T c3) { this->set(c1,c2,c3); }

    void set(T c1, T c2, T c3) { (*this)(0) = c1; (*this)(1) = c2; (*this)(2) = c3; }

    vector3 clone() const { return vector3(*this); }

    inline void set_x(const T& v) { x() = v; }
    inline void set_y(const T& v) { y() = v; }
    inline void set_z(const T& v) { z() = v; }

    inline const vector3 cross(const vector3<T>& vec2) const {
        return vector3<T>((*this)(1) * vec2(2) - vec2(1) * (*this)(2),
                          (*this)(2) * vec2(0) - vec2(2) * (*this)(0),
                          (*this)(0) * vec2(1) - vec2(0) * (*this)(1));
    }

    inline const T& x() const { return (*this)(0); }
    inline T& x() { return (*this)(0); }

    const T& y() const { return (*this)(1); }
    T& y() { return (*this)(1); }

    const T& z() const { return (*this)(2); }
    T& z() { return (*this)(2); }


    const vector<4,T> project(const T& w) const {
        return vector<4,T>(x(),y(),z(),w);
    }

#if _GCC_STUPID_
    inline std::tuple<T,T,T> values() const {
        return std::make_tuple(x(),y(),z());
    }
#endif


	static vector3<T> forward() { return vector3<T> ( 0, 0,-1); }
	static vector3<T> backward() { return vector3<T>( 0, 0, 1); }
	static vector3<T> right() { return vector3<T>   ( 1, 0, 0); }
	static vector3<T> left() { return vector3<T>    (-1, 0, 0); }
	static vector3<T> up() { return vector3<T>      ( 0, 1, 0); }
	static vector3<T> down() { return vector3<T>    ( 0,-1, 0); }

};



// Vec2x -----------------------------------------------------------------------

template <class T> class vector2 : public vector<2,T> {
public:

    vector2() {}

    vector2(T v1,T v2) {
        this->set(v1,v2);
    }

    void set(T v1,T v2) {
        (*this)(0) = v1; (*this)(1) = v2;
    }

    const T& x() const { return (*this)(0); }
    T& x() { return (*this)(0); }

    const T& y() const { return (*this)(1); }
    T& y() { return (*this)(1); }

};

// Vec4 -----------------------------------------------------------------------

template <typename T> class vector4 : public vector<4,T> {
public:

    using vector<4,T>::vector;

    vector4(const T& v1,const T& v2,const T& v3,const T& v4) {
        this->set(v1,v2,v3,v4);
    }

    void set(const T& v1,const T& v2,const T& v3,const T& v4) {
        (*this)(0) = v1;
        (*this)(1) = v2;
        (*this)(2) = v3;
        (*this)(3) = v4;
    }

    inline const vector3<T> xyz() const { return vector3<T>(x(),y(),z()); }
    inline const vector2<T> xy() const { return vector2<T>(x(),y()); }

    const T& x() const { return (*this)(0); }
    T& x() { return (*this)(0); }

    const T& y() const { return (*this)(1); }
    T& y() { return (*this)(1); }

    const T& z() const { return (*this)(2); }
    T& z() { return (*this)(2); }

    const T& w() const { return (*this)(3); }
    T& w() { return (*this)(3); }



};


typedef vector2<unsigned char> vector2ub;
typedef vector2<char> vector2b;
typedef vector2<unsigned short> vector2us;
typedef vector2<short> vector2s;
typedef vector2<unsigned int> vector2ui;
typedef vector2<int> vector2i;
typedef vector2<unsigned long> vector2ul;
typedef vector2<long> vector2l;

typedef vector2<double> vector2d;
typedef vector2<float> vector2f;




typedef vector3<double> vector3d;
typedef vector3<float> vector3f;
typedef vector3<int> vector3i;
typedef vector3<unsigned int> vector3ui;


typedef vector4<double> vector4d;
typedef vector4<float> vector4f;
typedef vector4<int> vector4i;
typedef vector4<unsigned int> vector4ui;

}


//////////////////////////////////////////////////////////////////////////





#endif
