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

#include <vector>
#include <osg/Object>
#include <osg/NodeVisitor>
#include <osgCompute/Export>
#include <osgCompute/Resource>
#include <osgCompute/Callback>

namespace osgCompute
{
    class Context;
    class Module;

    typedef std::vector<osg::ref_ptr<osgCompute::Module> >                          ModuleList;
    typedef std::vector<osg::ref_ptr<osgCompute::Module> >::iterator                ModuleListItr;
    typedef std::vector<osg::ref_ptr<osgCompute::Module> >::const_iterator          ModuleListCnstItr;
}

#define OSGCOMPUTE_CREATE_MODULE_FUNCTION		osgComputeCreateModuleFunc
#define OSGCOMPUTE_CREATE_MODULE_FUNCTION_STR	"osgComputeCreateModuleFunc"

#if defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) || defined( __BCPLUSPLUS__) || defined( __MWERKS__)
#	define OSGCOMPUTE_MODULE_EXPORT				__declspec( dllexport )
#else
#   define OSGCOMPUTE_MODULE_EXPORT
#endif 

typedef osgCompute::Module* (*OSGCOMPUTE_CREATE_MODULE_FUNCTION_PTR)( void );

namespace osgCompute
{
    //! Base class for (parallel) algorithms
    /**
	A module is the base class to implement application specific 
	parallel algorithms working on resources. Modules implement a
	strategy design pattern in connection with a computation node. Think of a 
	module as a separated algorithm executed once in each frame like osg::Program objects 
	are executed during rendering. However, modules are much more flexible as 
	execution is handed over to the module.	The following code shows a simple example 
	how to define a module. Your own module at least has to implement the launch() method 
	as it is done in the osgGeometry example:
	<br />
	\code
	class Warp : public osgCompute::Module
	{
	public:
		Warp() {}
		META_Object(,Warp)

		virtual void launch()
		{ 
			//... Warp Cow Geometry ...
		}
	}
	\endcode
	<br />
	Modules are usually used in combination with computation nodes.
	A module then is attached to a computation node an will be 
	executed either during the update cycle or during the 
	render cycle (see osgCompute::Computation). A module's init() 
	method is called before the launch() method and you can overload 
	it to define your own initialization procedures.
	After initialization, launch() is called frequently every time 
	a computation node is traversed. If the module is disabled the launch()
	method is not called. During both methods init() and launch() an OpenGL-context
	is available allowing for interoperability resources like
	textures or geometry objects (see Interoperability).
	
	<br />
	A computation node will hand over all resources to each attached module via 
	acceptResource(). Overwrite this method to get access to resources 
	of a computation node. Identification of specific resources is done by name:
	<br />
	\code
	class Warp : public osgCompute::Module
	{
	public:
		Warp() {}
		META_Object(,Warp);

		virtual void acceptResource( osgCompute::Resource& resource )
		{
			if( resource.isIdentifiedBy("WARP_GEOMETRY") )
				_vertices = dynamic_cast<osgCompute::Memory*>( &resource );	
		}

		virtual void launch()
		{
			void* devPtr = _vertices->map();
			//...Warp cow vertices on GPU...
		}

	private:
		osg::ref_ptr<osgCompute::Memory> _vertices;
	}
	\endcode
	<br />
	 
	You are not limited to scene graph resources and its very easy to 
	define own resources like timers. 
	<br />
	A module can have an event callback or update callback to watch out for events 
	or changes to the graph.
    */
    class LIBRARY_EXPORT Module : public Resource
    {
    public:
        /** Constructor. 
        */
        Module() { clearLocal(); }

        /** This function should be used to create and initialize all resources necessary for the algorithm
        If the module is attached to a computation node the function is called once before launch() and every
        time the module returns true on isClear().
        */
        virtual bool init();

        /** The launch method is the core function of each algorithm. A computation node will call launch of its
        modules in each frame as long as the module returns true on isEnabled().
        */
        virtual void launch() = 0;

        /** The accept resource method exchanges resources between this modules and other modules that are spread throughout
        the current graph. A module can use the isIdentifiedBy() method of resources to check if
        the resource is required by an algorithm or not.
        @param[in] resource Reference to the resource.
        */
        virtual void acceptResource( Resource& resource );


        /** Change a module's launch behavior by reimplementing this method. 
        The action applied to the hint is up to the respective module. 
        By default nothing happens.
        @param[in] hint Hint to change the behavior of the algorithm.
        */
        virtual void setLaunchHint( unsigned int hint = 0 );

        /** Returns the current hint of this module.
        @return Hint that defines the launch behavior of the algorithm.
        */
        virtual unsigned int getLaunchHint() const;

