//
// Copyright (C) 2007 Skew Matrix Software LLC (http://www.skew-matrix.com)
//
// 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 OSG_OCCLUSION_QUERY_NODE
#define OSG_OCCLUSION_QUERY_NODE 1

#include <osg/Export>
#include <osg/CopyOp>
#include <osg/Group>



namespace osg {


// This Node performs occlusion query testing on its children.
//   You can use it directly to occlusion query test a portion
//   of your scene graph, or you can use it implicitly with an
//   OcclusionQueryRoot, which places OcclusionQueryNodes where
//   needed and acts as a master control.
class OSG_EXPORT OcclusionQueryNode : public osg::Group
{
public:
    OcclusionQueryNode();

    // Copy constructor using CopyOp to manage deep vs shallow copy.
    OcclusionQueryNode( const OcclusionQueryNode& oqn, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY );

    META_Node( osg, OcclusionQueryNode );

    virtual osg::BoundingSphere computeBound() const;

    virtual void releaseGLObjects( osg::State* state = 0 ) const;
    
    
    // When disabled, OQN doesn't perform occlusion queries, and simply
    //   renders its children.
    void setQueriesEnabled( bool enable=true );
    bool getQueriesEnabled() const { return _enabled; }


    // Sets/gets the visibility threshold. If the test indicates that
    //   the number of visible pixels is less than the specified
    //   threshold, don't draw the actual geometry.
    void setVisibilityThreshold( unsigned int pixels ) { _visThreshold = pixels; }
    unsigned int getVisibilityThreshold() const { return _visThreshold; }

    // Specifies how many frames to wait before issuing another query.
    void setQueryFrameCount( int frames ) { _queryFrameCount = frames; }
    int getQueryFrameCount() const { return _queryFrameCount; }

    // Indicate whether or not the bounding box used in the occlusion query test
    //   should be rendered. Handy for debugging and development.
    // Should only be called outside of cull/draw. No thread issues.
    void setDebugDisplay( bool enable );
    bool getDebugDisplay() const;


    // Set and get the StateSet used by the OcclusionQueryNode
    //   when rendering the query geometry. OQN creates its own by
    //   default, but if you use many OQNs you might want to use
    //   this method to set all OQNs to use the same StateSet
    //   for more efficient processing.
    void setQueryStateSet( osg::StateSet* ss );
    osg::StateSet* getQueryStateSet();
    const osg::StateSet* getQueryStateSet() const;

    // Set and get the StateSet used by the OcclusionQueryNode
    //   when rendering the debug query geometry (see setDebugDisplay).
    void setDebugStateSet( osg::StateSet* ss );
    osg::StateSet* getDebugStateSet();
    const osg::StateSet* getDebugStateSet() const;

    // For statistics gathering, e.g., by a NodeVisitor.
    bool getPassed() const;
    
    
    // These methods are public so that osgUtil::CullVisitor can access them.
    // Not intended for application use.
    bool getPassed( const osg::Camera* camera, osg::NodeVisitor& nv );
    void traverseQuery( const osg::Camera* camera, osg::NodeVisitor& nv );
    void traverseDebug( osg::NodeVisitor& nv );


    // Delete unused query IDs for this contextID.
    static void flushDeletedQueryObjects( unsigned int contextID, double currentTime, double& availableTime );

    // discard all the cached query objects which need to be deleted
    // in the OpenGL context related to contextID.
    // Note, unlike flush no OpenGL calls are made, instead the handles are all removed.
    // this call is useful for when an OpenGL context has been destroyed.
    static void discardDeletedQueryObjects( unsigned int contextID );

protected:
    virtual ~OcclusionQueryNode();

    void createSupportNodes();

    osg::ref_ptr< osg::Geode > _queryGeode;
    osg::ref_ptr< osg::Geode > _debugGeode;

    bool _enabled;

    // Tracks the last frame number that we performed a query.
    // User can set how many times  (See setQueryFrameCount).
    typedef std::map< const osg::Camera*, int > FrameCountMap;
    FrameCountMap _frameCountMap;
    mutable OpenThreads::Mutex _frameCountMutex;

    // For statistics gathering
    bool _passed;

    // User-settable variables
    unsigned int _visThreshold;
    int _queryFrameCount;
    bool _debugBB;


    // Required to ensure that computeBound() is thread-safe.
    mutable OpenThreads::Mutex _computeBoundMutex;
};

}


#endif
