/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 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_PRIMITIVESET
#define OSG_PRIMITIVESET 1

#include <osg/GL>
#include <osg/Object>
#include <osg/buffered_value>
#include <osg/Vec2>
#include <osg/Vec3>
#include <osg/Vec4>

#include <vector>

namespace osg {

class State;    
class PrimitiveFunctor
{
public:

    virtual ~PrimitiveFunctor() {}

    virtual void setVertexArray(unsigned int count,const Vec2* vertices) = 0;
    virtual void setVertexArray(unsigned int count,const Vec3* vertices) = 0;
    virtual void setVertexArray(unsigned int count,const Vec4* vertices) = 0;

    virtual void drawArrays(GLenum mode,GLint first,GLsizei count) = 0;
    virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices) = 0;
    virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) = 0;
    virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) = 0;

    virtual void begin(GLenum mode) = 0;
    virtual void vertex(const Vec2& vert) = 0;
    virtual void vertex(const Vec3& vert) = 0;
    virtual void vertex(const Vec4& vert) = 0;
    virtual void vertex(float x,float y) = 0;
    virtual void vertex(float x,float y,float z) = 0;
    virtual void vertex(float x,float y,float z,float w) = 0;
    virtual void end() = 0;
};

class PrimitiveIndexFunctor
{
public:

    virtual ~PrimitiveIndexFunctor() {}

    virtual void setVertexArray(unsigned int count,const Vec2* vertices) = 0;
    virtual void setVertexArray(unsigned int count,const Vec3* vertices) = 0;
    virtual void setVertexArray(unsigned int count,const Vec4* vertices) = 0;

    virtual void drawArrays(GLenum mode,GLint first,GLsizei count) = 0;
    virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices) = 0;
    virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) = 0;
    virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) = 0;

    virtual void begin(GLenum mode) = 0;
    virtual void vertex(unsigned int pos) = 0;
    virtual void end() = 0;
};

#ifndef _MSC_VER

typedef std::vector<GLsizei> VectorSizei;
typedef std::vector<GLubyte> VectorUByte;
typedef std::vector<GLushort> VectorUShort;
typedef std::vector<GLuint> VectorUInt;

#else // _MSC_VER

// Following Vector wrapper classes are work arounds for MS linker problems with
// multiply implemented methods.
//
// An alternative, sent in by Clay Fowler, is workaround in VS to prevent the problem:
// the following changes have to be made to the project to make it compile, 
// but NO changes are required to the actual source code:
// In the osgUtil project, go to the project properties, select the Linker/Command Line property page, 
// and add the following switch in the "Additional Options" field:
// FORCE:MULTIPLE

class VectorSizei: public std::vector<GLsizei> {
	typedef std::vector<value_type> inherited;
public:
	VectorSizei(): inherited() {}
	explicit VectorSizei(size_type n): inherited(n) {}
	VectorSizei(const VectorSizei &copy): inherited(copy) {}
	//VectorSizei(value_type *beg_, value_type *end_): inherited(beg_, end_) {}
	template<class InputIterator>
	VectorSizei(InputIterator beg_, InputIterator end_): inherited(beg_, end_) {}
};

class VectorUByte: public std::vector<GLubyte> {
	typedef std::vector<value_type> inherited;
public:
	VectorUByte(): inherited() {}
	explicit VectorUByte(size_type n): inherited(n) {}
	VectorUByte(const VectorUByte &copy): inherited(copy) {}
	//VectorUByte(value_type *beg_, value_type *end_): inherited(beg_, end_) {}
	template<class InputIterator>
	VectorUByte(InputIterator beg_, InputIterator end_): inherited(beg_, end_) {}
};

class VectorUShort: public std::vector<GLushort> {
	typedef std::vector<value_type> inherited;
public:
	VectorUShort(): inherited() {}
	explicit VectorUShort(size_type n): inherited(n) {}
	VectorUShort(const VectorUShort &copy): inherited(copy) {}
	//VectorUShort(value_type *beg_, value_type *end_): inherited(beg_, end_) {}
	template<class InputIterator>
	VectorUShort(InputIterator beg_, InputIterator end_): inherited(beg_, end_) {}
};

class VectorUInt: public std::vector<GLuint> {
	typedef std::vector<value_type> inherited;
public:
	VectorUInt(): inherited() {}
	explicit VectorUInt(size_type n): inherited(n) {}
	VectorUInt(const VectorUInt &copy): inherited(copy) {}
	//VectorUInt(value_type *beg_, value_type *end_): inherited(beg_, end_) {}
	template<class InputIterator>
	VectorUInt(InputIterator beg_, InputIterator end_): inherited(beg_, end_) {}
};

