/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library 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 
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSG_VERTEXPROGRAM
#define OSG_VERTEXPROGRAM 1

#include <osg/StateAttribute>
#include <osg/Vec4>
#include <osg/Matrix>
#include <osg/buffered_value>

#include <map>
#include <string>

// if not defined by gl.h use the definition found in:
// http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_program.txt
#ifndef GL_ARB_vertex_program
#define GL_VERTEX_PROGRAM_ARB                              0x8620
#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB                   0x8642
#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB                     0x8643
#define GL_COLOR_SUM_ARB                                   0x8458
#define GL_PROGRAM_FORMAT_ASCII_ARB                        0x8875
#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB                 0x8622
#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB                    0x8623
#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB                  0x8624
#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB                    0x8625
#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB              0x886A
#define GL_CURRENT_VERTEX_ATTRIB_ARB                       0x8626
#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB                 0x8645
#define GL_PROGRAM_LENGTH_ARB                              0x8627
#define GL_PROGRAM_FORMAT_ARB                              0x8876
#define GL_PROGRAM_BINDING_ARB                             0x8677
#define GL_PROGRAM_INSTRUCTIONS_ARB                        0x88A0
#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB                    0x88A1
#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB                 0x88A2
#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB             0x88A3
#define GL_PROGRAM_TEMPORARIES_ARB                         0x88A4
#define GL_MAX_PROGRAM_TEMPORARIES_ARB                     0x88A5
#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB                  0x88A6
#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB              0x88A7
#define GL_PROGRAM_PARAMETERS_ARB                          0x88A8
#define GL_MAX_PROGRAM_PARAMETERS_ARB                      0x88A9
#define GL_PROGRAM_NATIVE_PARAMETERS_ARB                   0x88AA
#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB               0x88AB
#define GL_PROGRAM_ATTRIBS_ARB                             0x88AC
#define GL_MAX_PROGRAM_ATTRIBS_ARB                         0x88AD
#define GL_PROGRAM_NATIVE_ATTRIBS_ARB                      0x88AE
#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB                  0x88AF
#define GL_PROGRAM_ADDRESS_REGISTERS_ARB                   0x88B0
#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB               0x88B1
#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB            0x88B2
#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB        0x88B3
#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB                0x88B4
#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB                  0x88B5
#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB                 0x88B6
#define GL_PROGRAM_STRING_ARB                              0x8628
#define GL_PROGRAM_ERROR_POSITION_ARB                      0x864B
#define GL_CURRENT_MATRIX_ARB                              0x8641
#define GL_TRANSPOSE_CURRENT_MATRIX_ARB                    0x88B7
#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB                  0x8640
#define GL_MAX_VERTEX_ATTRIBS_ARB                          0x8869
#define GL_MAX_PROGRAM_MATRICES_ARB                        0x862F
#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB              0x862E
#define GL_PROGRAM_ERROR_STRING_ARB                        0x8874
#define GL_MATRIX0_ARB                                     0x88C0
#define GL_MATRIX1_ARB                                     0x88C1
#define GL_MATRIX2_ARB                                     0x88C2
#define GL_MATRIX3_ARB                                     0x88C3
#define GL_MATRIX4_ARB                                     0x88C4
#define GL_MATRIX5_ARB                                     0x88C5
#define GL_MATRIX6_ARB                                     0x88C6
#define GL_MATRIX7_ARB                                     0x88C7
#define GL_MATRIX8_ARB                                     0x88C8
#define GL_MATRIX9_ARB                                     0x88C9
#define GL_MATRIX10_ARB                                    0x88CA
#define GL_MATRIX11_ARB                                    0x88CB
#define GL_MATRIX12_ARB                                    0x88CC
#define GL_MATRIX13_ARB                                    0x88CD
#define GL_MATRIX14_ARB                                    0x88CE
#define GL_MATRIX15_ARB                                    0x88CF
#define GL_MATRIX16_ARB                                    0x88D0
#define GL_MATRIX17_ARB                                    0x88D1
#define GL_MATRIX18_ARB                                    0x88D2
#define GL_MATRIX19_ARB                                    0x88D3
#define GL_MATRIX20_ARB                                    0x88D4
#define GL_MATRIX21_ARB                                    0x88D5
#define GL_MATRIX22_ARB                                    0x88D6
#define GL_MATRIX23_ARB                                    0x88D7
#define GL_MATRIX24_ARB                                    0x88D8
#define GL_MATRIX25_ARB                                    0x88D9
#define GL_MATRIX26_ARB                                    0x88DA
#define GL_MATRIX27_ARB                                    0x88DB
#define GL_MATRIX28_ARB                                    0x88DC
#define GL_MATRIX29_ARB                                    0x88DD
#define GL_MATRIX30_ARB                                    0x88DE
#define GL_MATRIX31_ARB                                    0x88DF
#endif


