/* -*-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/Uniform
 * author: Mike Weiblen 2008-01-02
*/

#ifndef OSG_UNIFORM
#define OSG_UNIFORM 1

#include <osg/ref_ptr>
#include <osg/Array>
#include <osg/Callback>
#include <osg/Vec2>
#include <osg/Vec3>
#include <osg/Vec4>
#include <osg/Vec2d>
#include <osg/Vec3d>
#include <osg/Vec4d>
#include <osg/Matrix>
#include <osg/GLExtensions>

#include <string.h> // for memset
#include <string>
#include <vector>


namespace osg {

// forward declare
class NodeVisitor;

///////////////////////////////////////////////////////////////////////////
// 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 MatrixTemplate
{
    public:
      enum { col_count = ColN };
      enum { row_count = RowN };
      enum { value_count = ColN * RowN };

      typedef T value_type;


    public:
        MatrixTemplate() {}
        ~MatrixTemplate() {}

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

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

        void set(const MatrixTemplate& 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() { memset(_mat, 0, sizeof( value_type ) * value_count); }

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

template <typename T>
class Matrix2Template : public MatrixTemplate<T, 2, 2>
{
    public:
        typedef MatrixTemplate<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 MatrixTemplate<T, 2, 3>
{
    public:
        typedef MatrixTemplate<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 MatrixTemplate<T, 2, 4>
{
    public:
        typedef MatrixTemplate<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 MatrixTemplate<T, 3, 2>
{
    public:
        typedef MatrixTemplate<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 MatrixTemplate<T, 3, 3>
{
    public:
        typedef MatrixTemplate<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 MatrixTemplate<T, 3, 4>
{
    public:
        typedef MatrixTemplate<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 MatrixTemplate<T, 4, 2>
{
    public:
        typedef MatrixTemplate<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 MatrixTemplate<T, 4, 3>
{
    public:
        typedef MatrixTemplate<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;



///////////////////////////////////////////////////////////////////////////

/** Uniform encapsulates glUniform values */
class OSG_EXPORT Uniform : public Object
{
    public:
        enum Type {
            FLOAT      = GL_FLOAT,
            FLOAT_VEC2 = GL_FLOAT_VEC2,
            FLOAT_VEC3 = GL_FLOAT_VEC3,
            FLOAT_VEC4 = GL_FLOAT_VEC4,

            DOUBLE      = GL_DOUBLE,
            DOUBLE_VEC2 = GL_DOUBLE_VEC2,
            DOUBLE_VEC3 = GL_DOUBLE_VEC3,
            DOUBLE_VEC4 = GL_DOUBLE_VEC4,

            INT      = GL_INT,
            INT_VEC2 = GL_INT_VEC2,
            INT_VEC3 = GL_INT_VEC3,
            INT_VEC4 = GL_INT_VEC4,

            UNSIGNED_INT      = GL_UNSIGNED_INT,
            UNSIGNED_INT_VEC2 = GL_UNSIGNED_INT_VEC2_EXT,
            UNSIGNED_INT_VEC3 = GL_UNSIGNED_INT_VEC3_EXT,
            UNSIGNED_INT_VEC4 = GL_UNSIGNED_INT_VEC4_EXT,

            BOOL      = GL_BOOL,
            BOOL_VEC2 = GL_BOOL_VEC2,
            BOOL_VEC3 = GL_BOOL_VEC3,
            BOOL_VEC4 = GL_BOOL_VEC4,

            INT64            = GL_INT64_ARB,
            UNSIGNED_INT64   = GL_UNSIGNED_INT64_ARB,

            FLOAT_MAT2   = GL_FLOAT_MAT2,
            FLOAT_MAT3   = GL_FLOAT_MAT3,
            FLOAT_MAT4   = GL_FLOAT_MAT4,
            FLOAT_MAT2x3 = GL_FLOAT_MAT2x3,
            FLOAT_MAT2x4 = GL_FLOAT_MAT2x4,
            FLOAT_MAT3x2 = GL_FLOAT_MAT3x2,
            FLOAT_MAT3x4 = GL_FLOAT_MAT3x4,
            FLOAT_MAT4x2 = GL_FLOAT_MAT4x2,
            FLOAT_MAT4x3 = GL_FLOAT_MAT4x3,

            DOUBLE_MAT2   = GL_DOUBLE_MAT2,
            DOUBLE_MAT3   = GL_DOUBLE_MAT3,
            DOUBLE_MAT4   = GL_DOUBLE_MAT4,
            DOUBLE_MAT2x3 = GL_DOUBLE_MAT2x3,
            DOUBLE_MAT2x4 = GL_DOUBLE_MAT2x4,
            DOUBLE_MAT3x2 = GL_DOUBLE_MAT3x2,
            DOUBLE_MAT3x4 = GL_DOUBLE_MAT3x4,
            DOUBLE_MAT4x2 = GL_DOUBLE_MAT4x2,
            DOUBLE_MAT4x3 = GL_DOUBLE_MAT4x3,

            SAMPLER_1D                    = GL_SAMPLER_1D,
            SAMPLER_2D                    = GL_SAMPLER_2D,
            SAMPLER_3D                    = GL_SAMPLER_3D,
            SAMPLER_CUBE                  = GL_SAMPLER_CUBE,
            SAMPLER_1D_SHADOW             = GL_SAMPLER_1D_SHADOW,
            SAMPLER_2D_SHADOW             = GL_SAMPLER_2D_SHADOW,
            SAMPLER_1D_ARRAY              = GL_SAMPLER_1D_ARRAY_EXT,
            SAMPLER_2D_ARRAY              = GL_SAMPLER_2D_ARRAY_EXT,
            SAMPLER_CUBE_MAP_ARRAY        = GL_SAMPLER_CUBE_MAP_ARRAY,
            SAMPLER_1D_ARRAY_SHADOW       = GL_SAMPLER_1D_ARRAY_SHADOW_EXT,
            SAMPLER_2D_ARRAY_SHADOW       = GL_SAMPLER_2D_ARRAY_SHADOW_EXT,
            SAMPLER_2D_MULTISAMPLE        = GL_SAMPLER_2D_MULTISAMPLE,
            SAMPLER_2D_MULTISAMPLE_ARRAY  = GL_SAMPLER_2D_MULTISAMPLE_ARRAY,
            SAMPLER_CUBE_SHADOW           = GL_SAMPLER_CUBE_SHADOW_EXT,
            SAMPLER_CUBE_MAP_ARRAY_SHADOW = GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW,
            SAMPLER_BUFFER                = GL_SAMPLER_BUFFER_EXT,
            SAMPLER_2D_RECT               = GL_SAMPLER_2D_RECT,
            SAMPLER_2D_RECT_SHADOW        = GL_SAMPLER_2D_RECT_SHADOW,

            INT_SAMPLER_1D                   = GL_INT_SAMPLER_1D_EXT,
            INT_SAMPLER_2D                   = GL_INT_SAMPLER_2D_EXT,
            INT_SAMPLER_3D                   = GL_INT_SAMPLER_3D_EXT,
            INT_SAMPLER_CUBE                 = GL_INT_SAMPLER_CUBE_EXT,
            INT_SAMPLER_1D_ARRAY             = GL_INT_SAMPLER_1D_ARRAY_EXT,
            INT_SAMPLER_2D_ARRAY             = GL_INT_SAMPLER_2D_ARRAY_EXT,
            INT_SAMPLER_CUBE_MAP_ARRAY       = GL_INT_SAMPLER_CUBE_MAP_ARRAY,
            INT_SAMPLER_2D_MULTISAMPLE       = GL_INT_SAMPLER_2D_MULTISAMPLE,
            INT_SAMPLER_2D_MULTISAMPLE_ARRAY = GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
            INT_SAMPLER_BUFFER               = GL_INT_SAMPLER_BUFFER_EXT,
            INT_SAMPLER_2D_RECT              = GL_INT_SAMPLER_2D_RECT_EXT,

            UNSIGNED_INT_SAMPLER_1D                   = GL_UNSIGNED_INT_SAMPLER_1D_EXT,
            UNSIGNED_INT_SAMPLER_2D                   = GL_UNSIGNED_INT_SAMPLER_2D_EXT,
            UNSIGNED_INT_SAMPLER_3D                   = GL_UNSIGNED_INT_SAMPLER_3D_EXT,
            UNSIGNED_INT_SAMPLER_CUBE                 = GL_UNSIGNED_INT_SAMPLER_CUBE_EXT,
            UNSIGNED_INT_SAMPLER_1D_ARRAY             = GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT,
            UNSIGNED_INT_SAMPLER_2D_ARRAY             = GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT,
            UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY       = GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY,
            UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE       = GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE,
            UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
            UNSIGNED_INT_SAMPLER_BUFFER               = GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT,
            UNSIGNED_INT_SAMPLER_2D_RECT              = GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT,

            IMAGE_1D                   = GL_IMAGE_1D,
            IMAGE_2D                   = GL_IMAGE_2D,
            IMAGE_3D                   = GL_IMAGE_3D,
            IMAGE_2D_RECT              = GL_IMAGE_2D_RECT,
            IMAGE_CUBE                 = GL_IMAGE_CUBE,
            IMAGE_BUFFER               = GL_IMAGE_BUFFER,
            IMAGE_1D_ARRAY             = GL_IMAGE_1D_ARRAY,
            IMAGE_2D_ARRAY             = GL_IMAGE_2D_ARRAY,
            IMAGE_CUBE_MAP_ARRAY       = GL_IMAGE_CUBE_MAP_ARRAY,
            IMAGE_2D_MULTISAMPLE       = GL_IMAGE_2D_MULTISAMPLE,
            IMAGE_2D_MULTISAMPLE_ARRAY = GL_IMAGE_2D_MULTISAMPLE_ARRAY,

            INT_IMAGE_1D                   = GL_INT_IMAGE_1D,
            INT_IMAGE_2D                   = GL_INT_IMAGE_2D,
            INT_IMAGE_3D                   = GL_INT_IMAGE_3D,
            INT_IMAGE_2D_RECT              = GL_INT_IMAGE_2D_RECT,
            INT_IMAGE_CUBE                 = GL_INT_IMAGE_CUBE,
            INT_IMAGE_BUFFER               = GL_INT_IMAGE_BUFFER,
            INT_IMAGE_1D_ARRAY             = GL_INT_IMAGE_1D_ARRAY,
            INT_IMAGE_2D_ARRAY             = GL_INT_IMAGE_2D_ARRAY,
            INT_IMAGE_CUBE_MAP_ARRAY       = GL_INT_IMAGE_CUBE_MAP_ARRAY,
            INT_IMAGE_2D_MULTISAMPLE       = GL_INT_IMAGE_2D_MULTISAMPLE,
            INT_IMAGE_2D_MULTISAMPLE_ARRAY = GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY,

            UNSIGNED_INT_IMAGE_1D                   = GL_UNSIGNED_INT_IMAGE_1D,
            UNSIGNED_INT_IMAGE_2D                   = GL_UNSIGNED_INT_IMAGE_2D,
            UNSIGNED_INT_IMAGE_3D                   = GL_UNSIGNED_INT_IMAGE_3D,
            UNSIGNED_INT_IMAGE_2D_RECT              = GL_UNSIGNED_INT_IMAGE_2D_RECT,
            UNSIGNED_INT_IMAGE_CUBE                 = GL_UNSIGNED_INT_IMAGE_CUBE,
            UNSIGNED_INT_IMAGE_BUFFER               = GL_UNSIGNED_INT_IMAGE_BUFFER,
            UNSIGNED_INT_IMAGE_1D_ARRAY             = GL_UNSIGNED_INT_IMAGE_1D_ARRAY,
            UNSIGNED_INT_IMAGE_2D_ARRAY             = GL_UNSIGNED_INT_IMAGE_2D_ARRAY,
            UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY       = GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY,
            UNSIGNED_INT_IMAGE_2D_MULTISAMPLE       = GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE,
            UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY = GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY,

            UNDEFINED = 0x0
        };

    public:

        Uniform();
        Uniform( Type type, const std::string& name, int numElements=1 );

        /** Copy constructor using CopyOp to manage deep vs shallow copy. */
        Uniform(const Uniform& rhs, const CopyOp& copyop=CopyOp::SHALLOW_COPY);

        META_Object(osg, Uniform);

        /** Convert 'this' into a Uniform pointer if Object is a Uniform, otherwise return 0.
          * Equivalent to dynamic_cast<Uniform*>(this).*/
        virtual Uniform* asUniform() { return this; }

        /** convert 'const this' into a const Uniform pointer if Object is a Uniform, otherwise return 0.
          * Equivalent to dynamic_cast<const Uniform*>(this).*/
        virtual const Uniform* asUniform() const { return this; }

        
        /** Set the type of glUniform, ensuring it is only set once.*/
        bool setType( Type t );

        /** Get the type of glUniform as enum. */
        Type getType() const { return _type; }

        /** Set the name of the glUniform, ensuring it is only set once.*/
        virtual void setName( const std::string& name );

        /** Set the length of a uniform, ensuring it is only set once (1==scalar)*/
        void setNumElements( unsigned int numElements );

        /** Get the number of GLSL elements of the osg::Uniform (1==scalar) */
        unsigned int getNumElements() const { return _numElements; }

        /** Get the number of elements required for the internal data array.
          * Returns 0 if the osg::Uniform is not properly configured.  */
        unsigned int getInternalArrayNumElements() const;

        /** Return the name of a Type enum as string. */
        static const char* getTypename( Type t );

        /** Return the number of components for a GLSL type. */
        static int getTypeNumComponents( Type t );

        /** Return the Type enum of a Uniform typename string */
        static Uniform::Type getTypeId( const std::string& tname );

        /** Return the GL API type corresponding to a GLSL type */
        static Type getGlApiType( Type t );

        /** Return the internal data array type corresponding to a GLSL type */
        static GLenum getInternalArrayType( Type t );

        /** Return the number that the name maps to uniquely */
        static unsigned int getNameID(const std::string& name);

        /** convenient scalar (non-array) constructors w/ assignment */
        explicit Uniform( const char* name, float f );
        explicit Uniform( const char* name, double d );
        explicit Uniform( const char* name, int i );
        explicit Uniform( const char* name, unsigned int ui );
        explicit Uniform( const char* name, bool b );
        explicit Uniform( const char* name, unsigned long long ull);
        explicit Uniform( const char* name, long long ll );
        Uniform( const char* name, const osg::Vec2& v2 );
        Uniform( const char* name, const osg::Vec3& v3 );
        Uniform( const char* name, const osg::Vec4& v4 );
        Uniform( const char* name, const osg::Vec2d& v2 );
        Uniform( const char* name, const osg::Vec3d& v3 );
        Uniform( const char* name, const osg::Vec4d& v4 );
        Uniform( const char* name, const osg::Matrix2& m2 );
        Uniform( const char* name, const osg::Matrix3& m3 );
        Uniform( const char* name, const osg::Matrixf& m4 );
        Uniform( const char* name, const osg::Matrix2x3& m2x3 );
        Uniform( const char* name, const osg::Matrix2x4& m2x4 );
        Uniform( const char* name, const osg::Matrix3x2& m3x2 );
        Uniform( const char* name, const osg::Matrix3x4& m3x4 );
        Uniform( const char* name, const osg::Matrix4x2& m4x2 );
        Uniform( const char* name, const osg::Matrix4x3& m4x3 );
        Uniform( const char* name, const osg::Matrix2d& m2 );
        Uniform( const char* name, const osg::Matrix3d& m3 );
        Uniform( const char* name, const osg::Matrixd& m4 );
        Uniform( const char* name, const osg::Matrix2x3d& m2x3 );
        Uniform( const char* name, const osg::Matrix2x4d& m2x4 );
        Uniform( const char* name, const osg::Matrix3x2d& m3x2 );
        Uniform( const char* name, const osg::Matrix3x4d& m3x4 );
        Uniform( const char* name, const osg::Matrix4x2d& m4x2 );
        Uniform( const char* name, const osg::Matrix4x3d& m4x3 );
        Uniform( const char* name, int i0, int i1 );
        Uniform( const char* name, int i0, int i1, int i2 );
        Uniform( const char* name, int i0, int i1, int i2, int i3 );
        Uniform( const char* name, unsigned int ui0, unsigned int ui1 );
        Uniform( const char* name, unsigned int ui0, unsigned int ui1, unsigned int ui2 );
        Uniform( const char* name, unsigned int ui0, unsigned int ui1, unsigned int ui2, unsigned int ui3 );
        Uniform( const char* name, bool b0, bool b1 );
        Uniform( const char* name, bool b0, bool b1, bool b2 );
        Uniform( const char* name, bool b0, bool b1, bool b2, bool b3 );

        /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */
        virtual int compare(const Uniform& rhs) const;
        virtual int compareData(const Uniform& rhs) const;

        bool operator <  (const Uniform& rhs) const { return compare(rhs)<0; }
        bool operator == (const Uniform& rhs) const { return compare(rhs)==0; }
        bool operator != (const Uniform& rhs) const { return compare(rhs)!=0; }

        void copyData( const Uniform& rhs );


        /** A vector of osg::StateSet pointers which is used to store the parent(s) of this Uniform, the parents could be osg::Node or osg::Drawable.*/
        typedef std::vector<StateSet*> ParentList;

        /** Get the parent list of this Uniform. */
        inline const ParentList& getParents() const { return _parents; }

        /** Get the a copy of parent list of node. A copy is returned to
          * prevent modification of the parent list.*/
        inline ParentList getParents() { return _parents; }

        inline StateSet* getParent(unsigned int i)  { return _parents[i]; }
        /**
         * Get a single const parent of this Uniform.
         * @param i index of the parent to get.
         * @return the parent i.
         */
        inline const StateSet* getParent(unsigned int i) const  { return _parents[i]; }

        /**
         * Get the number of parents of this Uniform.
         * @return the number of parents of this Uniform.
         */
        inline unsigned int getNumParents() const { return static_cast<unsigned int>(_parents.size()); }


        /** convenient scalar (non-array) value assignment */
        bool set( float f );
        bool set( double d );
        bool set( int i );
        bool set( unsigned int ui );
        bool set( bool b );
        bool set( unsigned long long ull );
        bool set( long long ll );
        bool set( const osg::Vec2& v2 );
        bool set( const osg::Vec3& v3 );
        bool set( const osg::Vec4& v4 );
        bool set( const osg::Vec2d& v2 );
        bool set( const osg::Vec3d& v3 );
        bool set( const osg::Vec4d& v4 );
        bool set( const osg::Matrix2& m2 );
        bool set( const osg::Matrix3& m3 );
        bool set( const osg::Matrixf& m4 );
        bool set( const osg::Matrix2x3& m2x3 );
        bool set( const osg::Matrix2x4& m2x4 );
        bool set( const osg::Matrix3x2& m3x2 );
        bool set( const osg::Matrix3x4& m3x4 );
        bool set( const osg::Matrix4x2& m4x2 );
        bool set( const osg::Matrix4x3& m4x3 );
        bool set( const osg::Matrix2d& m2 );
        bool set( const osg::Matrix3d& m3 );
        bool set( const osg::Matrixd& m4 );
        bool set( const osg::Matrix2x3d& m2x3 );
        bool set( const osg::Matrix2x4d& m2x4 );
        bool set( const osg::Matrix3x2d& m3x2 );
        bool set( const osg::Matrix3x4d& m3x4 );
        bool set( const osg::Matrix4x2d& m4x2 );
        bool set( const osg::Matrix4x3d& m4x3 );
        bool set( int i0, int i1 );
        bool set( int i0, int i1, int i2 );
        bool set( int i0, int i1, int i2, int i3 );
        bool set( unsigned int ui0, unsigned int ui1 );
        bool set( unsigned int ui0, unsigned int ui1, unsigned int ui2 );
        bool set( unsigned int ui0, unsigned int ui1, unsigned int ui2, unsigned int ui3 );
        bool set( bool b0, bool b1 );
        bool set( bool b0, bool b1, bool b2 );
        bool set( bool b0, bool b1, bool b2, bool b3 );

        /** convenient scalar (non-array) value query */
        bool get( float& f ) const;
        bool get( double& d ) const;
        bool get( int& i ) const;
        bool get( unsigned int& ui ) const;
        bool get( bool& b ) const;
        bool get( unsigned long long & ull ) const;
        bool get( long long& ll ) const;
        bool get( osg::Vec2& v2 ) const;
        bool get( osg::Vec3& v3 ) const;
        bool get( osg::Vec4& v4 ) const;
        bool get( osg::Vec2d& v2 ) const;
        bool get( osg::Vec3d& v3 ) const;
        bool get( osg::Vec4d& v4 ) const;
        bool get( osg::Matrix2& m2 ) const;
        bool get( osg::Matrix3& m3 ) const;
        bool get( osg::Matrixf& m4 ) const;
        bool get( osg::Matrix2x3& m2x3 ) const;
        bool get( osg::Matrix2x4& m2x4 ) const;
        bool get( osg::Matrix3x2& m3x2 ) const;
        bool get( osg::Matrix3x4& m3x4 ) const;
        bool get( osg::Matrix4x2& m4x2 ) const;
        bool get( osg::Matrix4x3& m4x3 ) const;
        bool get( osg::Matrix2d& m2 ) const;
        bool get( osg::Matrix3d& m3 ) const;
        bool get( osg::Matrixd& m4 ) const;
        bool get( osg::Matrix2x3d& m2x3 ) const;
        bool get( osg::Matrix2x4d& m2x4 ) const;
        bool get( osg::Matrix3x2d& m3x2 ) const;
        bool get( osg::Matrix3x4d& m3x4 ) const;
        bool get( osg::Matrix4x2d& m4x2 ) const;
        bool get( osg::Matrix4x3d& m4x3 ) const;
        bool get( int& i0, int& i1 ) const;
        bool get( int& i0, int& i1, int& i2 ) const;
        bool get( int& i0, int& i1, int& i2, int& i3 ) const;
        bool get( unsigned int& ui0, unsigned int& ui1 ) const;
        bool get( unsigned int& ui0, unsigned int& ui1, unsigned int& ui2 ) const;
        bool get( unsigned int& ui0, unsigned int& ui1, unsigned int& ui2, unsigned int& ui3 ) const;
        bool get( bool& b0, bool& b1 ) const;
        bool get( bool& b0, bool& b1, bool& b2 ) const;
        bool get( bool& b0, bool& b1, bool& b2, bool& b3 ) const;

        /** value assignment for array uniforms */
        bool setElement( unsigned int index, float f );
        bool setElement( unsigned int index, double d );
        bool setElement( unsigned int index, int i );
        bool setElement( unsigned int index, unsigned int ui );
        bool setElement( unsigned int index, bool b );
        bool setElement( unsigned int index, unsigned long long  ull );
        bool setElement( unsigned int index, long long ll );
        bool setElement( unsigned int index, const osg::Vec2& v2 );
        bool setElement( unsigned int index, const osg::Vec3& v3 );
        bool setElement( unsigned int index, const osg::Vec4& v4 );
        bool setElement( unsigned int index, const osg::Vec2d& v2 );
        bool setElement( unsigned int index, const osg::Vec3d& v3 );
        bool setElement( unsigned int index, const osg::Vec4d& v4 );
        bool setElement( unsigned int index, const osg::Matrix2& m2 );
        bool setElement( unsigned int index, const osg::Matrix3& m3 );
        bool setElement( unsigned int index, const osg::Matrixf& m4 );
        bool setElement( unsigned int index, const osg::Matrix2x3& m2x3 );
        bool setElement( unsigned int index, const osg::Matrix2x4& m2x4 );
        bool setElement( unsigned int index, const osg::Matrix3x2& m3x2 );
        bool setElement( unsigned int index, const osg::Matrix3x4& m3x4 );
        bool setElement( unsigned int index, const osg::Matrix4x2& m4x2 );
        bool setElement( unsigned int index, const osg::Matrix4x3& m4x3 );
        bool setElement( unsigned int index, const osg::Matrix2d& m2 );
        bool setElement( unsigned int index, const osg::Matrix3d& m3 );
        bool setElement( unsigned int index, const osg::Matrixd& m4 );
        bool setElement( unsigned int index, const osg::Matrix2x3d& m2x3 );
        bool setElement( unsigned int index, const osg::Matrix2x4d& m2x4 );
        bool setElement( unsigned int index, const osg::Matrix3x2d& m3x2 );
        bool setElement( unsigned int index, const osg::Matrix3x4d& m3x4 );
        bool setElement( unsigned int index, const osg::Matrix4x2d& m4x2 );
        bool setElement( unsigned int index, const osg::Matrix4x3d& m4x3 );
        bool setElement( unsigned int index, int i0, int i1 );
        bool setElement( unsigned int index, int i0, int i1, int i2 );
        bool setElement( unsigned int index, int i0, int i1, int i2, int i3 );
        bool setElement( unsigned int index, unsigned int ui0, unsigned int ui1 );
        bool setElement( unsigned int index, unsigned int ui0, unsigned int ui1, unsigned int ui2 );
        bool setElement( unsigned int index, unsigned int ui0, unsigned int ui1, unsigned int ui2, unsigned int ui3 );
        bool setElement( unsigned int index, bool b0, bool b1 );
        bool setElement( unsigned int index, bool b0, bool b1, bool b2 );
        bool setElement( unsigned int index, bool b0, bool b1, bool b2, bool b3 );

        /** value query for array uniforms */
        bool getElement( unsigned int index, float& f ) const;
        bool getElement( unsigned int index, double& d ) const;
        bool getElement( unsigned int index, int& i ) const;
        bool getElement( unsigned int index, unsigned int& ui ) const;
        bool getElement( unsigned int index, bool& b ) const;
        bool getElement( unsigned int index, unsigned long long & ull ) const;
        bool getElement( unsigned int index, long long& ll ) const;
        bool getElement( unsigned int index, osg::Vec2& v2 ) const;
        bool getElement( unsigned int index, osg::Vec3& v3 ) const;
        bool getElement( unsigned int index, osg::Vec4& v4 ) const;
        bool getElement( unsigned int index, osg::Vec2d& v2 ) const;
        bool getElement( unsigned int index, osg::Vec3d& v3 ) const;
        bool getElement( unsigned int index, osg::Vec4d& v4 ) const;
        bool getElement( unsigned int index, osg::Matrix2& m2 ) const;
        bool getElement( unsigned int index, osg::Matrix3& m3 ) const;
        bool getElement( unsigned int index, osg::Matrixf& m4 ) const;
        bool getElement( unsigned int index, osg::Matrix2x3& m2x3 ) const;
        bool getElement( unsigned int index, osg::Matrix2x4& m2x4 ) const;
        bool getElement( unsigned int index, osg::Matrix3x2& m3x2 ) const;
        bool getElement( unsigned int index, osg::Matrix3x4& m3x4 ) const;
        bool getElement( unsigned int index, osg::Matrix4x2& m4x2 ) const;
        bool getElement( unsigned int index, osg::Matrix4x3& m4x3 ) const;
        bool getElement( unsigned int index, osg::Matrix2d& m2 ) const;
        bool getElement( unsigned int index, osg::Matrix3d& m3 ) const;
        bool getElement( unsigned int index, osg::Matrixd& m4 ) const;
        bool getElement( unsigned int index, osg::Matrix2x3d& m2x3 ) const;
        bool getElement( unsigned int index, osg::Matrix2x4d& m2x4 ) const;
        bool getElement( unsigned int index, osg::Matrix3x2d& m3x2 ) const;
        bool getElement( unsigned int index, osg::Matrix3x4d& m3x4 ) const;
        bool getElement( unsigned int index, osg::Matrix4x2d& m4x2 ) const;
        bool getElement( unsigned int index, osg::Matrix4x3d& m4x3 ) const;
        bool getElement( unsigned int index, int& i0, int& i1 ) const;
        bool getElement( unsigned int index, int& i0, int& i1, int& i2 ) const;
        bool getElement( unsigned int index, int& i0, int& i1, int& i2, int& i3 ) const;
        bool getElement( unsigned int index, unsigned int& ui0, unsigned int& ui1 ) const;
        bool getElement( unsigned int index, unsigned int& ui0, unsigned int& ui1, unsigned int& ui2 ) const;
        bool getElement( unsigned int index, unsigned int& ui0, unsigned int& ui1, unsigned int& ui2, unsigned int& ui3 ) const;
        bool getElement( unsigned int index, bool& b0, bool& b1 ) const;
        bool getElement( unsigned int index, bool& b0, bool& b1, bool& b2 ) const;
        bool getElement( unsigned int index, bool& b0, bool& b1, bool& b2, bool& b3 ) const;
        
        
        /** provide typedef for backwards compatibility to OSG-3.2 and other previous versions. */
        typedef UniformCallback Callback;
        

        /** Set the UpdateCallback which allows users to attach customize the updating of an object during the update traversal.*/
        void setUpdateCallback(UniformCallback* uc);

        /** Get the non const UpdateCallback.*/
        UniformCallback* getUpdateCallback() { return _updateCallback.get(); }

        /** Get the const UpdateCallback.*/
        const UniformCallback* getUpdateCallback() const { return _updateCallback.get(); }

        /** Set the EventCallback which allows users to attach customize the updating of an object during the Event traversal.*/
        void setEventCallback(UniformCallback* ec);

        /** Get the non const EventCallback.*/
        UniformCallback* getEventCallback() { return _eventCallback.get(); }

        /** Get the const EventCallback.*/
        const UniformCallback* getEventCallback() const { return _eventCallback.get(); }

        /** Increment the modified count on the Uniform so Programs watching it know it update themselves.
          * NOTE: automatically called during osg::Uniform::set*();
          * you must call if modifying the internal data array directly. */
        inline void dirty() { ++_modifiedCount; }

        /** Set the internal data array for a osg::Uniform */
        bool setArray( FloatArray* array );
        bool setArray( DoubleArray* array );
        bool setArray( IntArray* array );
        bool setArray( UIntArray* array );
        bool setArray( UInt64Array* array );
        bool setArray( Int64Array* array );
        /** Get the internal data array for a float osg::Uniform. */
        FloatArray* getFloatArray() { return _floatArray.get(); }
        const FloatArray* getFloatArray() const { return _floatArray.get(); }

        /** Get the internal data array for a double osg::Uniform. */
        DoubleArray* getDoubleArray() { return _doubleArray.get(); }
        const DoubleArray* getDoubleArray() const { return _doubleArray.get(); }

        /** Get the internal data array for an int osg::Uniform. */
        IntArray* getIntArray() { return _intArray.get(); }
        const IntArray* getIntArray() const { return _intArray.get(); }

        /** Get the internal data array for an unsigned int osg::Uniform. */
        UIntArray* getUIntArray() { return _uintArray.get(); }
        const UIntArray* getUIntArray() const { return _uintArray.get(); }

        /** Get the internal data array for an unsigned int osg::Uniform. */
        UInt64Array* getUInt64Array() { return _uint64Array.get(); }
        const UInt64Array* getUInt64Array() const { return _uint64Array.get(); }

        /** Get the internal data array for an unsigned int osg::Uniform. */
        Int64Array* getInt64Array() { return _int64Array.get(); }
        const Int64Array* getInt64Array() const { return _int64Array.get(); }

        inline void setModifiedCount(unsigned int mc) { _modifiedCount = mc; }
        inline unsigned int getModifiedCount() const { return _modifiedCount; }

        /** Get the number that the Uniform's name maps to uniquely */
        unsigned int getNameID() const;

        void apply(const GLExtensions* ext, GLint location) const;


    protected:

        virtual ~Uniform();
        Uniform& operator=(const Uniform&) { return *this; }

        bool isCompatibleType( Type t ) const;
        // for backward compatibility only
        // see getElement(index, osg::Matrixd &)
        // see setElement(index, osg::Matrixd &)
        bool isCompatibleType( Type t1, Type t2 ) const;
        bool isScalar() const { return _numElements==1; }
        void allocateDataArray();

        void addParent(osg::StateSet* object);
        void removeParent(osg::StateSet* object);

        ParentList _parents;
        friend class osg::StateSet;

        Type                 _type;
        unsigned int         _numElements;
        unsigned int         _nameID;


        // The internal data for osg::Uniforms are stored as an array of
        // getInternalArrayType() of length getInternalArrayNumElements().
        ref_ptr<FloatArray>  _floatArray;
        ref_ptr<DoubleArray> _doubleArray;
        ref_ptr<IntArray>    _intArray;
        ref_ptr<UIntArray>   _uintArray;
        ref_ptr<Int64Array>  _int64Array;
        ref_ptr<UInt64Array> _uint64Array;

        ref_ptr<UniformCallback> _updateCallback;
        ref_ptr<UniformCallback> _eventCallback;

        unsigned int         _modifiedCount;
};

}

#endif