/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 * Copyright (C) 2003-2005 3Dlabs Inc. Ltd.
 * Copyright (C) 2008 Zebra Imaging
 * Copyright (C) 2012 David Callu
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commercial and non commercial
 * applications, as long as this copyright notice is maintained.
 *
 * This application 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.
*/

/* file:   include/osg/MatrxTemplate
 * author: Mike Weiblen 2008-01-02
*/

#ifndef OSG_MATRIXTEMPLATE
#define OSG_MATRIXTEMPLATE 1


namespace osg {


///////////////////////////////////////////////////////////////////////////
// C++ classes to represent the GLSL-specific types.
//
// Warning :
// OSG  is Row major
// GLSL is Column Major
//
// If you define an Uniform with type Uniform::FLOAT_MAT4X2 and so use a Matrix4x2 to setup your Uniform,
// like this :
// 1 2
// 3 4
// 5 6
// 7 8
//
// you will get in your shader a Column Major Matrix like this :
// 1 3 5 7
// 2 4 6 8
//
// In simple term, you matrix in OSG will be a transposed matrix in GLSL
//
//
// You can avoid this behaviours starting GLSL version 1.40 with uniform layout :
//
// <GLSL code>
// layout(row_major) uniform matrix4x2 myVariable;
// <GLSL code>
//
//
template <typename T, unsigned int RowN, unsigned int ColN>
class TemplateMatrix
{
    public:
      enum { col_count = ColN };
      enum { row_count = RowN };
      enum { value_count = ColN * RowN };

      typedef T value_type;


    public:
        TemplateMatrix() {}
        ~TemplateMatrix() {}

        value_type& operator()(int row, int col) { return _mat[row][col]; }
        value_type operator()(int row, int col) const { return _mat[row][col]; }

        TemplateMatrix& operator = (const TemplateMatrix& rhs)
        {
            if( &rhs == this ) return *this;
            set(rhs.ptr());
            return *this;
        }

        void set(const TemplateMatrix& rhs) { set(rhs.ptr()); }

        void set(value_type const * const ptr)
        {
            value_type* local_ptr = (value_type*)_mat;
            for(int i=0;i<value_count;++i) local_ptr[i]=ptr[i];
        }

        value_type* ptr() { return (value_type*)_mat; }
        const value_type* ptr() const { return (const value_type*)_mat; }

        value_type& operator [] (int i) {return ptr()[i];}
        value_type operator [] (int i) const {return ptr()[i];}

        void reset() { for(int i=0; i<value_count; ++i) ptr()[i] = static_cast<value_type>(0.0); }

    protected:
        value_type _mat[row_count][col_count];
};

template <typename T>
class Matrix2Template : public TemplateMatrix<T, 2, 2>
{
    public:
        typedef TemplateMatrix<T, 2, 2>         base_class;
        typedef typename base_class::value_type value_type;


    public:
        Matrix2Template() { makeIdentity(); }
        Matrix2Template( const Matrix2Template& mat ) { set(mat.ptr()); }
        Matrix2Template( value_type a00, value_type a01,
                         value_type a10, value_type a11 )
        {
            set( a00, a01,
                 a10, a11 );
        }
        ~Matrix2Template() {}

        using base_class::set;

        void set(value_type a00, value_type a01,
                 value_type a10, value_type a11 )
         {
             base_class::_mat[0][0]=a00; base_class::_mat[0][1]=a01;
             base_class::_mat[1][0]=a10; base_class::_mat[1][1]=a11;
         }

        void makeIdentity()
        {
            set( 1, 0,
                 0, 1 );
        }
};

template <typename T>
class Matrix2x3Template : public TemplateMatrix<T, 2, 3>
{
    public:
        typedef TemplateMatrix<T, 2, 3>         base_class;
        typedef typename base_class::value_type value_type;


    public:
        Matrix2x3Template() { base_class::reset(); }
        Matrix2x3Template( const Matrix2x3Template& mat ) { set(mat.ptr()); }
        Matrix2x3Template( value_type a00, value_type a01, value_type a02,
                           value_type a10, value_type a11, value_type a12 )
        {
            set( a00, a01, a02,
                 a10, a11, a12 );
        }
        ~Matrix2x3Template() {}

        using base_class::set;

        void set( value_type a00, value_type a01, value_type a02,
                  value_type a10, value_type a11, value_type a12 )
         {
             base_class::_mat[0][0]=a00; base_class::_mat[0][1]=a01; base_class::_mat[0][2]=a02;
             base_class::_mat[1][0]=a10; base_class::_mat[1][1]=a11; base_class::_mat[1][2]=a12;
         }
};

template <typename T>
class Matrix2x4Template : public TemplateMatrix<T, 2, 4>
{
    public:
        typedef TemplateMatrix<T, 2, 4>         base_class;
        typedef typename base_class::value_type value_type;


