/* osgCompute - Copyright (C) 2008-2009 SVT Group
*                                                                     
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*                                                                     
* 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
* GNU Lesse General Public License for more details.
*
* The full license is in LICENSE file included with this distribution.
*/
#ifndef OSGCUDA_GEOMETRYBUFFER_H
#define OSGCUDA_GEOMETRYBUFFER_H 1

#include <driver_types.h>
#include <osg/Geometry>
#include <osgCompute/Interoperability>

#define MAP_INDICES 0x01000000

namespace osgCuda
{
    class Geometry;

    enum GeometryMapping
    {
        MAP_DEVICE_INDICES          = MAP_INDICES | osgCompute::MAP_DEVICE,
        MAP_DEVICE_TARGET_INDICES   = MAP_INDICES | osgCompute::MAP_DEVICE_TARGET,
        MAP_DEVICE_SOURCE_INDICES   = MAP_INDICES | osgCompute::MAP_DEVICE_SOURCE,
        MAP_HOST_INDICES            = MAP_INDICES | osgCompute::MAP_HOST,
        MAP_HOST_TARGET_INDICES     = MAP_INDICES | osgCompute::MAP_HOST_TARGET,
        MAP_HOST_SOURCE_INDICES     = MAP_INDICES | osgCompute::MAP_HOST_SOURCE,
    };

    /**
    */
    class LIBRARY_EXPORT GeometryObject : public osgCompute::MemoryObject
    {
    public:
        void*						_hostPtr;
        void*						_devPtr;
        cudaGraphicsResource*       _graphicsResource;
        std::vector<unsigned int>	_lastModifiedCount;

        GeometryObject();
        virtual ~GeometryObject();


    private:
        // not allowed to call copy-constructor or copy-operator
        GeometryObject( const GeometryObject& ) {}
        GeometryObject& operator=( const GeometryObject& ) { return *this; }
    };


    /**
    */
    class LIBRARY_EXPORT IndexedGeometryObject : public GeometryObject
    {
    public:
        void*						_hostIdxPtr;
        void*						_devIdxPtr;
        cudaGraphicsResource*       _graphicsIdxResource;
        std::vector<unsigned int>	_lastIdxModifiedCount;
        unsigned int                _syncIdxOp;
        unsigned int                _idxMapping;


        IndexedGeometryObject();
        virtual ~IndexedGeometryObject();


    private:
        // not allowed to call copy-constructor or copy-operator
        IndexedGeometryObject( const IndexedGeometryObject& ) {}
        IndexedGeometryObject& operator=( const IndexedGeometryObject& ) { return *this; }
    };


    /**
    */
    class LIBRARY_EXPORT GeometryBuffer : public osgCompute::InteropMemory
    {
    public:
        GeometryBuffer();

        META_Object(osgCuda,GeometryBuffer)

		virtual osgCompute::InteropObject* getInteropObject(); 
		virtual const osgCompute::InteropObject* getInteropObject() const; 

        virtual bool init();

        virtual void* map( unsigned int mapping = osgCompute::MAP_DEVICE, unsigned int offset = 0, unsigned int hint = 0 );
        virtual void unmap( unsigned int hint = 0 );
        virtual bool reset( unsigned int hint = 0 );
        virtual bool isMappingAllowed( unsigned int mapping, unsigned int hint = 0 ) const;

        virtual void clear();
    protected:
        friend class Geometry;
        virtual ~GeometryBuffer();
        void clearLocal();


        bool setup( unsigned int mapping );
        bool alloc( unsigned int mapping );
        bool sync( unsigned int mapping );

        virtual osgCompute::MemoryObject* createObject() const;
        virtual unsigned int computePitch() const;

        osg::ref_ptr<osgCuda::Geometry>		_geomref;
    private:
        // copy constructor and operator should not be called
        GeometryBuffer( const GeometryBuffer& , const osg::CopyOp& ) {}
        GeometryBuffer& operator=(const GeometryBuffer&) { return (*this); }
    };

    /**
    */
    class LIBRARY_EXPORT IndexedGeometryBuffer : public GeometryBuffer
    {
    public:
        IndexedGeometryBuffer();

        META_Object(osgCuda,IndexedGeometryBuffer)

        virtual bool init();

        virtual void* map( unsigned int mapping = osgCompute::MAP_DEVICE, unsigned int offset = 0, unsigned int hint = 0 );
        virtual void unmap( unsigned int hint = 0 );
        virtual bool reset( unsigned int hint = 0 );
        virtual bool isMappingAllowed( unsigned int mapping, unsigned int hint = 0 ) const;

        virtual void* mapIndices( unsigned int mapping = osgCompute::MAP_DEVICE, unsigned int offset = 0, unsigned int hint = 0 );
        virtual void unmapIndices( unsigned int hint = 0 );
        virtual bool resetIndices( unsigned int hint = 0 );

        virtual unsigned int getIndicesByteSize() const;

        virtual void clear();
    protected:
        friend class Geometry;
        virtual ~IndexedGeometryBuffer();
        void clearLocal();


        bool setupIndices( unsigned int mapping );
        bool allocIndices( unsigned int mapping );
        bool syncIndices( unsigned int mapping );

        unsigned int                        _indicesByteSize;

        virtual osgCompute::MemoryObject* createObject() const;
    private:
        // copy constructor and operator should not be called
        IndexedGeometryBuffer( const IndexedGeometryBuffer& , const osg::CopyOp& ) {}
        IndexedGeometryBuffer& operator=(const IndexedGeometryBuffer&) { return (*this); }
    };

    /**
    */
    class LIBRARY_EXPORT Geometry : public osg::Geometry, public osgCompute::InteropObject
    {
    public:
        Geometry();

        META_Object( osgCuda, Geometry )

        virtual osgCompute::InteropMemory* getInteropMemory();
        virtual const osgCompute::InteropMemory* getInteropMemory() const;
        virtual osgCompute::InteropMemory* getOrCreateInteropMemory();

        virtual void addIdentifier( const std::string& identifier );
        virtual void removeIdentifier( const std::string& identifier );
		virtual bool isIdentifiedBy( const std::string& identifier ) const;
		virtual osgCompute::IdentifierSet& getIdentifiers();
		virtual const osgCompute::IdentifierSet& getIdentifiers() const;

        virtual bool init();

        virtual void drawImplementation(osg::RenderInfo& renderInfo) const;
        virtual void releaseGLObjects(osg::State* state=0) const;


        virtual void freeProxy();
        virtual bool isClear();
        virtual void clear();

    protected:
        friend class GeometryBuffer;
        virtual ~Geometry();
        void clearLocal();

        mutable GeometryBuffer* 	_proxy;
        bool						_clear;
        osgCompute::IdentifierSet	_identifiers;

    private:
        // copy constructor and operator should not be called
        Geometry( const Geometry& , const osg::CopyOp& ) {}
        Geometry& operator=(const Geometry&) { return (*this); }
    };
}

#endif //OSGCUDA_GEOMETRYBUFFER_H