namespace osg {



/** VertexProgram - encapsulates the OpenGL ARB vertex program state. */
class OSG_EXPORT VertexProgram : public StateAttribute
{
    public:

        VertexProgram();

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

        META_StateAttribute(osg, VertexProgram, VERTEXPROGRAM);

        /** Return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */
        virtual int compare(const osg::StateAttribute& sa) const
        {
            // check the types are equal and then create the rhs variable
            // used by the COMPARE_StateAttribute_Parameter macros below.
            COMPARE_StateAttribute_Types(VertexProgram,sa)

            // compare each parameter in turn against the rhs.
            COMPARE_StateAttribute_Parameter(_vertexProgram)

            return 0; // passed all the above comparison macros, must be equal.
        }

        virtual bool getModeUsage(StateAttribute::ModeUsage& usage) const
        {
            usage.usesMode(GL_VERTEX_PROGRAM_ARB);
            return true;
        }

        // data access methods.

        /** Get the handle to the vertex program ID for the current context. */
        inline GLuint& getVertexProgramID(unsigned int contextID) const
        {
            return _vertexProgramIDList[contextID];
        }

        
        /** Set the vertex program using a C style string. */
        inline void setVertexProgram( const char* program ) 
        { 
            _vertexProgram = program; 
            dirtyVertexProgramObject();
        }

        /** Set the vertex program using C++ style string. */
        inline void setVertexProgram( const std::string& program )
        {
            _vertexProgram = program;
            dirtyVertexProgramObject();
        }

        /** Get the vertex program. */
        inline const std::string& getVertexProgram() const { return _vertexProgram; }

        /** Set Program Parameters */
        inline void setProgramLocalParameter(const GLuint index, const Vec4& p)
        {
            _programLocalParameters[index] = p;
        }

        typedef std::map<GLuint,Vec4> LocalParamList;

        /** Set list of Program Parameters */
        inline void setLocalParameters(const LocalParamList& lpl) { _programLocalParameters = lpl; }

        /** Get list of Program Parameters */
        inline LocalParamList& getLocalParameters() { return _programLocalParameters; }

        /** Get const list of Program Parameters */
        inline const LocalParamList& getLocalParameters() const { return _programLocalParameters; }

        /** Matrix */
        inline void setMatrix(const GLenum mode, const Matrix& matrix)
        {
            _matrixList[mode] = matrix;
        }

        typedef std::map<GLenum,Matrix> MatrixList;

        /** Set list of Matrices */
        inline void setMatrices(const MatrixList& matrices) { _matrixList = matrices; }

        /** Get list of Matrices */
        inline MatrixList& getMatrices() { return _matrixList; }

        /** Get list of Matrices */
        inline const MatrixList& getMatrices() const { return _matrixList; }

        /** Force a recompile on next apply() of associated OpenGL vertex program objects. */
        void dirtyVertexProgramObject();        

        /** Use deleteVertexProgramObject instead of glDeletePrograms to allow
          * OpenGL Vertex Program objects to cached until they can be deleted
          * by the OpenGL context in which they were created, specified
          * by contextID.
        */
        static void deleteVertexProgramObject(unsigned int contextID,GLuint handle);

