/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * 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
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSGUTIL_GLOBJECTSVISITOR
#define OSGUTIL_GLOBJECTSVISITOR 1

#include <OpenThreads/Mutex>
#include <osg/NodeVisitor>
#include <osg/Geode>
#include <osg/State>

#include <osgUtil/Export>

namespace osgUtil {

/** Visitor for traversing scene graph and setting each osg::Drawable's _useDisplayList flag,
  * with option to immediately compile osg::Drawable OpenGL Display lists and
  * osg::StateAttribute's.
  */
class OSGUTIL_EXPORT GLObjectsVisitor : public osg::NodeVisitor
{
    public:

        /** Operation modes of the.*/
        enum ModeValues
        {
            SWITCH_ON_DISPLAY_LISTS             = 0x1,
            SWITCH_OFF_DISPLAY_LISTS            = 0x2,
            COMPILE_DISPLAY_LISTS               = 0x4,
            COMPILE_STATE_ATTRIBUTES            = 0x8,
            RELEASE_DISPLAY_LISTS               = 0x10,
            RELEASE_STATE_ATTRIBUTES            = 0x20,
            SWITCH_ON_VERTEX_BUFFER_OBJECTS     = 0x40,
            SWITCH_OFF_VERTEX_BUFFER_OBJECTS    = 0x80,
            CHECK_BLACK_LISTED_MODES            = 0x100
        };

        typedef unsigned int Mode;

        /** Construct a GLObjectsVisitor to traverse all children, operating on
          * node according to specified mode, such as to compile or release
          * display list/texture objects etc. Default mode is to compile
          * GL objects.
          */
        GLObjectsVisitor(Mode mode=COMPILE_DISPLAY_LISTS|COMPILE_STATE_ATTRIBUTES|CHECK_BLACK_LISTED_MODES);

        META_NodeVisitor(osgUtil, GLObjectsVisitor)

        virtual void reset()
        {
            _drawablesAppliedSet.clear();
            _stateSetAppliedSet.clear();
        }


        /** Set the operational mode of what operations to do on the scene graph.*/
        void setMode(Mode mode) { _mode = mode; }

        /** Get the operational mode.*/
        Mode getMode() const { return _mode; }


        /** Set the State to use during traversal. */
        void setState(osg::State* state)
        {
            _renderInfo.setState(state);
        }

        osg::State* getState()
        {
            return _renderInfo.getState();
        }

        void setRenderInfo(osg::RenderInfo& renderInfo)
        {
            _renderInfo = renderInfo;
        }

        osg::RenderInfo& getRenderInfo()
        {
            return _renderInfo;
        }

        /** Set whether and how often OpenGL errors should be checked for, defaults to osg::State::ONCE_PER_ATTRIBUTE. */
        void setCheckForGLErrors(osg::State::CheckForGLErrors check) { _checkGLErrors = check; }

        /** Get whether and how often OpenGL errors should be checked for.*/
        osg::State::CheckForGLErrors getCheckForGLErrors() const { return _checkGLErrors; }

        /** Simply traverse using standard NodeVisitor traverse method.*/
        virtual void apply(osg::Node& node);

        void apply(osg::Drawable& drawable);
        void apply(osg::StateSet& stateset);

        /** Do a compile traversal and then reset any state,*/
        void compile(osg::Node& node);

    protected:

        typedef std::set<osg::Drawable*> DrawableAppliedSet;
        typedef std::set<osg::StateSet*> StatesSetAppliedSet;

        Mode                            _mode;
        osg::RenderInfo                 _renderInfo;
        osg::State::CheckForGLErrors    _checkGLErrors;

        DrawableAppliedSet              _drawablesAppliedSet;
        StatesSetAppliedSet             _stateSetAppliedSet;
        osg::ref_ptr<osg::Program>      _lastCompiledProgram;

};

class OSGUTIL_EXPORT GLObjectsOperation : public osg::GraphicsOperation
{
    public:

        GLObjectsOperation(GLObjectsVisitor::Mode mode = GLObjectsVisitor::COMPILE_DISPLAY_LISTS|GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES|GLObjectsVisitor::CHECK_BLACK_LISTED_MODES);

        GLObjectsOperation(osg::Node* subgraph, GLObjectsVisitor::Mode mode = GLObjectsVisitor::COMPILE_DISPLAY_LISTS|GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES|GLObjectsVisitor::CHECK_BLACK_LISTED_MODES);

        virtual void operator () (osg::GraphicsContext* context);

    protected:

        osg::ref_ptr<osg::Node> _subgraph;
        GLObjectsVisitor::Mode  _mode;
};

}

#endif