/* -*-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_ARRAY
#define OSG_ARRAY 1

#include <vector>

#include <osg/Vec2>
#include <osg/Vec3>
#include <osg/Vec4>
#include <osg/UByte4>

#include <osg/Object>
#include <osg/GL>

namespace osg {

class ArrayVisitor;
class ConstArrayVisitor;

class ValueVisitor;
class ConstValueVisitor;

class OSG_EXPORT Array : public Object
{

    public:
    
        enum Type
        {
            ArrayType = 0,
            ByteArrayType     = 1,
            ShortArrayType    = 2,
            IntArrayType      = 3,
            UByteArrayType    = 4,
            UShortArrayType   = 5,
            UIntArrayType     = 6,
            UByte4ArrayType   = 7,
            FloatArrayType    = 8,
            Vec2ArrayType     = 9,
            Vec3ArrayType     = 10,
            Vec4ArrayType     = 11
        };

        Array(Type arrayType=ArrayType,GLint dataSize=0,GLenum dataType=0):
            _arrayType(arrayType),
            _dataSize(dataSize),
            _dataType(dataType),
            _modifiedCount(0) {}
    
        Array(const Array& array,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            Object(array,copyop),
            _arrayType(array._arrayType),
            _dataSize(array._dataSize),
            _dataType(array._dataType),
            _modifiedCount(0) {}

        virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const Array*>(obj)!=NULL; }
        virtual const char* libraryName() const { return "osg"; }
        virtual const char* className() const;
        
        virtual void accept(ArrayVisitor&) = 0;
        virtual void accept(ConstArrayVisitor&) const = 0;

        virtual void accept(unsigned int index,ValueVisitor&) = 0;
        virtual void accept(unsigned int index,ConstValueVisitor&) const = 0;

        /** Return -1 if lhs element is less than rhs element, 0 if equal,
          * 1 if lhs element is greater than rhs element. */
        virtual int compare(unsigned int lhs,unsigned int rhs) const = 0;

        Type                    getType() const { return _arrayType; }
        GLint                   getDataSize() const { return _dataSize; }
        GLenum                  getDataType() const { return _dataType; }
        virtual const GLvoid*   getDataPointer() const = 0;
        virtual unsigned int    getTotalDataSize() const = 0;
        virtual unsigned int    getNumElements() const = 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 ~Array() {}

        Type        _arrayType;
        GLint       _dataSize;
        GLenum      _dataType;
        unsigned int    _modifiedCount;
};

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
class TemplateArray : public Array, public std::vector<T>
{
    public:
    
        TemplateArray() : Array(ARRAYTYPE,DataSize,DataType) {}

        TemplateArray(const TemplateArray& ta,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            Array(ta,copyop),
            std::vector<T>(ta) {}
        
        TemplateArray(unsigned int no) : 
            Array(ARRAYTYPE,DataSize,DataType),
            std::vector<T>(no) {}

        TemplateArray(unsigned int no,T* ptr) : 
            Array(ARRAYTYPE,DataSize,DataType),
            std::vector<T>(ptr,ptr+no) {}

        template <class InputIterator>
        TemplateArray(InputIterator first,InputIterator last) : 
            Array(ARRAYTYPE,DataSize,DataType),
            std::vector<T>(first,last) {}        

        TemplateArray& operator = (const TemplateArray& array)
        {
            if (this==&array) return *this;
            assign(array.begin(),array.end());
            return *this;
        } 

        virtual Object* cloneType() const { return new TemplateArray(); }
        virtual Object* clone(const CopyOp& copyop) const { return new TemplateArray(*this,copyop); }        

        inline virtual void accept(ArrayVisitor& av);
        inline virtual void accept(ConstArrayVisitor& av) const;

        inline virtual void accept(unsigned int index,ValueVisitor& vv);
        inline virtual void accept(unsigned int index,ConstValueVisitor& vv) const;

        virtual int compare(unsigned int lhs,unsigned int rhs) const
        {
            const T& elem_lhs = (*this)[lhs];
            const T& elem_rhs = (*this)[rhs];
            if (elem_lhs<elem_rhs) return -1;
            if (elem_rhs<elem_lhs) return 1;
            return 0;
        }
        
        virtual const GLvoid*   getDataPointer() const { if (!this->empty()) return &this->front(); else return 0; }
        virtual unsigned int    getTotalDataSize() const { return this->size()*sizeof(T); }
        virtual unsigned int    getNumElements() const { return this->size(); }

    protected:
    
        virtual ~TemplateArray() {}
};

class OSG_EXPORT IndexArray : public Array
{