    public:
        Matrix2x4Template() { base_class::reset(); }
        Matrix2x4Template( const Matrix2x4Template& mat ) { set(mat.ptr()); }
        Matrix2x4Template( value_type a00, value_type a01, value_type a02, value_type a03,
                           value_type a10, value_type a11, value_type a12, value_type a13 )
        {
            set( a00, a01, a02, a03,
                 a10, a11, a12, a13 );
        }
        ~Matrix2x4Template() {}

        using base_class::set;

        void set( value_type a00, value_type a01, value_type a02, value_type a03,
                  value_type a10, value_type a11, value_type a12, value_type a13 )
         {
             base_class::_mat[0][0]=a00; base_class::_mat[0][1]=a01; base_class::_mat[0][2]=a02; base_class::_mat[0][3]=a03;
             base_class::_mat[1][0]=a10; base_class::_mat[1][1]=a11; base_class::_mat[1][2]=a12; base_class::_mat[1][3]=a13;
         }
};

template <typename T>
class Matrix3x2Template : public TemplateMatrix<T, 3, 2>
{
    public:
        typedef TemplateMatrix<T, 3, 2>         base_class;
        typedef typename base_class::value_type value_type;

    public:
        Matrix3x2Template() { base_class::reset(); }
        Matrix3x2Template( const Matrix3x2Template& mat ) { set(mat.ptr()); }
        Matrix3x2Template( value_type a00, value_type a01,
                           value_type a10, value_type a11,
                           value_type a20, value_type a21 )
        {
            set( a00, a01,
                 a10, a11,
                 a20, a21 );
        }
        ~Matrix3x2Template() {}

        using base_class::set;

        void set( value_type a00, value_type a01,
                  value_type a10, value_type a11,
                  value_type a20, value_type a21 )
         {
             base_class::_mat[0][0]=a00; base_class::_mat[0][1]=a01;
             base_class::_mat[1][0]=a10; base_class::_mat[1][1]=a11;
             base_class::_mat[2][0]=a20; base_class::_mat[2][1]=a21;
         }
};

template <typename T>
class Matrix3Template : public TemplateMatrix<T, 3, 3>
{
    public:
        typedef TemplateMatrix<T, 3, 3>         base_class;
        typedef typename base_class::value_type value_type;

    public:
        Matrix3Template() { base_class::reset(); }
        Matrix3Template( const Matrix3Template& mat ) { set(mat.ptr()); }
        Matrix3Template( value_type a00, value_type a01, value_type a02,
                         value_type a10, value_type a11, value_type a12,
                         value_type a20, value_type a21, value_type a22 )
        {
            set( a00, a01, a02,
                 a10, a11, a12,
                 a20, a21, a22 );
        }
        ~Matrix3Template() {}

        using base_class::set;

        void set( value_type a00, value_type a01, value_type a02,
                  value_type a10, value_type a11, value_type a12,
                  value_type a20, value_type a21, value_type a22 )
         {
             base_class::_mat[0][0]=a00; base_class::_mat[0][1]=a01; base_class::_mat[0][2]=a02;
             base_class::_mat[1][0]=a10; base_class::_mat[1][1]=a11; base_class::_mat[1][2]=a12;
             base_class::_mat[2][0]=a20; base_class::_mat[2][1]=a21; base_class::_mat[2][2]=a22;
         }

        void makeIdentity()
        {
            set( 1, 0, 0,
                 0, 1, 0,
                 0, 0, 1 );
        }
};

template <typename T>
class Matrix3x4Template : public TemplateMatrix<T, 3, 4>
{
    public:
        typedef TemplateMatrix<T, 3, 4>         base_class;
        typedef typename base_class::value_type value_type;

    public:
        Matrix3x4Template() { base_class::reset(); }
        Matrix3x4Template( const Matrix3x4Template& mat ) { set(mat.ptr()); }
        Matrix3x4Template( value_type a00, value_type a01, value_type a02, value_type a03,
                           value_type a10, value_type a11, value_type a12, value_type a13,
                           value_type a20, value_type a21, value_type a22, value_type a23 )
        {
            set( a00, a01, a02, a03,
                 a10, a11, a12, a13,
                 a20, a21, a22, a23 );
        }
        ~Matrix3x4Template() {}

        using base_class::set;

