/* 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>               
#include <osgCompute/Context>               

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,
    }; 

    //! A BufferStream provides information about the memory resources of a buffer addressed within a single computation context.
    /** 
    Every time a buffer is mapped into a computation context an object of this type is created. All allocations are done lazily
    at the time Buffer::map is called. You can remove the stream by calling Buffer::clear(const Context). However, as a 
    normal user you should never get into contact with BufferStreams. Such objects are hidden from application developers. 
    BufferStreams are created on the heap.
    */
    class LIBRARY_EXPORT BufferStream
    {
    public:
        //! The current mapping. Value is set before leaving the mapping function (See Buffer::map).
        unsigned int                    _mapping;	
        //! The current computation context used to allocate the stream. This context information is necessary
        //! to relate streams to a single context.
        osg::ref_ptr<Context>			_context;	
        //! The allocation hint used during allocation of the stream.
        unsigned int                    _allocHint; 

        //! The constructor sets up the initial default values.
        BufferStream();
        //! The destructor frees the related memory
        virtual ~BufferStream();

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

    //! Buffers allow developers to deal with memory resources on an abstract level.
    /**
    A buffer handles device memory as well as host memory. At least you have to specify the dimensions of a buffer and
    the size of a single element. The following example shows how to setup a 1D buffer with 125000 floats: 
    \code
    someBuffer->setDimension( 0, 125000 );
    someBuffer->setElementSize( sizeof(float) );
    \endcode
    At any time when you call the mapping function (Buffer::map) the respective memory block then is allocated lazily 
    within the desired memory space. So in order to allocate the buffer on the device memory just use one of the following
    function calls:
    \code
    void* devPtr = someBuffer->map( context, osgCompute::MAP_DEVICE );
    // or as osgCompute::MAP_DEVICE is the default paramter...
    void* devPtr = someBuffer->map( context );
    // or access the buffer with an offset of 120000 bytes
    void* devPtr = someBuffer->map( context, osgCompute::MAP_DEVICE, 120000 );
    \endcode
    */
    class LIBRARY_EXPORT Buffer : public Resource
    {  
    public:
        //! calls clearLocal() which initializes the default values
        Buffer();

        virtual bool init();

        /***/
        virtual void* map( unsigned int mapping = MAP_DEVICE, unsigned int offset = 0, unsigned int hint = 0 ) = 0;
        virtual void unmap( unsigned int hint = 0 ) = 0;
        virtual bool setMemory( int value, unsigned int mapping = MAP_DEVICE, unsigned int offset = 0, unsigned int count = UINT_MAX, unsigned int hint = 0 ) = 0;
        virtual bool resetMemory( unsigned int hint = 0  ) = 0;
        virtual unsigned int getMapping( unsigned int hint = 0 ) const;

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

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

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

        virtual void setSubloadCallback( SubloadCallback* sc );
        virtual SubloadCallback* getSubloadCallback();
        virtual const SubloadCallback* getSubloadCallback() const;

        virtual void swap( unsigned int incr = 1 );
        virtual unsigned int getSwapIdx() const;
        virtual unsigned int getSwapCount() const;

        virtual void clear();
    protected:
        virtual ~Buffer();
        void clearLocal();

        virtual BufferStream* newStream() const;
        virtual BufferStream* lookupStream() const;
        virtual void init( const Context& context ) const;
        virtual void clear( const Context& context ) const;

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

        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); }
    };
}

#endif //OSGCOMPUTE_BUFFER
