/*
 * 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


	inline static vector3_<T> forward() { return vector3_<T> ( 0, 0,-1); }
	inline static vector3_<T> backward() { return vector3_<T>( 0, 0, 1); }
	inline static vector3_<T> right() { return vector3_<T>   ( 1, 0, 0); }
	inline static vector3_<T> left() { return vector3_<T>    (-1, 0, 0); }
	inline static vector3_<T> up() { return vector3_<T>      ( 0, 1, 0); }
	inline 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
