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

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

namespace osg {

typedef std::vector< ref_ptr<Node> > NodeList;

/** General group node which maintains a list of children.
    Children are reference counted. This allows children to be shared
    with memory management handled automatically via osg::Referenced.
*/
class SG_EXPORT Group : public Node
{
    public :


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

        META_Node(osg, Group);

        virtual Group* asGroup() { return this; }
        virtual const Group* asGroup() const { return this; }

        virtual void traverse(NodeVisitor& nv);

        /** Add Node to Group.
         *  If node is not NULL and is not contained in Group then increment its  
         *  reference count, add it to the child list and dirty the bounding 
         *  sphere to force it to recompute on next getBound() and return true for success.
         *  Otherwise return false. Scene nodes can't be added as child nodes.
         */
        virtual bool addChild( Node *child );

        /** Insert Node to Group at specific location.
         *  The new child node is inserted into the child list
         *  before the node at the specified index.  No nodes
         *  are removed from the group with this operation. 
         */
        virtual bool insertChild( unsigned int index, Node *child );

        /** Remove Node from Group.
         *  If Node is contained in Group then remove it from the child
         *  list, decrement its reference count, and dirty the 
         *  bounding sphere to force it to recompute on next getBound() and
         *  return true for success.  If Node is not found then return false
         *  and do not change the reference count of the Node.
         */
        virtual bool removeChild( Node *child );

        virtual bool removeChild(unsigned int pos,unsigned int numChildrenToRemove=1);

        /** Replace specified Node with another Node.
          * Equivalent to setChild(getChildIndex(orignChild),node), 
          * see docs for setChild for futher details on implementation.*/
        virtual bool replaceChild( Node *origChild, Node* newChild );

        /** return the number of chilren nodes.*/
        inline unsigned int getNumChildren() const { return _children.size(); }

        /** set child node at position i.
         *  return true if set correctly, false on failure (if node==NULL || i is out of range).
         *  When set can be successful applied, the algorithm is : decrement the reference count origNode and increments the
         *  reference count of newNode, and dirty the bounding sphere
         *  to force it to recompute on next getBound() and returns true.
         *  If origNode is not found then return false and do not 
         *  add newNode.  If newNode is NULL then return false and do
         *  not remove origNode. Also returns false if newChild is a Scene node.
         */
        virtual bool setChild( unsigned  int i, Node* node );

        /** return child node at position i.*/
        inline Node* getChild( unsigned  int i ) { return _children[i].get(); }

        /** return child node at position i.*/
        inline const Node* getChild( unsigned  int i ) const { return _children[i].get(); }

        /** return true if node is contained within Group.*/
        inline bool containsNode( const Node* node ) const
        {
            
            for (NodeList::const_iterator itr=_children.begin();
                 itr!=_children.end();
                 ++itr)
            {
                if (itr->get()==node) return true;
            }
            return false;
        }

        /** Get the index number of child, return a value between
          * 0 and _children.size()-1 if found, if not found then
          * return _children.size().*/
        inline unsigned int getChildIndex( const Node* node ) const
        {
            for (unsigned int childNum=0;childNum<_children.size();++childNum)
            {
                if (_children[childNum]==node) return childNum;
            }
            return _children.size(); // node not found.
        }

    protected:

        virtual ~Group();

        virtual bool computeBound() const;

        NodeList _children;


};

}

#endif
