/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield 
 * Copyright (C) 2003-2005 3Dlabs Inc. Ltd.
 *
 * This application is open source and may be redistributed and/or modified   
 * freely and without restriction, both in commericial and non commericial
 * 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 2005-05-05
*/

#ifndef OSG_UNIFORM
#define OSG_UNIFORM 1

#include <osg/ref_ptr>
#include <osg/Vec2>
#include <osg/Vec3>
#include <osg/Vec4>
#include <osg/Matrix>
#include <osg/GL2Extensions>

#include <string>
#include <vector>

namespace osg {

// forward declare
class GL2Extensions;
class NodeVisitor;

/** 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,
            INT = GL_INT,
            INT_VEC2 = GL_INT_VEC2,
            INT_VEC3 = GL_INT_VEC3,
            INT_VEC4 = GL_INT_VEC4,
            BOOL = GL_BOOL,
            BOOL_VEC2 = GL_BOOL_VEC2,
            BOOL_VEC3 = GL_BOOL_VEC3,
            BOOL_VEC4 = GL_BOOL_VEC4,
            FLOAT_MAT2 = GL_FLOAT_MAT2,
            FLOAT_MAT3 = GL_FLOAT_MAT3,
            FLOAT_MAT4 = GL_FLOAT_MAT4,
            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,
            UNDEFINED = -1
        };

    public:
        Uniform();
        Uniform( Type type, const std::string& name );

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

        META_Object(osg, Uniform);




        bool setType( Type t );
        bool setName( const std::string& name );

        /** Get the name of glUniform. */
        const std::string& getName() const { return _name; }

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

        /** Return the name of a Type enum as string. */
        static const char* getTypename( 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 );

        /** convenient construction w/ assignment */
        explicit Uniform( const char* name, float f );
        explicit Uniform( const char* name, int i );
        explicit Uniform( const char* name, bool b );
        Uniform( const char* name, const osg::Vec2& v2 );
        Uniform( const char* name, const osg::Vec3& v3 );
        Uniform( const char* name, const osg::Vec4& v4 );
        //TODO Uniform( const char* name, const osg::Matrix2& m2 );
        //TODO Uniform( const char* name, const osg::Matrix3& m3 );
        Uniform( const char* name, const osg::Matrixf& m4 );
        Uniform( const char* name, const osg::Matrixd& m4 );
        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, 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 _parents.size(); }


        /** value assignment */
        bool set( float f );
        bool set( int i );
        bool set( bool b );
        bool set( const osg::Vec2& v2 );
        bool set( const osg::Vec3& v3 );
        bool set( const osg::Vec4& v4 );
        //TODO bool set( const osg::Matrix2& m2 );
        //TODO bool set( const osg::Matrix3& m3 );
        bool set( const osg::Matrixf& m4 );
        bool set( const osg::Matrixd& m4 );
        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( bool b0, bool b1 );
        bool set( bool b0, bool b1, bool b2 );
        bool set( bool b0, bool b1, bool b2, bool b3 );

        /** value query */
        bool get( float& f ) const;
        bool get( int& i ) const;
        bool get( bool& b ) const;
        bool get( osg::Vec2& v2 ) const;
        bool get( osg::Vec3& v3 ) const;
        bool get( osg::Vec4& v4 ) const;
        //TODO bool get( osg::Matrix2& m2 ) const;
        //TODO bool get( osg::Matrix3& m3 ) const;
        bool get( osg::Matrixf& m4 ) const;
        bool get( osg::Matrixd& m4 ) 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( 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;
        

        struct Callback : public virtual osg::Object
        {
            Callback() {}

            Callback(const Callback&,const CopyOp&) {}

            META_Object(osg,Callback);

            /** do customized update code.*/
            virtual void operator () (Uniform*, NodeVisitor*) {}
        };

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

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

        /** Get the const UpdateCallback.*/
        const Callback* 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(Callback* ec);

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

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

        /** Increment the modified count on the Uniform so ProgramObjects watching it know it update themselves.
          * note, autoatmatically call bet Uniform::set(*). */
        inline void dirty() { ++_modifiedCount; }

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


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


    protected:
    
        virtual ~Uniform() {}
        Uniform& operator=(const Uniform&) { return *this; }    // disallowed

        bool isCompatibleType( Type t ) const;

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

        ParentList _parents;
        friend class osg::StateSet;

        Type _type;
        std::string _name;

        union
        {
            GLfloat f1;         // float
            GLfloat f2[2];      // vec2
            GLfloat f3[3];      // vec3
            GLfloat f4[4];      // vec4, mat2
            GLfloat f9[9];      // mat3
            GLfloat f16[16];    // mat4
            GLint i1;           // int, bool, sampler*
            GLint i2[2];        // ivec2, bvec2
            GLint i3[3];        // ivec3, bvec3
            GLint i4[4];        // ivec4, bvec4
        } _data;

        ref_ptr<Callback>   _updateCallback;
        ref_ptr<Callback>   _eventCallback;
        
        unsigned int        _modifiedCount;

};

}

#endif

/*EOF*/