    public:

        IndexArray(Type arrayType=ArrayType,GLint dataSize=0,GLenum dataType=0):
            Array(arrayType,dataSize,dataType) {}
    
        IndexArray(const Array& array,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            Array(array,copyop) {}

        virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const IndexArray*>(obj)!=NULL; }
        
        virtual unsigned int index(unsigned int pos) const = 0;

    protected:
    
        virtual ~IndexArray() {}
};

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
class TemplateIndexArray : public IndexArray, public std::vector<T>
{
    public:
    
        TemplateIndexArray() : IndexArray(ARRAYTYPE,DataSize,DataType) {}

        TemplateIndexArray(const TemplateIndexArray& ta,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
            IndexArray(ta,copyop),
            std::vector<T>(ta) {}
        
        TemplateIndexArray(unsigned int no) : 
            IndexArray(ARRAYTYPE,DataSize,DataType),
            std::vector<T>(no) {}

        TemplateIndexArray(unsigned int no,T* ptr) : 
            IndexArray(ARRAYTYPE,DataSize,DataType),
            std::vector<T>(ptr,ptr+no) {}

        template <class InputIterator>
        TemplateIndexArray(InputIterator first,InputIterator last) : 
            IndexArray(ARRAYTYPE,DataSize,DataType),
            std::vector<T>(first,last) {}        

        TemplateIndexArray& operator = (const TemplateIndexArray& array)
        {
            if (this==&array) return *this;
            assign(array.begin(),array.end());
            return *this;
        } 

        virtual Object* cloneType() const { return new TemplateIndexArray(); }
        virtual Object* clone(const CopyOp& copyop) const { return new TemplateIndexArray(*this,copyop); }        

        inline virtual void accept(ArrayVisitor& av);
        inline virtual void accept(ConstArrayVisitor& av) const;

        inline virtual void accept(unsigned int index,ValueVisitor& vv);
        inline virtual void accept(unsigned int index,ConstValueVisitor& vv) const;

        virtual int compare(unsigned int lhs,unsigned int rhs) const
        {
            const T& elem_lhs = (*this)[lhs];
            const T& elem_rhs = (*this)[rhs];
            if (elem_lhs<elem_rhs) return -1;
            if (elem_rhs<elem_lhs) return 1;
            return 0;
        }

        virtual const GLvoid*   getDataPointer() const { if (!this->empty()) return &this->front(); else return 0; }
        virtual unsigned int    getTotalDataSize() const { return this->size()*sizeof(T); }
        virtual unsigned int    getNumElements() const { return this->size(); }

        virtual unsigned int    index(unsigned int pos) const { return (*this)[pos]; }

    protected:
    
        virtual ~TemplateIndexArray() {}
};

typedef TemplateIndexArray<GLbyte,Array::ByteArrayType,1,GL_BYTE>               ByteArray;
typedef TemplateIndexArray<GLshort,Array::ShortArrayType,1,GL_SHORT>            ShortArray;
typedef TemplateIndexArray<GLint,Array::IntArrayType,1,GL_INT>                  IntArray;
typedef TemplateIndexArray<GLubyte,Array::UByteArrayType,1,GL_UNSIGNED_BYTE>    UByteArray;
typedef TemplateIndexArray<GLushort,Array::UShortArrayType,1,GL_UNSIGNED_SHORT> UShortArray;
typedef TemplateIndexArray<GLuint,Array::UIntArrayType,1,GL_UNSIGNED_INT>       UIntArray;

typedef TemplateArray<GLfloat,Array::FloatArrayType,1,GL_FLOAT>                 FloatArray;
typedef TemplateArray<UByte4,Array::UByte4ArrayType,4,GL_UNSIGNED_BYTE>         UByte4Array;
typedef TemplateArray<Vec2,Array::Vec2ArrayType,2,GL_FLOAT>                     Vec2Array;
typedef TemplateArray<Vec3,Array::Vec3ArrayType,3,GL_FLOAT>                     Vec3Array;
typedef TemplateArray<Vec4,Array::Vec4ArrayType,4,GL_FLOAT>                     Vec4Array;

class ArrayVisitor
{
    public:
        ArrayVisitor() {}
        
