
#ifndef SPECTRE_MATH_MATRIX4_H
#define SPECTRE_MATH_MATRIX4_H

// 4x4 matrix implementation.

#include <string>
#include <iostream>
#include <Spectre/Math/Vector3.h>
#include <Spectre/Math/Vector2.h>

template<typename T>
struct Matrix4
{
	// Column-major order are used for the matrices here to be compatible with OpenGL.
	union {
		T m[4][4]; // format: [col][row]

		/* Indicies to use for accessing elements.
			_          _
			|           |
			| 0 4 8  12 |
			| 1 5 9  13 |
			| 2 6 10 14 |
			| 3 7 11 15 |
			|_         _|
		*/
		T e[16];
	};

	static const Matrix4<T> Identity;

	inline Matrix4();
	inline Matrix4(
		T a00, T a01, T a02, T a03,
		T a10, T a11, T a12, T a13,
		T a20, T a21, T a22, T a23,
		T a30, T a31, T a32, T a33);

	inline Matrix4(T *a);

	// -------------------
	//  Copy & Assignment
	// -------------------

	template <typename U>
	inline Matrix4(const Matrix4<U>& mat);

	inline Matrix4<T>& operator=(const Matrix4<T>& mat);

	// ------------------
	//  Named operations
	// ------------------

	// Transpose matrix.
	Matrix4<T> transpose() const;

	std::string toString() const;
};

// -----------------
// Compare
// -----------------

template <typename T>
inline bool operator==(const Matrix4<T>& a, const Matrix4<T>& b);
template <typename T>
inline bool operator!=(const Matrix4<T>& a, const Matrix4<T>& b);

// --------------------------
// Arithmetic: Matrix-Matrix
// --------------------------

template <typename T>
inline Matrix4<T> operator*(const Matrix4<T>& a, const Matrix4<T>& b);
template <typename T>
inline Matrix4<T>& operator*=(Matrix4<T>& a, const Matrix4<T>& b);

// --------------------------
// Arithmetic: Matrix-Vector
// --------------------------

template <typename T>
inline Vector3<T> operator*(const Matrix4<T>& mat, const Vector3<T>& vec);

template <typename T>
inline std::ostream& operator<<(std::ostream &s, const Matrix4<T>& mat);

// -----------------------------
//  Common specialization types
// -----------------------------

typedef Matrix4<float>		Matrix4f;
typedef Matrix4<double>		Matrix4d;
typedef Matrix4<int>		Matrix4i;
typedef Matrix4<unsigned>	Matrix4u;

typedef Matrix4<float>		mat4f;
typedef Matrix4<double>		mat4d;
typedef Matrix4<int>		mat4i;
typedef Matrix4<unsigned>	mat4u;

// ----------------
//  Implementation
// ----------------

#include "Matrix4.inl"

#endif /* SPECTRE_MATH_MATRIX4_H */