#endif


class PrimitiveSet : public Object
{
    public:
    
        enum Type
        {
            PrimitiveType,
            DrawArraysPrimitiveType,
            DrawArrayLengthsPrimitiveType,
            DrawElementsUBytePrimitiveType,
            DrawElementsUShortPrimitiveType,
            DrawElementsUIntPrimitiveType
        };

        enum Mode
        {
            POINTS = GL_POINTS,
            LINES = GL_LINES,
            LINE_STRIP = GL_LINE_STRIP,
            LINE_LOOP = GL_LINE_LOOP,
            TRIANGLES = GL_TRIANGLES,
            TRIANGLE_STRIP = GL_TRIANGLE_STRIP,
            TRIANGLE_FAN = GL_TRIANGLE_FAN,
            QUADS = GL_QUADS,
            QUAD_STRIP = GL_QUAD_STRIP,
            POLYGON = GL_POLYGON
        };

        PrimitiveSet(Type primType=PrimitiveType,GLenum mode=0):
            _primitiveType(primType),
            _mode(mode),
            _modifiedCount(0) {}
    
        PrimitiveSet(const PrimitiveSet& prim,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            Object(prim,copyop),
            _primitiveType(prim._primitiveType),
            _mode(prim._mode),
            _modifiedCount(0) {}

        virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const PrimitiveSet*>(obj)!=NULL; }
        virtual const char* libraryName() const { return "osg"; }
        virtual const char* className() const { return "PrimitiveSet"; }
        
        Type                    getType() const { return _primitiveType; }
        virtual const GLvoid*   getDataPointer() const { return 0; }
        virtual unsigned int    getTotalDataSize() const { return 0; }
        virtual bool            supportsBufferObject() const { return false; }
            
        void setMode(GLenum mode) { _mode = mode; }
        GLenum getMode() const { return _mode; }

        virtual void draw(State& state, bool useVertexBufferObjects) const = 0;
        
        virtual void accept(PrimitiveFunctor& functor) const = 0;
        virtual void accept(PrimitiveIndexFunctor& functor) const = 0;
        
        virtual unsigned int index(unsigned int pos) const = 0;
        virtual unsigned int getNumIndices() const = 0;
        virtual void offsetIndices(int offset) = 0;

        virtual unsigned int getNumPrimitives() const 
        {
            switch(_mode)
            {
                case(POINTS): return getNumIndices();
                case(LINES): return getNumIndices()/2;
                case(TRIANGLES): return getNumIndices()/3;
                case(QUADS): return getNumIndices()/4;
                case(LINE_STRIP):
                case(LINE_LOOP):
                case(TRIANGLE_STRIP):
                case(TRIANGLE_FAN):
                case(QUAD_STRIP):
                case(POLYGON): return 1;
            }
            return 0;
        }

        /** Dirty the primitive, which increments the modified count, to force buffer objects to update. */
        inline void dirty() { ++_modifiedCount; }      
      
        /** Set the modified count value.*/
        inline void setModifiedCount(unsigned int value) { _modifiedCount=value; }

        /** Get modified count value.*/
        inline unsigned int getModifiedCount() const { return _modifiedCount; }

    protected:

        virtual ~PrimitiveSet() {}

        Type            _primitiveType;
        GLenum          _mode;
        unsigned int    _modifiedCount;
};

class OSG_EXPORT DrawArrays : public PrimitiveSet
{
    public:

        DrawArrays(GLenum mode=0):
            PrimitiveSet(DrawArraysPrimitiveType,mode),
            _first(0),
            _count(0) {}
    
        DrawArrays(GLenum mode, GLint first, GLsizei count):
            PrimitiveSet(DrawArraysPrimitiveType,mode),
            _first(first),
            _count(count) {}

        DrawArrays(const DrawArrays& da,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            PrimitiveSet(da,copyop),
            _first(da._first),
            _count(da._count) {}

        virtual Object* cloneType() const { return new DrawArrays(); }
        virtual Object* clone(const CopyOp& copyop) const { return new DrawArrays(*this,copyop); }        
        virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const DrawArrays*>(obj)!=NULL; }
        virtual const char* libraryName() const { return "osg"; }
        virtual const char* className() const { return "DrawArrays"; }
        

        void set(GLenum mode,GLint first, GLsizei count)
        {
            _mode = mode;
            _first = first;
            _count = count;
        }

        void setFirst(GLint first) { _first = first; }
        GLint getFirst() const { return _first; }
        
        void setCount(GLsizei count) { _count = count; }
        GLsizei getCount() const { return _count; }

        virtual void draw(State& state, bool useVertexBufferObjects) const;
        
