//##########################################################################
//#                                                                        #
//#                              CLOUDCOMPARE                              #
//#                                                                        #
//#  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; version 2 or later of the License.      #
//#                                                                        #
//#  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.                          #
//#                                                                        #
//#          COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI)             #
//#                                                                        #
//##########################################################################

#ifndef CC_GL_MATRIX_HEADER
#define CC_GL_MATRIX_HEADER

//Local
#include "ccGLMatrixTpl.h"

//CCCoreLib
#include <SquareMatrix.h>

//! Float version of ccGLMatrixTpl
class QCC_DB_LIB_API ccGLMatrix : public ccGLMatrixTpl<float>
{
public:

	//! Default constructor
	/** Matrix is set to identity (see ccGLMatrixTpl::toIdentity) by default.
	**/
	ccGLMatrix() : ccGLMatrixTpl<float>() {}

	//! Copy constructor from a ccGLMatrixTpl
	/** \param mat matrix
	**/
	ccGLMatrix(const ccGLMatrixTpl<float>& mat) : ccGLMatrixTpl<float>(mat) {}

	//! Constructor from a float GL matrix array
	/** \param mat16f a 16 elements array (column major order)
	**/
	explicit ccGLMatrix(const float* mat16f) : ccGLMatrixTpl<float>(mat16f) {}

	//! Constructor from a double GL matrix array
	/** \warning Will implicitly cast the elements to float!
		\param mat16d a 16 elements array (column major order)
	**/
	explicit ccGLMatrix(const double* mat16d) : ccGLMatrixTpl<float>(mat16d) {}

	//! Constructor from 4 columns (X,Y,Z,Tr)
	/** \param X 3 first elements of the 1st column (last one is 0)
		\param Y 3 first elements of the 2nd column (last one is 0)
		\param Z 3 first elements of the 3rd column (last one is 0)
		\param Tr 3 first elements of the last column (last one is 1)
	**/
	ccGLMatrix(const Vector3Tpl<float>& X, const Vector3Tpl<float>& Y, const Vector3Tpl<float>& Z, const Vector3Tpl<float>& Tr) : ccGLMatrixTpl<float>(X,Y,Z,Tr) {}

};

//! Double version of ccGLMatrixTpl
class QCC_DB_LIB_API ccGLMatrixd : public ccGLMatrixTpl<double>
{
public:

	//! Default constructor
	/** Matrix is set to identity (see ccGLMatrixTpl::toIdentity) by default.
	**/
	ccGLMatrixd() : ccGLMatrixTpl<double>() {}

	//! Copy constructor from a ccGLMatrixTpl
	/** \param mat matrix
	**/
	ccGLMatrixd(const ccGLMatrixTpl<double>& mat) : ccGLMatrixTpl<double>(mat) {}

	//! Constructor from a float GL matrix array
	/** \param mat16f a 16 elements array (column major order)
	**/
	explicit ccGLMatrixd(const float* mat16f) : ccGLMatrixTpl<double>(mat16f) {}

	//! Constructor from a double GL matrix array
	/** \param mat16d a 16 elements array (column major order)
	**/
	explicit ccGLMatrixd(const double* mat16d) : ccGLMatrixTpl<double>(mat16d) {}

	//! Constructor from 4 columns (X,Y,Z,Tr)
	/** \param X 3 first elements of the 1st column (last one is 0)
		\param Y 3 first elements of the 2nd column (last one is 0)
		\param Z 3 first elements of the 3rd column (last one is 0)
		\param Tr 3 first elements of the last column (last one is 1)
	**/
	ccGLMatrixd(const Vector3Tpl<double>& X, const Vector3Tpl<double>& Y, const Vector3Tpl<double>& Z, const Vector3Tpl<double>& Tr) : ccGLMatrixTpl<double>(X,Y,Z,Tr) {}

};

/*** Helpers ***/

//! Constructor from a 3x3 rotation matrix R and a vector Tr
template <typename Tin, typename Tout> ccGLMatrixTpl<Tout> FromCCLibMatrix(const CCCoreLib::SquareMatrixTpl<Tin>& R, const Vector3Tpl<Tin>& Tr)
{
	ccGLMatrixTpl<Tout> outputMat;
	//outputMat.toIdentity(); //already done in the constructor

	if (R.isValid() && R.size() == 3)
	{
		//we copy each column
		Tout* mat = outputMat.data();
		for (unsigned j=0; j<3; ++j)
		{
			*mat++ = static_cast<Tout>(R.m_values[0][j]);
			*mat++ = static_cast<Tout>(R.m_values[1][j]);
			*mat++ = static_cast<Tout>(R.m_values[2][j]);
			mat++;
		}
	}

	outputMat.setTranslation( Vector3Tpl<Tout>( static_cast<Tout>(Tr.x),
												static_cast<Tout>(Tr.y),
												static_cast<Tout>(Tr.z) ));

	return outputMat;
}

//! Constructor from a 3x3 rotation matrix R, a vector Tr, a scale S
template <typename Tin, typename Tout> ccGLMatrixTpl<Tout> FromCCLibMatrix(const CCCoreLib::SquareMatrixTpl<Tin>& R, const Vector3Tpl<Tin>& Tr, Tin S)
{
	ccGLMatrixTpl<Tout> outputMat;
	//outputMat.toIdentity(); //already done in the constructor

	if (R.isValid() && R.size() == 3)
	{
		//we copy each column
		Tout* mat = outputMat.data();
		for (unsigned j=0; j<3; ++j)
		{
			*mat++ = static_cast<Tout>(R.m_values[0][j] * S);
			*mat++ = static_cast<Tout>(R.m_values[1][j] * S);
			*mat++ = static_cast<Tout>(R.m_values[2][j] * S);
			mat++;
		}
	}

	outputMat.setTranslation( Vector3Tpl<Tout>( static_cast<Tout>(Tr.x),
												static_cast<Tout>(Tr.y),
												static_cast<Tout>(Tr.z) ));

	return outputMat;
}

//! Constructor from a rotation center G, a 3x3 rotation matrix R and a vector Tr
template <typename Tin, typename Tout> ccGLMatrixTpl<Tout> FromCCLibMatrix(const CCCoreLib::SquareMatrixTpl<Tin>& R, const Vector3Tpl<Tin>& Tr, const Vector3Tpl<Tin>& rotCenter)
{
	ccGLMatrixTpl<Tout> outputMat = FromCCLibMatrix<Tin,Tout>(R,Tr);

	outputMat.shiftRotationCenter(rotCenter);

	return outputMat;
}

#endif //CC_GL_MATRIX_HEADER
