#ifndef VECTOR_H
#define VECTOR_H

#include <cmath>

#include "common/utils/String.h"
#include "client/math/Matrix.h"

template<uint N>
struct Vector final {

    Vector() {
        for(uint i = 0; i < N; i++) {
            values[i] = 0.0f;
        }
    }

    Vector(float, float) = delete;
    Vector(float, float, float) = delete;
    Vector& set(float, float, float) = delete;
    Vector& setAngles(float, float) = delete;
    Vector cross(const Vector&) const = delete;

    Vector& operator+=(const Vector& other) {
        for(uint i = 0; i < N; i++) {
            values[i] += other.values[i];
        }
        return *this;
    }

    Vector operator+(const Vector& other) const {
        Vector v = *this;
        v += other;
        return v;
    }

    Vector& operator-=(const Vector& other) {
        for(uint i = 0; i < N; i++) {
            values[i] -= other.values[i];
        }
        return *this;
    }

    Vector operator-() const {
        Vector v = *this;
        for(uint i = 0; i < N; i++) {
            v.values[i] = -v.values[i];
        }
        return v;
    }

    Vector operator-(const Vector& other) const {
        Vector v = *this;
        v -= other;
        return v;
    }

    Vector& operator*=(float factor) {
        for(uint i = 0; i < N; i++) {
            values[i] *= factor;
        }
        return *this;
    }

    Vector operator*(float factor) const {
        Vector v = *this;
        v *= factor;
        return v;
    }

    float dot(const Vector& v) const {
        float length = 0.0f;
        for(uint i = 0; i < N; i++) {
            length += values[i] * v.values[i];
        }
        return length;
    }

    float squareLength() const {
        return dot(*this);
    }

    float length() const {
        return sqrtf(squareLength());
    }

    Vector& normalize() {
        *this *= 1.0f / length();
        return *this;
    }

    const float& operator[](uint index) const {
        return values[index];
    }

private:
    float values[N];
};

template<uint N>
Vector<N> operator*(float factor, const Vector<N>& v) {
    return v * factor;
}

template<> Vector<3>::Vector(float x, float y, float z);
template<> Vector<2>::Vector(float x, float y);
template<> Vector<3>& Vector<3>::setAngles(float lengthAngle, float widthAngle);
template<> Vector<3>::Vector(float lengthAngle, float widthAngle);
template<> Vector<3>& Vector<3>::set(float x, float y, float z);
template<> Vector<3> Vector<3>::cross(const Vector<3>& other) const;
Vector<3> operator*(const Matrix& m, const Vector<3>& v);

template<uint N>
String& operator+=(String& buffer, const Vector<N>& v) {
    buffer.append('(');
    if(N > 0) {
        buffer.append(v[0]);
    }
    for(uint i = 1; i < N; i++) {
        buffer.append(", ").append(v[i]);
    }
    buffer.append(')');
    return buffer;
}

typedef Vector<3> Vector3;
typedef Vector<2> Vector2;

#endif