        virtual void accept(PrimitiveFunctor& functor) const;
        virtual void accept(PrimitiveIndexFunctor& functor) const;
        
        virtual unsigned int getNumIndices() const { return _count; }
        virtual unsigned int index(unsigned int pos) const { return _first+pos; }
        virtual void offsetIndices(int offset) { _first += offset; }

    protected:

        virtual ~DrawArrays() {}

        GLint   _first;
        GLsizei _count;
};

class OSG_EXPORT DrawArrayLengths : public PrimitiveSet, public VectorSizei
{
    public:

        DrawArrayLengths(GLenum mode=0):
            PrimitiveSet(DrawArrayLengthsPrimitiveType,mode),
            _first(0) {}
    
        DrawArrayLengths(const DrawArrayLengths& dal,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            PrimitiveSet(dal,copyop),
            VectorSizei(dal),
            _first(dal._first) {}

        DrawArrayLengths(GLenum mode, GLint first, unsigned int no, GLsizei* ptr) : 
            PrimitiveSet(DrawArrayLengthsPrimitiveType,mode),
            VectorSizei(ptr,ptr+no),
            _first(first) {}

        DrawArrayLengths(GLenum mode,GLint first, unsigned int no) : 
            PrimitiveSet(DrawArrayLengthsPrimitiveType,mode),
            VectorSizei(no),
            _first(first) {}

        DrawArrayLengths(GLenum mode,GLint first) : 
            PrimitiveSet(DrawArrayLengthsPrimitiveType,mode),
            VectorSizei(),
            _first(first) {}


        virtual Object* cloneType() const { return new DrawArrayLengths(); }
        virtual Object* clone(const CopyOp& copyop) const { return new DrawArrayLengths(*this,copyop); }        
        virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const DrawArrayLengths*>(obj)!=NULL; }
        virtual const char* libraryName() const { return "osg"; }
        virtual const char* className() const { return "DrawArrayLengths"; }
        

        void setFirst(GLint first) { _first = first; }
        GLint getFirst() const { return _first; }
        
        virtual void draw(State& state, bool useVertexBufferObjects) const;
        
        virtual void accept(PrimitiveFunctor& functor) const;
        virtual void accept(PrimitiveIndexFunctor& functor) const;
        
        virtual unsigned int getNumIndices() const;
        virtual unsigned int index(unsigned int pos) const { return _first+pos; }
        virtual void offsetIndices(int offset) { _first += offset; }

        virtual unsigned int getNumPrimitives() const 
        {
            switch(_mode)
            {
                case(POINTS): return getNumIndices();
                case(LINES): return getNumIndices()/2;
                case(TRIANGLES): return getNumIndices()/3;
                case(QUADS): return getNumIndices()/4;
                case(LINE_STRIP):
                case(LINE_LOOP):
                case(TRIANGLE_STRIP):
                case(TRIANGLE_FAN):
                case(QUAD_STRIP):
                case(POLYGON): return size();
            }
            return 0;
        }

    protected:

        virtual ~DrawArrayLengths() {}

        GLint   _first;
};

class OSG_EXPORT DrawElementsUByte : public PrimitiveSet, public VectorUByte
{
    public:

        DrawElementsUByte(GLenum mode=0):
            PrimitiveSet(DrawElementsUBytePrimitiveType,mode) {}
    
        DrawElementsUByte(const DrawElementsUByte& array,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            PrimitiveSet(array,copyop),
            VectorUByte(array) {}

        DrawElementsUByte(GLenum mode,unsigned int no,GLubyte* ptr) : 
            PrimitiveSet(DrawElementsUBytePrimitiveType,mode),
            VectorUByte(ptr,ptr+no) {}

        DrawElementsUByte(GLenum mode,unsigned int no) : 
            PrimitiveSet(DrawElementsUBytePrimitiveType,mode),
            VectorUByte(no) {}

        virtual Object* cloneType() const { return new DrawElementsUByte(); }
        virtual Object* clone(const CopyOp& copyop) const { return new DrawElementsUByte(*this,copyop); }        
        virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const DrawElementsUByte*>(obj)!=NULL; }
        virtual const char* libraryName() const { return "osg"; }
        virtual const char* className() const { return "DrawElementsUByte"; }

        virtual const GLvoid*   getDataPointer() const { return empty()?0:&front(); }
        virtual unsigned int    getTotalDataSize() const { return size(); }
        virtual bool            supportsBufferObject() const { return false; }

        virtual void draw(State& state, bool useVertexBufferObjects) const ;
        
        virtual void accept(PrimitiveFunctor& functor) const;
        virtual void accept(PrimitiveIndexFunctor& functor) const;

