//	GLMatrix.cpp - A GLFloat matrix used to set up translations etc.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#include "GLMatrix.h"

#include <cmath>

//------------------------------------------------------------------------------
GLMatrix::GLMatrix()
{
	identity();
}

//------------------------------------------------------------------------------
GLMatrix::~GLMatrix()
{
	
}

//------------------------------------------------------------------------------
void GLMatrix::translate(float x, float y, float z)
{
	tempMatrix[0] = tempMatrix[5] = tempMatrix[10] = tempMatrix[15] = 1.0;
    tempMatrix[1] = tempMatrix[2] = tempMatrix[3] = tempMatrix[4] = 0.0;
    tempMatrix[6] = tempMatrix[7] = tempMatrix[8] = tempMatrix[9] = 0.0;
    tempMatrix[11] = 0.0;
    tempMatrix[12] = x;
    tempMatrix[13] = y;
    tempMatrix[14] = z;

	multiplyWithTemp();
}

//------------------------------------------------------------------------------
void GLMatrix::scale(float x, float y, float z)
{
	tempMatrix[1] = tempMatrix[2] = tempMatrix[3] = tempMatrix[4] = 0.0;
    tempMatrix[6] = tempMatrix[7] = tempMatrix[8] = tempMatrix[9] = 0.0;
    tempMatrix[11] = tempMatrix[12] = tempMatrix[13] = tempMatrix[14] = 0.0;
    tempMatrix[0] = x;
    tempMatrix[5] = y;
    tempMatrix[10] = z;
    tempMatrix[15] = 1.0;

	multiplyWithTemp();
}

//------------------------------------------------------------------------------
void GLMatrix::rotate(float angle, float x, float y, float z)
{
	GLfloat mag = sqrtf((x*x) + (y*y) + (z*z));

	if(mag == 0.0f)
	{
		x = 0.0;
		y = 0.0;
		z = 1.0;
	}
	else if(mag != 1.0f)
	{
		x /= mag;
		y /= mag;
		z /= mag;
	}

	GLfloat c = cosf(angle);
	GLfloat s = sinf(angle);

	tempMatrix[0] = (x*x)*(1-c) + c;
	tempMatrix[1] = (x*y)*(1-c) - (z*s);
	tempMatrix[2] = (x*z)*(1-c) + (y*s);
	tempMatrix[3] = 0.0f;

	tempMatrix[4] = (y*x)*(1-c)+(z*s);
	tempMatrix[5] = (y*y)*(1-c)+c;
	tempMatrix[6] = (y*z)*(1-c)-(x*s);
	tempMatrix[7] = 0.0f;

	tempMatrix[8] = (x*z)*(1-c)-(y*s);
	tempMatrix[9] = (y*z)*(1-c)+(x*s);
	tempMatrix[10] = (z*z)*(1-c)+c;
	tempMatrix[11] = 0.0f;

	tempMatrix[12] = 0.0f;
	tempMatrix[13] = 0.0f;
	tempMatrix[14] = 0.0f;
	tempMatrix[15] = 1.0f;

	multiplyWithTemp();
}

//------------------------------------------------------------------------------
void GLMatrix::push()
{
	stack.push(matrix);
}

//------------------------------------------------------------------------------
void GLMatrix::pop()
{
	//If the stack is empty, set the matrix to the identity matrix.
	if(!stack.size())
		identity();
	else
	{
		matrix.swap(stack.top());
		stack.pop();
	}
}

//------------------------------------------------------------------------------
void GLMatrix::clear()
{
	while(stack.size() > 0)
		stack.pop();
}

//------------------------------------------------------------------------------
void GLMatrix::generateOrthoProjection(float left,
									   float right,
									   float bottom,
									   float top)
{
	//This code mostly grabbed from: http://stackoverflow.com/questions/6449095/how-do-i-set-up-the-viewable-area-in-a-scene-within-opengl-es-2-0
	GLfloat near = -1.0f;
	GLfloat far = 1.0f;
	GLfloat r_l = right - left;
    GLfloat t_b = top - bottom;
    GLfloat f_n = far - near;
    GLfloat tx = - (right + left)/(right - left);
    GLfloat ty = - (top + bottom)/(top - bottom);
    GLfloat tz = - (far + near)/(far - near);

    matrix[0] = 2.0f/r_l;
    matrix[1] = 0.0f;
    matrix[2] = 0.0f;
    matrix[3] = tx;

    matrix[4] = 0.0f;
    matrix[5] = 2.0f/t_b;
    matrix[6] = 0.0f;
    matrix[7] = ty;

    matrix[8] = 0.0f;
    matrix[9] = 0.0f;
    matrix[10] = 2.0f/f_n;
    matrix[11] = tz;

    matrix[12] = 0.0f;
    matrix[13] = 0.0f;
    matrix[14] = 0.0f;
    matrix[15] = 1.0f;
}
