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

#include <osgCompute/Resource>

#define META_Buffer( libraryname, classname )              \
    META_Resource( libraryname, classname, false )                      

namespace osg
{
    class Image;
    class Vec2b;
    class Vec3b;
    class Vec4b;
    class Vec4ub;
    class Vec2s;
    class Vec3s;
    class Vec4s;
    class Vec2f;
    class Vec3f;
    class Vec4f;
    class Vec2d;
    class Vec3d;
    class Vec4d;
}

namespace osgCompute
{
    class Buffer;

    enum Mapping
    {
        UNMAPPED                       = 0x00000000,
        MAP_HOST                       = 0x00000011,
        MAP_HOST_SOURCE                = 0x00000001,
        MAP_HOST_TARGET                = 0x00000010,
        MAP_DEVICE                     = 0x00110000,
        MAP_DEVICE_SOURCE              = 0x00010000,
        MAP_DEVICE_TARGET              = 0x00100000,
    }; 


    enum ALLOC_HINT
    {
        NO_ALLOC_HINT = 0x0,
        ALLOC_DYNAMIC = 0x1
    };

    /**
    */
    class LIBRARY_EXPORT BufferStream
    {
    public:
        unsigned int                    _mapping;
        osg::ref_ptr<Context>           _context;
        unsigned int                    _allocHint;

        BufferStream();
        virtual ~BufferStream();

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

    /**
    */
    class LIBRARY_EXPORT Buffer : public Resource
    {  
    public:
        Buffer();

        virtual bool init();

        virtual bool setMemory( const Context& context, int value, unsigned int mapping, unsigned int offset = 0, unsigned int count = UINT_MAX ) const = 0;
        virtual void* map( const Context& context, unsigned int mapping ) const = 0;
        virtual void unmap( const Context& context ) const = 0;

		inline void setElementSize( unsigned int elementSize );
		inline unsigned int getElementSize() const;
        virtual unsigned int getByteSize() const;

        inline void setDimension( unsigned int dimIdx, unsigned int dimSize );
        inline unsigned int getDimension( unsigned int dimIdx ) const;
        inline unsigned int getNumDimensions() const;

        inline unsigned int getNumElements() const;

        inline void setAllocHint( unsigned int allocHint );
        inline unsigned int getAllocHint() const;

        virtual unsigned int getMapping( const Context& context ) const;

        inline void setSubloadResourceCallback( SubloadCallback* sc );
        inline SubloadCallback* getSubloadResourceCallback();
        inline const SubloadCallback* getSubloadResourceCallback() const;

        virtual void clear();
    protected:
        virtual ~Buffer() { clearLocal(); }
        inline void clearLocal();

        virtual bool init( const Context& context ) const;
        virtual void clear( const Context& context ) const;

        virtual BufferStream* newStream( const Context& context ) const;
        inline BufferStream* lookupStream( const Context& context ) const;

        unsigned int                                    _allocHint;
        std::vector<unsigned int>                       _dimensions;
        unsigned int                                    _numElements;
		unsigned int									_elementSize;
        osg::ref_ptr<SubloadCallback>                   _subloadCallback;

        mutable OpenThreads::Mutex                      _mutex;
        mutable std::vector<BufferStream*>              _streams;

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

	/////////////////////////////////////////////////////////////////////////////////////////////////
	// PUBLIC FUNCTIONS /////////////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////////////////////////////////
	//------------------------------------------------------------------------------
	inline void Buffer::setElementSize( unsigned int elementSize ) 
	{ 
		if( !isClear() )
			return;

		_elementSize = elementSize; 
	}

	//------------------------------------------------------------------------------
	inline unsigned int Buffer::getElementSize() const 
	{ 
		return _elementSize; 
	}

	//------------------------------------------------------------------------------
	inline void Buffer::setDimension( unsigned int dimIdx, unsigned int dimSize )
    {
        if( !isClear() )
            return;

        if (_dimensions.size()<=dimIdx)
            _dimensions.resize(dimIdx+1,0);

        _dimensions[dimIdx] = dimSize;
    }

	//------------------------------------------------------------------------------
	inline unsigned int Buffer::getDimension( unsigned int dimIdx ) const
    { 
        if( dimIdx > (_dimensions.size()-1) )
            return 0;

        return _dimensions[dimIdx];
    }

	//------------------------------------------------------------------------------
	inline unsigned int Buffer::getNumDimensions() const
    { 
        return _dimensions.size();
    }

    //------------------------------------------------------------------------------
    inline unsigned int osgCompute::Buffer::getNumElements() const
    {
        return _numElements;
    }

    //------------------------------------------------------------------------------
    inline void osgCompute::Buffer::setAllocHint( unsigned int allocHint )
    {
        if( !isClear() )
            return;

        _allocHint = (_allocHint | allocHint);
    }

    //------------------------------------------------------------------------------
    inline unsigned int osgCompute::Buffer::getAllocHint() const
    {
        return _allocHint;
    }

    //------------------------------------------------------------------------------
    inline void Buffer::setSubloadResourceCallback( SubloadCallback* sc ) 
    { 
        _subloadCallback = sc; 
    }

    //------------------------------------------------------------------------------
    inline SubloadCallback* Buffer::getSubloadResourceCallback() 
    { 
        return _subloadCallback.get(); 
    }

    //------------------------------------------------------------------------------
    inline const SubloadCallback* Buffer::getSubloadResourceCallback() const 
    { 
        return _subloadCallback.get(); 
    }


    /////////////////////////////////////////////////////////////////////////////////////////////////
    // PROTECTED FUNCTIONS //////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////////////
    //------------------------------------------------------------------------------
    inline void Buffer::clearLocal()
    {
        _streams.clear();
        _dimensions.clear();
        _numElements = 0;
		_elementSize = 0;
        _allocHint = NO_ALLOC_HINT;
        _subloadCallback = NULL;
    }

    //------------------------------------------------------------------------------
    inline BufferStream* Buffer::lookupStream( const Context& context ) const
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);

        /////////////////////
        // ALLOCATE STREAM //
        /////////////////////
        // Init stream array and register at context
        if( _streams.size()<=context.getId() ||
            _streams[context.getId()] == NULL )
            init( context );

        return _streams[context.getId()];
    }
}

#endif //OSGCOMPUTE_BUFFER