        virtual unsigned int getNumIndices() const { return size(); }
        virtual unsigned int index(unsigned int pos) const { return (*this)[pos]; }
        virtual void offsetIndices(int offset);

    protected:

        typedef osg::buffered_value<GLuint> GLObjectList;
        mutable GLObjectList    _vboList;

        virtual ~DrawElementsUByte();
};


class OSG_EXPORT DrawElementsUShort : public PrimitiveSet, public VectorUShort
{
    public:

        DrawElementsUShort(GLenum mode=0):
            PrimitiveSet(DrawElementsUShortPrimitiveType,mode) {}
    
        DrawElementsUShort(const DrawElementsUShort& array,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            PrimitiveSet(array,copyop),
            VectorUShort(array) {}

        DrawElementsUShort(GLenum mode,unsigned int no,GLushort* ptr) : 
            PrimitiveSet(DrawElementsUShortPrimitiveType,mode),
            VectorUShort(ptr,ptr+no) {}

        DrawElementsUShort(GLenum mode,unsigned int no) : 
            PrimitiveSet(DrawElementsUShortPrimitiveType,mode),
            VectorUShort(no) {}

        template <class InputIterator>
        DrawElementsUShort(GLenum mode, InputIterator first,InputIterator last) : 
            PrimitiveSet(DrawElementsUShortPrimitiveType,mode),
            VectorUShort(first,last) {}

        virtual Object* cloneType() const { return new DrawElementsUShort(); }
        virtual Object* clone(const CopyOp& copyop) const { return new DrawElementsUShort(*this,copyop); }        
        virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const DrawElementsUShort*>(obj)!=NULL; }
        virtual const char* libraryName() const { return "osg"; }
        virtual const char* className() const { return "DrawElementsUShort"; }

        virtual const GLvoid*   getDataPointer() const { return empty()?0:&front(); }
        virtual unsigned int    getTotalDataSize() const { return 2*size(); }
        virtual bool            supportsBufferObject() const { return false; }

        virtual void draw(State& state, bool useVertexBufferObjects) const;
        
        virtual void accept(PrimitiveFunctor& functor) const;
        virtual void accept(PrimitiveIndexFunctor& functor) const;

        virtual unsigned int getNumIndices() const { return size(); }
        virtual unsigned int index(unsigned int pos) const { return (*this)[pos]; }
        virtual void offsetIndices(int offset);

    protected:

        typedef osg::buffered_value<GLuint> GLObjectList;
        mutable GLObjectList    _vboList;

        virtual ~DrawElementsUShort();
};

class OSG_EXPORT DrawElementsUInt : public PrimitiveSet, public VectorUInt
{
    public:

        DrawElementsUInt(GLenum mode=0):
            PrimitiveSet(DrawElementsUIntPrimitiveType,mode) {}
    
        DrawElementsUInt(const DrawElementsUInt& array,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            PrimitiveSet(array,copyop),
            VectorUInt(array) {}

        DrawElementsUInt(GLenum mode,unsigned int no,GLuint* ptr) : 
            PrimitiveSet(DrawElementsUIntPrimitiveType,mode),
            VectorUInt(ptr,ptr+no) {}

        DrawElementsUInt(GLenum mode,unsigned int no) : 
            PrimitiveSet(DrawElementsUIntPrimitiveType,mode),
            VectorUInt(no) {}

        template <class InputIterator>
        DrawElementsUInt(GLenum mode, InputIterator first,InputIterator last) : 
            PrimitiveSet(DrawElementsUIntPrimitiveType,mode),
            VectorUInt(first,last) {}

        virtual Object* cloneType() const { return new DrawElementsUInt(); }
        virtual Object* clone(const CopyOp& copyop) const { return new DrawElementsUInt(*this,copyop); }        
        virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const DrawElementsUInt*>(obj)!=NULL; }
        virtual const char* libraryName() const { return "osg"; }
        virtual const char* className() const { return "DrawElementsUInt"; }

        virtual const GLvoid*   getDataPointer() const { return empty()?0:&front(); }
        virtual unsigned int    getTotalDataSize() const { return 4*size(); }
        virtual bool            supportsBufferObject() const { return false; }

        virtual void draw(State& state, bool useVertexBufferObjects) const;
        
        virtual void accept(PrimitiveFunctor& functor) const;
        virtual void accept(PrimitiveIndexFunctor& functor) const;

        virtual unsigned int getNumIndices() const { return size(); }
        virtual unsigned int index(unsigned int pos) const { return (*this)[pos]; }
        virtual void offsetIndices(int offset);

    protected:

        virtual ~DrawElementsUInt();

        typedef osg::buffered_value<GLuint> GLObjectList;
        mutable GLObjectList    _vboList;
};

}

#endif