        void set( value_type a00, value_type a01, value_type a02, value_type a03,
                  value_type a10, value_type a11, value_type a12, value_type a13,
                  value_type a20, value_type a21, value_type a22, value_type a23 )
         {
             base_class::_mat[0][0]=a00; base_class::_mat[0][1]=a01; base_class::_mat[0][2]=a02; base_class::_mat[0][3]=a03;
             base_class::_mat[1][0]=a10; base_class::_mat[1][1]=a11; base_class::_mat[1][2]=a12; base_class::_mat[1][3]=a13;
             base_class::_mat[2][0]=a20; base_class::_mat[2][1]=a21; base_class::_mat[2][2]=a22; base_class::_mat[2][3]=a23;
         }
};

template <typename T>
class Matrix4x2Template : public TemplateMatrix<T, 4, 2>
{
    public:
        typedef TemplateMatrix<T, 4, 2>         base_class;
        typedef typename base_class::value_type value_type;

    public:
        Matrix4x2Template() { base_class::reset(); }
        Matrix4x2Template( const Matrix4x2Template& mat ) { set(mat.ptr()); }
        Matrix4x2Template( value_type a00, value_type a01,
                           value_type a10, value_type a11,
                           value_type a20, value_type a21,
                           value_type a30, value_type a31 )
        {
            set( a00, a01,
                 a10, a11,
                 a20, a21,
                 a30, a31 );
        }
        ~Matrix4x2Template() {}

        using base_class::set;

        void set( value_type a00, value_type a01,
                  value_type a10, value_type a11,
                  value_type a20, value_type a21,
                  value_type a30, value_type a31 )
         {
             base_class::_mat[0][0]=a00; base_class::_mat[0][1]=a01;
             base_class::_mat[1][0]=a10; base_class::_mat[1][1]=a11;
             base_class::_mat[2][0]=a20; base_class::_mat[2][1]=a21;
             base_class::_mat[3][0]=a30; base_class::_mat[3][1]=a31;
         }
};

template <typename T>
class Matrix4x3Template : public TemplateMatrix<T, 4, 3>
{
    public:
        typedef TemplateMatrix<T, 4, 3>         base_class;
        typedef typename base_class::value_type value_type;

    public:
        Matrix4x3Template() { base_class::reset(); }
        Matrix4x3Template( const Matrix4x3Template& mat ) { set(mat.ptr()); }
        Matrix4x3Template( value_type a00, value_type a01, value_type a02,
                           value_type a10, value_type a11, value_type a12,
                           value_type a20, value_type a21, value_type a22,
                           value_type a30, value_type a31, value_type a32 )
        {
            set( a00, a01, a02,
                 a10, a11, a12,
                 a20, a21, a22,
                 a30, a31, a32 );
        }
        ~Matrix4x3Template() {}

        using base_class::set;

        void set( value_type a00, value_type a01, value_type a02,
                  value_type a10, value_type a11, value_type a12,
                  value_type a20, value_type a21, value_type a22,
                  value_type a30, value_type a31, value_type a32 )
         {
             base_class::_mat[0][0]=a00; base_class::_mat[0][1]=a01; base_class::_mat[0][2]=a02;
             base_class::_mat[1][0]=a10; base_class::_mat[1][1]=a11; base_class::_mat[1][2]=a12;
             base_class::_mat[2][0]=a20; base_class::_mat[2][1]=a21; base_class::_mat[2][2]=a22;
             base_class::_mat[3][0]=a30; base_class::_mat[3][1]=a31; base_class::_mat[3][2]=a32;
         }
};

typedef Matrix2Template<float>    Matrix2;
typedef Matrix2x3Template<float>  Matrix2x3;
typedef Matrix2x4Template<float>  Matrix2x4;

typedef Matrix3x2Template<float>  Matrix3x2;
typedef Matrix3Template<float>    Matrix3;
typedef Matrix3x4Template<float>  Matrix3x4;

typedef Matrix4x2Template<float>  Matrix4x2;
typedef Matrix4x3Template<float>  Matrix4x3;


typedef Matrix2Template<double>    Matrix2d;
typedef Matrix2x3Template<double>  Matrix2x3d;
typedef Matrix2x4Template<double>  Matrix2x4d;

typedef Matrix3x2Template<double>  Matrix3x2d;
typedef Matrix3Template<double>    Matrix3d;
typedef Matrix3x4Template<double>  Matrix3x4d;

typedef Matrix4x2Template<double>  Matrix4x2d;
typedef Matrix4x3Template<double>  Matrix4x3d;


}

#endif