        /** Users should (not necessarily) overwrite this method and return true if a resource with this identifier 
        is referenced by this module.
        @param[in] identifier The identifier of the resource.
        @return Returns true if module utilizes the resource.
        */
        virtual bool usesResource( const std::string& identifier ) const;

        /** Users should (not necessarily) overwrite this method in order to remove the referenced resource.
        @param[in] resource Reference to the resource.
        */
        virtual void removeResource( const Resource& resource );

        /** Users should (not necessarily) overwrite this method in order to remove the resource with the identifier.
        @param[in] identifier The identifier of the resource.
        */
        virtual void removeResource( const std::string& identifier );

        /** Users should (not necessarily) overwrite this method to return the resource with this identifier and NULL if the 
        the resource is not used.
        @param[in] identifier The identifier of the resource.
        @return Returns a pointer to the resource. NULL if it is not found.
        */
        virtual Resource* getResource( const std::string& identifier );

        /** Users should (not necessarily) overwrite this method to return the resource with this identifier and NULL if the 
        the resource is not used.
        @param[in] identifier The identifier of the resource.
        @return Returns a pointer to the resource. NULL if it is not found.
        */
        virtual const Resource* getResource( const std::string& identifier ) const;

        /** Users should (not necessarily) overwrite this method and return all utilized resources with the identifier in resource list.
        @param[out] resourceList A list of all resources with the identifier.
        @param[in] identifier The identifier of the resource.
        */
        virtual void getResources( ResourceList& resourceList, const std::string& identifier );

        /** Users should (not necessarily) overwrite this method and return all utilized resources in resource list.
        @param[out] resourceList A list of all resources of the module.
        */
        virtual void getAllResources( ResourceList& resourceList );

        /** Setup an update callback. During the update traversal this method will be called.
        Note that the module still might be launched in the update-cycle 
        (see osgCompute::ModuleCallback for further information).
        @param[in] uc A pointer to the callback.
        */
        virtual void setUpdateCallback( ModuleCallback* uc );

        /** Returns a pointer to the update callback.
        @return Returns the callback. NULL if no callback is utilized. 
        */
        virtual ModuleCallback* getUpdateCallback();

        /** Returns a pointer to the update callback.
        @return Returns the callback. NULL if no callback is utilized. 
        */
        virtual const ModuleCallback* getUpdateCallback() const;

        /** Add a event callback. An event callback is launched during the
        event-traversal of the scene 
        (see osgCompute::ModuleCallback for further information).
        @param[in] ec A pointer to the callback.
        */
        virtual void setEventCallback( ModuleCallback* ec );

        /** Returns a pointer to the event callback. 
        @return Returns the callback. NULL if no callback is utilized.
        */
        virtual ModuleCallback* getEventCallback();

        /** Returns a pointer to the event callback.
        @return Returns the callback. NULL if no callback is utilized.
        */
        virtual const ModuleCallback* getEventCallback() const;

        /** A module is enabled by default. An enabled module can be executed. With this
        flag modules can be switched off and turned on easily.
        */
        virtual void enable();

        /** Use this function to disable a module and prevent it from being launched.
        */
        virtual void disable();

        /**
        @return Returns true if the module is enabled*/
        virtual bool isEnabled() const;

        /** Returns the dynamic library name of the module. The library name is required during
        the dynamic load (See loadModule() ).
        @return Returns the string reference with the library name.
        */
        virtual const std::string& getLibraryName() const;

        /** Returns the library name of the module. The library name is required during
        the dynamic load (See loadModule() ).
        @return Returns the string reference with the library name.
        */
        virtual std::string& getLibraryName();

        /** Set the library name of this module. The library name is required during
        the dynamic load (See loadModule()).
        @param[in] libraryName the string reference with the library name.
        */
        virtual void setLibraryName( const std::string& libraryName );

        /** Use this function to load a module as a dynamic library. osgDB methods are
        utilized to encapsulate platform dependent dynamic linking. After the module
        has been loaded the function will return
        a pointer to a new object of type module. If the load fails then NULL is returned.
        @param[in] libraryName the library name of the module.
        */
        static Module* loadModule( const std::string& libraryName );

        /** Returns true if osgDB can find a dynamic library with that library name. 
        @param[in] libraryName the library name.
        */
        static bool existsModule( const std::string& libraryName );

        /** Removes all resources of a module. The method is used to clear the complete state of a module.
        After clear() the module should be initialized again to work properly. The method calls clearLocal().
        */
        virtual void clear();

    protected:	
        /**Destructor.
        */
        virtual ~Module() { clearLocal(); }

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

        void clearLocal();

        osg::ref_ptr<ModuleCallback>   _updateCallback;
        osg::ref_ptr<ModuleCallback>   _eventCallback;
        bool                           _enabled;
        std::string					   _libraryName;
    };
}

#endif //OSGCOMPUTE_MODULE