        virtual void apply(Array&) {}
        virtual void apply(ByteArray&) {}
        virtual void apply(ShortArray&) {}
        virtual void apply(IntArray&) {}
        virtual void apply(UByteArray&) {}
        virtual void apply(UShortArray&) {}
        virtual void apply(UIntArray&) {}
        virtual void apply(UByte4Array&) {}
        virtual void apply(FloatArray&) {}
        virtual void apply(Vec2Array&) {}
        virtual void apply(Vec3Array&) {}
        virtual void apply(Vec4Array&) {}
};

class ConstArrayVisitor
{
    public:
        ConstArrayVisitor() {}
        
        virtual void apply(const Array&) {}
        virtual void apply(const ByteArray&) {}
        virtual void apply(const ShortArray&) {}
        virtual void apply(const IntArray&) {}
        virtual void apply(const UByteArray&) {}
        virtual void apply(const UShortArray&) {}
        virtual void apply(const UIntArray&) {}
        virtual void apply(const UByte4Array&) {}
        virtual void apply(const FloatArray&) {}
        virtual void apply(const Vec2Array&) {}
        virtual void apply(const Vec3Array&) {}
        virtual void apply(const Vec4Array&) {}
};


class ValueVisitor
{
    public:
        ValueVisitor() {}
        
        virtual void apply(GLbyte&) {}
        virtual void apply(GLshort&) {}
        virtual void apply(GLint&) {}
        virtual void apply(GLushort&) {}
        virtual void apply(GLubyte&) {}
        virtual void apply(GLuint&) {}
        virtual void apply(GLfloat&) {}
        virtual void apply(UByte4&) {}
        virtual void apply(Vec2&) {}
        virtual void apply(Vec3&) {}
        virtual void apply(Vec4&) {}
};

class ConstValueVisitor
{
    public:
        ConstValueVisitor() {}
        
        virtual void apply(const GLbyte&) {}
        virtual void apply(const GLshort&) {}
        virtual void apply(const GLint&) {}
        virtual void apply(const GLushort&) {}
        virtual void apply(const GLubyte&) {}
        virtual void apply(const GLuint&) {}
        virtual void apply(const GLfloat&) {}
        virtual void apply(const UByte4&) {}
        virtual void apply(const Vec2&) {}
        virtual void apply(const Vec3&) {}
        virtual void apply(const Vec4&) {}
};

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
inline void TemplateArray<T,ARRAYTYPE,DataSize,DataType>::accept(ArrayVisitor& av) { av.apply(*this); }

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
inline void TemplateArray<T,ARRAYTYPE,DataSize,DataType>::accept(ConstArrayVisitor& av) const { av.apply(*this); }

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
inline void TemplateArray<T,ARRAYTYPE,DataSize,DataType>::accept(unsigned int index,ValueVisitor& vv) { vv.apply( (*this)[index] ); }

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
inline void TemplateArray<T,ARRAYTYPE,DataSize,DataType>::accept(unsigned int index,ConstValueVisitor& vv) const {  vv.apply( (*this)[index] );}

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
inline void TemplateIndexArray<T,ARRAYTYPE,DataSize,DataType>::accept(ArrayVisitor& av) { av.apply(*this); }

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
inline void TemplateIndexArray<T,ARRAYTYPE,DataSize,DataType>::accept(ConstArrayVisitor& av) const { av.apply(*this); }

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
inline void TemplateIndexArray<T,ARRAYTYPE,DataSize,DataType>::accept(unsigned int index,ValueVisitor& vv) { vv.apply( (*this)[index] ); }

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
inline void TemplateIndexArray<T,ARRAYTYPE,DataSize,DataType>::accept(unsigned int index,ConstValueVisitor& vv) const {  vv.apply( (*this)[index] );}

}

#endif
