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

#include <osg/Node>
#include <osg/NodeVisitor>
#include <osg/Drawable>

namespace osg {

/** A \c Geode is a "geometry node", that is, a leaf node on the scene graph
  * that can have "renderable things" attached to it. In OSG, renderable things
  * are represented by objects from the \c Drawable class, so a \c Geode is a
  * \c Node whose purpose is grouping <tt>Drawable</tt>s.
*/
class OSG_EXPORT Geode : public Node
{
    public:

        typedef std::vector< ref_ptr<Drawable> > DrawableList;

        Geode();

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

        META_Node(osg, Geode);

        virtual Geode* asGeode() { return this; }
        virtual const Geode* asGeode() const { return this; }
        
        /** Add a \c Drawable to the \c Geode.
          * If \c drawable is not \c NULL and is not contained in the \c Geode
          * then increment its reference count, add it to the drawables list and
          * dirty the bounding sphere to force it to be recomputed on the next
          * call to \c getBound().
          * @param drawable The \c Drawable to be added to the \c Geode.
          * @return  \c true for success; \c false otherwise.
        */
        virtual bool addDrawable( Drawable *drawable );

        /** Remove a \c Drawable from the \c Geode.
          * Equivalent to <tt>removeDrawable(getDrawableIndex(drawable)</tt>.
          * @param drawable The drawable to be removed.
          * @return \c true if at least one \c Drawable was removed. \c false
          *         otherwise.
        */
        virtual bool removeDrawable( Drawable *drawable );

        /** Remove <tt>Drawable</tt>(s) from the specified position in
          * <tt>Geode</tt>'s drawable list.
          * @param i The index of the first \c Drawable to remove.
          * @param numDrawablesToRemove The number of <tt>Drawable</tt> to
          *        remove.
          * @return \c true if at least one \c Drawable was removed. \c false
          *         otherwise.
        */
        virtual bool removeDrawables(unsigned int i,unsigned int numDrawablesToRemove=1);

        /** Replace specified Drawable with another Drawable.
         * Equivalent to <tt>setDrawable(getDrawableIndex(origDraw),newDraw)</tt>,
         * see docs for \c setDrawable() for further details on implementation.
        */
        virtual bool replaceDrawable( Drawable *origDraw, Drawable *newDraw );

        /** Set \c Drawable at position \c i.
          * Decrement the reference count origGSet and increments the
          * reference count of newGset, and dirty the bounding sphere
          * to force it to recompute on next getBound() and returns true.
          * If origDrawable is not found then return false and do not
          * add newGset.  If newGset is NULL then return false and do
          * not remove origGset.
          * @return \c true if set correctly, \c false on failure
          *         (if node==NULL || i is out of range).
        */
        virtual bool setDrawable( unsigned int i, Drawable* drawable );

        /** Return the number of <tt>Drawable</tt>s currently attached to the
          * \c Geode.
        */
        inline unsigned int getNumDrawables() const { return _drawables.size(); }

        /** Return the \c Drawable at position \c i.*/
        inline Drawable* getDrawable( unsigned int i ) { return _drawables[i].get(); }

        /** Return the \c Drawable at position \c i.*/
        inline const Drawable* getDrawable( unsigned int i ) const { return _drawables[i].get(); }

        /** Return \c true if a given \c Drawable is contained within \c Geode.*/
        inline bool containsDrawable(const Drawable* gset) const
        {
            for (DrawableList::const_iterator itr=_drawables.begin();
                 itr!=_drawables.end();
                 ++itr)
            {
                if (itr->get()==gset) return true;
            }
            return false;
        }

        /** Get the index number of \c drawable.
          * @return A value between 0 and <tt>getNumDrawables()-1</tt> if
          *         \c drawable is found; if not found, then
          *         <tt>getNumDrawables()</tt> is returned.
        */
        inline unsigned int getDrawableIndex( const Drawable* drawable ) const
        {
            for (unsigned int drawableNum=0;drawableNum<_drawables.size();++drawableNum)
            {
                if (_drawables[drawableNum]==drawable) return drawableNum;
            }
            return _drawables.size(); // drawable not found.
        }

        /** Get the list of drawables.*/
        const DrawableList& getDrawableList() const { return _drawables; }

        /** Compile OpenGL Display List for each drawable.*/
        void compileDrawables(RenderInfo& renderInfo);

        /** Return the Geode's bounding box, which is the union of all the
          * bounding boxes of the geode's drawables.*/
        inline const BoundingBox& getBoundingBox() const
        {
            if(!_boundingSphereComputed) getBound();
            return _bbox;
        }

        virtual BoundingSphere computeBound() const;

        /** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/
        virtual void setThreadSafeRefUnref(bool threadSafe);

        /** Resize any per context GLObject buffers to specified size. */
        virtual void resizeGLObjectBuffers(unsigned int maxSize);

        /** If State is non-zero, this function releases any associated OpenGL objects for
           * the specified graphics context. Otherwise, releases OpenGL objects
           * for all graphics contexts. */
        virtual void releaseGLObjects(osg::State* = 0) const;


    protected:

        virtual ~Geode();


        mutable osg::BoundingBox        _bbox;
        DrawableList                    _drawables;

};

}

#endif
