/************************************************************************/
/* CaveUTGL                                                             */
/* Willem de Jonge (willem@argcargv.com)                                */
/* 2003                                                                 */
/************************************************************************/

#ifndef MATRIXSTACK_H
#define MATRIXSTACK_H

#if defined( _WIN32 )
#include <windows.h>
#endif
#if defined( __APPLE__ )
#include <OpenGL/gl.h>
#else
#include <gl/gl.h>
#endif

#include <vector>

/*  Matrix4 - general 4x4 matrix class, provides functionality to build opengl
	like matrix stack. Extract far and near planes from UT projection matrix.
	Construct UT frustum matrix.
*/
class Matrix4
{
public:

	Matrix4() {}
	Matrix4(const GLfloat* a) :
	  a00(a[ 0]), a10(a[ 1]), a20(a[ 2]), a30(a[ 3]),
	  a01(a[ 4]), a11(a[ 5]), a21(a[ 6]), a31(a[ 7]),
	  a02(a[ 8]), a12(a[ 9]), a22(a[10]), a32(a[11]),
	  a03(a[12]), a13(a[13]), a23(a[14]), a33(a[15]) {}
	Matrix4(const GLdouble* a) :
	  a00((GLfloat)a[ 0]), a10((GLfloat)a[ 1]), a20((GLfloat)a[ 2]), a30((GLfloat)a[ 3]),
	  a01((GLfloat)a[ 4]), a11((GLfloat)a[ 5]), a21((GLfloat)a[ 6]), a31((GLfloat)a[ 7]),
	  a02((GLfloat)a[ 8]), a12((GLfloat)a[ 9]), a22((GLfloat)a[10]), a32((GLfloat)a[11]),
	  a03((GLfloat)a[12]), a13((GLfloat)a[13]), a23((GLfloat)a[14]), a33((GLfloat)a[15]) {}

	/*  Note that row/columns in OpenGL are not how the representations
		reads in C/C++ source.
		So a00..a30 is actually the first column.
		(of course you can think about them the way you want)
	 */
    union {
        struct
		{
            GLfloat a00, a10, a20, a30;
            GLfloat a01, a11, a21, a31;
            GLfloat a02, a12, a22, a32;
            GLfloat a03, a13, a23, a33;
        };
        GLfloat m[16];
    };

	static Matrix4 Identity;

	Matrix4 operator*(const Matrix4&) const;
	operator const GLfloat*() const { return m; }
	void GetNearFar(GLfloat& n, GLfloat& f) const;
	void Frustum(GLfloat fovl, GLfloat fovr, GLfloat fovb, GLfloat fovt, GLfloat n, GLfloat f, bool swapz=false);
};

/*  MatrixStack - provides a matix stack with opengl funtionality. */
class MatrixStack
{
public:
	MatrixStack();

	// check if current top is a perspective matrix
	bool IsPerspective();

	// get top of matrix
	const Matrix4& CurrentMatrix();

	// methods matching gl<function>
	void PushMatrix();
	void PopMatrix();
	void LoadIdentity();
	void LoadMatrix(const GLfloat* m);
	void LoadTransposeMatrix(const GLfloat* m);
	void MultMatrix(const GLfloat* m);
    void MultTransposeMatrix(const GLfloat* m);
	void Frustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble znear, GLdouble zfar);
	void Ortho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
	void Translate(GLfloat x, GLfloat y, GLfloat z);
	void Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
	void Scale(GLfloat x, GLfloat y, GLfloat z);

private:
	std::vector<Matrix4> m_stack;
};

#endif