        /** Flush all the cached vertex programs which need to be deleted
          * in the OpenGL context related to contextID.
        */
        static void flushDeletedVertexProgramObjects(unsigned int contextID,double currentTime, double& availableTime);

        /** discard all the cached vertex programs which need to be deleted
          * in the OpenGL context related to contextID.
          * Note, unlike flush no OpenGL calls are made, instead the handles are all removed.
          * this call is useful for when an OpenGL context has been destroyed.
        */
        static void discardDeletedVertexProgramObjects(unsigned int contextID);

        virtual void apply(State& state) const;

        virtual void compileGLObjects(State& state) const { apply(state); }

        /** Resize any per context GLObject buffers to specified size. */
        virtual void resizeGLObjectBuffers(unsigned int maxSize);

        /** Release any OpenGL objects in specified graphics context if State
          * object is passed, otherwise release OpenGL objects for all graphics contexts if
          * State object pointer is NULL.
        */
        virtual void releaseGLObjects(State* state=0) const;

        /** Extensions class which encapsulates the querying of extensions and
          * associated function pointers, and provide convenience wrappers to 
          * check for the extensions or use the associated functions.
        */
        class OSG_EXPORT Extensions : public osg::Referenced
        {
            public:
                Extensions(unsigned int contextID);

                Extensions(const Extensions& rhs);
                
                void lowestCommonDenominator(const Extensions& rhs);
                
                void setupGLExtensions(unsigned int contextID);

                void setVertexProgramSupported(bool flag) { _isVertexProgramSupported=flag; }
                bool isVertexProgramSupported() const { return _isVertexProgramSupported; }

                void glBindProgram(GLenum target, GLuint id) const;
                void glGenPrograms(GLsizei n, GLuint *programs) const;
                void glDeletePrograms(GLsizei n, GLuint *programs) const;
                void glProgramString(GLenum target, GLenum format, GLsizei len, const void *string) const; 
                void glProgramLocalParameter4fv(GLenum target, GLuint index, const GLfloat *params) const;

            protected:

                ~Extensions() {}
                
                bool _isVertexProgramSupported;
                
                typedef void (GL_APIENTRY * BindProgramProc) (GLenum target, GLuint id);
                typedef void (GL_APIENTRY * GenProgramsProc) (GLsizei n, GLuint *programs);
                typedef void (GL_APIENTRY * DeleteProgramsProc) (GLsizei n, GLuint *programs);
                typedef void (GL_APIENTRY * ProgramStringProc) (GLenum target, GLenum format, GLsizei len, const void *string); 
                typedef void (GL_APIENTRY * ProgramLocalParameter4fvProc) (GLenum target, GLuint index, const GLfloat *params);

                BindProgramProc _glBindProgram;
                GenProgramsProc _glGenPrograms;
                DeleteProgramsProc _glDeletePrograms;
                ProgramStringProc _glProgramString;
                ProgramLocalParameter4fvProc _glProgramLocalParameter4fv;
 
        };
        
        /** Function to call to get the extension of a specified context.
          * If the Extension object for that context has not yet been created
          * and the 'createIfNotInitalized' flag been set to false then returns NULL.
          * If 'createIfNotInitalized' is true then the Extensions object is
          * automatically created. However, in this case the extension object
          * will only be created with the graphics context associated with ContextID.
        */
        static Extensions* getExtensions(unsigned int contextID,bool createIfNotInitalized);

        /** The setExtensions method allows users to override the extensions across graphics contexts.
          * Typically used when you have different extensions supported across graphics pipes
          * but need to ensure that they all use the same low common denominator extensions.
        */
        static void setExtensions(unsigned int contextID,Extensions* extensions);


    protected:


        virtual ~VertexProgram();

        typedef buffered_value<GLuint> VertexProgramIDList;
        mutable VertexProgramIDList _vertexProgramIDList;

        std::string     _vertexProgram;

        LocalParamList  _programLocalParameters;

        MatrixList  _matrixList;
};



}

#endif

