/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 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 {

/** Leaf Node for grouping Drawables.*/
class SG_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);

        /** Add Drawable to Geode.
         *  If drawable is not NULL and is not contained in Geode then increment its  
         *  reference count, add it to the drawables list and dirty the bounding 
         *  sphere to force it to recompute on next getBound() and return true for success.
         *  Otherwise return false.
         */
        virtual bool addDrawable( Drawable *drawable );

        /** Remove Drawable from Geode.
          * Equivalent to setDrawable(getDrawableIndex(originChild),node), 
          * see docs for setNode for further details on implementation.*/
        virtual bool removeDrawable( Drawable *drawable );

        /** Remove drawable(s) from the specified position in Geode's drawable list.*/
        virtual bool removeDrawable(unsigned int i,unsigned int numDrawablesToRemove=1);

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

        /** set drawable at position i.
         * return true if set correctly, false on failure (if node==NULL || i is out of range).
         *  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.
         */
        virtual bool setDrawable( unsigned int i, Drawable* drawable );

        /** return the number of drawables.*/
        inline unsigned int getNumDrawables() const { return _drawables.size(); }

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

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

        /** return true if drawable is contained within 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 drawable, return a value between
          * 0 and _drawables.size()-1 if found, if not found then
          * return _drawables.size().*/
        inline unsigned int getDrawableIndex( const Drawable* node ) const
        {
            for (unsigned int drawableNum=0;drawableNum<_drawables.size();++drawableNum)
            {
                if (_drawables[drawableNum]==node) return drawableNum;
            }
            return _drawables.size(); // node not found.
        }

        /** compile OpenGL Display List for each drawable.*/
        void compileDrawables(State& state);
        
        /** 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(!_bsphere_computed) computeBound();
            return _bbox;
        }
        
    protected:

        virtual ~Geode();

        virtual bool computeBound() const;

        mutable osg::BoundingBox        _bbox;
        DrawableList                    _drawables;

};

}

#endif
