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

#include <set>
#include <osg/Group>
#include <osgCompute/Resource>
#include <osgCompute/Visitor>
#include <osgCompute/ComputationBin>

#define OSGCOMPUTE_POST_TRAVERSAL		0x1
#define OSGCOMPUTE_PRE_TRAVERSAL		0x2
#define OSGCOMPUTE_POST_RENDER			0x4
#define OSGCOMPUTE_PRE_RENDER			0x8
#define OSGCOMPUTE_RENDER				0x100
#define OSGCOMPUTE_UPDATE				0x10000

namespace osg
{
    class NodeVisitor;
}

namespace osgUtil
{
    class CullVisitor;
}

#define META_Computation( libraryname, classname, binlibrary, binclass, ctxlibrary, ctxclass, visitorlibrary, visitorclass )  \
    META_Object( libraryname, classname )                                                       		\
    virtual std::string                  binLibraryName() const { return #binlibrary; }          		\
    virtual std::string                  binClassName() const { return #binclass; }              		\
    virtual std::string                  contextLibraryName() const { return #ctxlibrary; }      		\
    virtual std::string                  contextClassName() const { return #ctxclass; }          		\
    virtual osgCompute::Context*         newContext() const { return new ctxlibrary::ctxclass; } 		\
    virtual std::string                  visitorLibraryName() const { return #visitorlibrary; }      	\
    virtual std::string                  visitorClassName() const { return #visitorclass; }          	\
    virtual osgCompute::ResourceVisitor* newVisitor() const { return new visitorlibrary::visitorclass; }

namespace osgCompute
{
    //! Abstract base node of any computation
    /**
    * A computation node allows you to execute your own parallel code during the rendering within                   <br>
    * a scene graph. Your code should be encapsulated by modules which are added to                                 <br>
    * a computation by addModule() and is executed in the order they were added. Please use a                       <br>
    * launch callback to change the order in which the modules are executed or derive from                          <br>
    * a computation in order to define your own computations.                                                       <br>
    * Please note that the rendering of the computation's sub graph is enabled by default.                          <br> 
    * ( see xxx to disable the traversal of the sub-graph in order to remove the sub-graph from rendering ).        <br>
    * Parameters that should be shared throughout modules can be added via addParamHandle(). Please note            <br>
    * that each param is referenced by a handle and modules comparing these handles in order to accept parameters.  <br>
    **/                                                                                                         
    class LIBRARY_EXPORT Computation : public osg::Group
    {
    public: 

        enum ComputeOrder
        {
            RENDER_PRE_RENDER_POST_TRAVERSAL = OSGCOMPUTE_RENDER | OSGCOMPUTE_PRE_RENDER | OSGCOMPUTE_POST_TRAVERSAL,
            RENDER_PRE_RENDER_PRE_TRAVERSAL = OSGCOMPUTE_RENDER | OSGCOMPUTE_PRE_RENDER | OSGCOMPUTE_PRE_TRAVERSAL,
            RENDER_POST_RENDER_POST_TRAVERSAL = OSGCOMPUTE_RENDER | OSGCOMPUTE_POST_RENDER | OSGCOMPUTE_POST_TRAVERSAL,
            RENDER_POST_RENDER_PRE_TRAVERSAL = OSGCOMPUTE_RENDER | OSGCOMPUTE_POST_RENDER | OSGCOMPUTE_PRE_TRAVERSAL,
            UPDATE_POST_TRAVERSAL = OSGCOMPUTE_UPDATE | OSGCOMPUTE_POST_TRAVERSAL,
            UPDATE_PRE_TRAVERSAL = OSGCOMPUTE_UPDATE | OSGCOMPUTE_PRE_TRAVERSAL,
        };

    public:

        /** Constructor. The object will be initialized with default values. */
        Computation();

        /** 
        * Is called during the traversal of the graph. Overwrite this method
        * in a derived class to design your own traversal.
        */
        virtual void accept( osg::NodeVisitor& nv );

        /** The derived class must provide a valid library name of the required context. */
        virtual std::string contextLibraryName() const = 0;
        /** The derived class must provide a valid class name of the required context. */
        virtual std::string contextClassName() const = 0;
        /** The derived class must provide a method which allows to create new contexts*/
        virtual osgCompute::Context* newContext() const = 0;

        /** The derived class must provide a valid library name of the required computation bin. */
        virtual std::string binLibraryName() const = 0;
        /** The derived class must provide a valid class name of the required computation bin. */
        virtual std::string binClassName() const = 0;

        /** The derived class must provide a valid library name of the required computation bin. */
        virtual std::string visitorLibraryName() const = 0;
        /** The derived class must provide a valid class name of the required computation bin. */
        virtual std::string visitorClassName() const = 0;
        /** The derived class must provide a method which allows to create new bins*/
        virtual osgCompute::ResourceVisitor* newVisitor() const = 0;

        /** Load a module dynamically (not implemented yet) */
        virtual void loadModule( const std::string& modName );

        /** Add a module to the computation. */
        virtual void addModule( Module& module );

        /** Remove the module by providing a reference to the module. */
        virtual void removeModule( Module& module );

        /** Remove the module by providing the module's name. */
        virtual void removeModule( const std::string& moduleHandle );

        /** Returns true if the computation uses a module of module name. */
        virtual bool hasModule( const std::string& moduleHandle ) const;

        /** Returns true if the computation uses the module. */
        virtual bool hasModule( Module& module ) const;

        /** Returns true if the computation has modules at all. */
        virtual bool hasModules() const;

        /** Return all modules. */
        virtual ModuleList& getModules();
        virtual const ModuleList& getModules() const;

        /** Return the number of modules. */
        virtual unsigned int getNumModules() const;

        virtual void removeModules();

        /** Add a resource to the computation. */
        virtual void addResource( Resource& resource );
        virtual void removeResource( const std::string& handle );
        virtual void removeResource( Resource& resource );
        virtual bool hasResource( const std::string& handle ) const;
        virtual bool hasResource( Resource& resource ) const;
        virtual ResourceMap& getResources();
        virtual const ResourceMap& getResources() const;

        virtual void removeResources();

        virtual void subgraphChanged();

        virtual Computation* getParentComputation() const;

        virtual void setLaunchCallback( LaunchCallback* lc );
        virtual LaunchCallback* getLaunchCallback();
        virtual const LaunchCallback* getLaunchCallback() const;

        /** 
        * The compute order is used to decide when to execute the computation 
        * during the rendering ( see osgCompute::osgComputationBin for further information )
        */
        virtual void setComputeOrder( ComputeOrder co );

        /** Returns the compute order */
        virtual ComputeOrder getComputeOrder();


        virtual void setAutoCheckSubgraph( bool autoCheckSubgraph );
        virtual bool getAutoCheckSubgraph();

        /** Activates the computation (a computation is enabled by default) */
        virtual void enable();

        /** If the computation is disabled its modules won't be executed during rendering */
        virtual void disable();

        /** check whether the module is enabled */
        virtual bool isEnabled() const;

        /** Restore object to default state. Calls clearLocal(). */
        virtual void clear();

        virtual bool setContext( Context& context );
        virtual Context* getOrCreateContext( osg::State& state );
        virtual Context* getContext( osg::State& state );

        virtual void releaseGLObjects( osg::State* state ) const;

    protected:
        friend class ContextVisitor;
        friend class ResourceVisitor;
        /** Destructor. Release the memory by calling clearLocal(). */
        virtual ~Computation() { clearLocal(); }

        void clearLocal();

        virtual void acceptContext(  Context& context );
        virtual void removeContext( osg::State& state );
        virtual void distributeContext( Context& context );
        virtual void update( osg::NodeVisitor& uv );
        virtual void collectResources();

        virtual void launch();
        virtual void addBin( osgUtil::CullVisitor& cv );
        virtual void handleevent( osg::NodeVisitor& ev );

        virtual void setParentComputation( Computation* parentComputation );

        bool                                	_enabled;
        LaunchCallback*                     	_launchCallback; 
        ModuleList                          	_modules;
        ResourceMap                         	_resources;
        ComputeOrder                        	_computeOrder;
        bool                                	_subgraphChanged;
        osg::ref_ptr<ResourceVisitor>       	_resourceVisitor;
        Computation*                        	_parentComputation; 
        bool                                	_resourcesCollected;
        bool                                	_autoCheckSubgraph;

        mutable ContextMap                  	_contextMap;

    private:

        /** Copy constructor. This constructor should not be called.*/
        Computation( const Computation& ) {}

        /** Copy operator. This operator should not be called.*/
        Computation &operator=(const Computation &) { return *this; }
    };
}

#endif //OSGCOMPUTE_COMPUTATION
