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


#include <driver_types.h>
#include <osg/Image>
#include <osg/Array>
#include <osgCompute/Buffer>
#include <osgCuda/Export>

namespace osgCuda
{
    enum ALLOC_HINTS
    {
        ALLOC_LINEAR = 0x0,
        ALLOC_ARRAY = 0x1,
        ALLOC_DYNAMIC = 0x2
    };

	/**
	*/
	class LIBRARY_EXPORT ArrayStream : public osgCompute::BufferStream
	{
	public:
		cudaArray*                      _devArray;
		bool                            _devArrayAllocated;
		bool                            _syncDevice;
		void*							_hostPtr;
		bool                            _hostPtrAllocated;
		bool                            _syncHost;
		unsigned int                    _allocHint;
		unsigned int                    _modifyCount;

		ArrayStream();
		virtual ~ArrayStream();

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

    /**
    */
    class LIBRARY_EXPORT Array : public osgCompute::Buffer, public osg::Object
    {

    public:
        Array();

        META_Buffer( osgCuda, Array )

        virtual bool init();
        virtual void clear();

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

        virtual void setImage( osg::Image* image );
        virtual osg::Image* getImage();
        virtual const osg::Image* getImage() const;

        virtual void setArray( osg::Array* array );
        virtual osg::Array* getArray();
        virtual const osg::Array* getArray() const;

        inline void setChannelFormatDesc(cudaChannelFormatDesc& channelFormatDesc);
        inline cudaChannelFormatDesc& getChannelFormatDesc();
        inline const cudaChannelFormatDesc& getChannelFormatDesc() const;

    protected:
        virtual ~Array() { clearLocal(); }
        void clearLocal();

        void* mapStream( ArrayStream& stream, unsigned int mapping ) const;
        cudaArray* mapArrayStream( ArrayStream& stream, unsigned int mapping ) const;
        void unmapStream( ArrayStream& stream ) const;

        bool setupStream( unsigned int mapping, ArrayStream& stream ) const;
        bool allocStream( unsigned int mapping, ArrayStream& stream ) const;
        bool syncStream( unsigned int mapping, ArrayStream& stream ) const;

        virtual osgCompute::BufferStream* newStream( const osgCompute::Context& context ) const;

        mutable osg::ref_ptr<osg::Array>     _array;
        mutable osg::ref_ptr<osg::Image>     _image;

        cudaChannelFormatDesc                _channelFormatDesc;

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

	//------------------------------------------------------------------------------
	inline cudaChannelFormatDesc& Array::getChannelFormatDesc()
	{
		return _channelFormatDesc;
	}

	//------------------------------------------------------------------------------
	inline const cudaChannelFormatDesc& Array::getChannelFormatDesc() const
	{
		return _channelFormatDesc;
	}

	//------------------------------------------------------------------------------
	inline void Array::setChannelFormatDesc(cudaChannelFormatDesc& channelFormatDesc)
	{
		if( !osgCompute::Resource::isClear() )
			return;

		_channelFormatDesc = channelFormatDesc;
	}
}

#endif //OSGCUDA_ARRAY
