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

#include <osg/NodeVisitor>
#include <osg/Matrix>
#include <osg/Geometry>
#include <osg/Transform>

#include <osgUtil/Export>

#include <set>

namespace osgUtil {

/** Insert impostor nodes into scene graph.
  * For example of usage see src/Demos/osgimpostor.
  */
  
class OSGUTIL_EXPORT Optimizer
{

    public:

        Optimizer() {}

        enum OptimizationOptions
        {
            FLATTEN_STATIC_TRANSFORMS = 0x1,
            REMOVE_REDUNDANT_NODES = 0x2,
            COMBINE_ADJACENT_LODS = 0x4,
            SHARE_DUPLICATE_STATE = 0x8,
            MERGE_GEOMETRY = 0x10,
            CHECK_GEOMETRY = 0x20,
            ALL_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS |
                                REMOVE_REDUNDANT_NODES |
                                COMBINE_ADJACENT_LODS |
                                SHARE_DUPLICATE_STATE |
                                MERGE_GEOMETRY |
                                CHECK_GEOMETRY
        };

        /** traverse the node and its subgraph with a series of optimization
          * visitors, specificied by the OptizationOptions.*/
        virtual void optimize(osg::Node* node, unsigned int options = ALL_OPTIMIZATIONS);




        /** Flatten Static Trasform nodes by applying their transform to the
          * geometry on the leaves of the scene graph, then removing the 
          * now redundant transforms.*/        
        class OSGUTIL_EXPORT FlattenStaticTransformsVisitor : public osg::NodeVisitor
        {
            public:



                FlattenStaticTransformsVisitor():
                    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}

                virtual void apply(osg::Geode& geode);
                virtual void apply(osg::Billboard& geode);
                virtual void apply(osg::Transform& transform);

                bool removeTransforms(osg::Node* nodeWeCannotRemove);

            
            protected:

                typedef std::vector<osg::Transform*>                TransformStack;
                typedef std::set<osg::Drawable*>                    DrawableSet;
                typedef std::set<osg::Billboard*>                   BillboardSet;
                typedef std::set<osg::Transform*>                   TransformSet;

                TransformStack  _transformStack;
                DrawableSet     _drawableSet;
                BillboardSet    _billboardSet;
                TransformSet    _transformSet;
                                                
                

        };


        /** Remove rendundant nodes, such as groups with one single child.*/
        class OSGUTIL_EXPORT RemoveEmptyNodesVisitor : public osg::NodeVisitor
        {
            public:


                typedef std::set<osg::Node*> NodeList;
                NodeList                     _redundantNodeList;

                RemoveEmptyNodesVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
                
                virtual void apply(osg::Geode& geode);
                virtual void apply(osg::Group& group);
                
                void removeEmptyNodes();

        };

        /** Remove rendundant nodes, such as groups with one single child.*/
        class OSGUTIL_EXPORT RemoveRedundantNodesVisitor : public osg::NodeVisitor
        {
            public:

                typedef std::set<osg::Node*> NodeList;
                NodeList                     _redundantNodeList;

                RemoveRedundantNodesVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
                
                virtual void apply(osg::Group& group);
                virtual void apply(osg::Transform& transform);
                
                void removeRedundantNodes();

        };

        /** Optimize the LOD groups, by combining adjacent LOD's which have
          * complementary ranges.*/
        class OSGUTIL_EXPORT CombineLODsVisitor : public osg::NodeVisitor
        {
            public:

                typedef std::set<osg::Group*>  GroupList;
                GroupList                      _groupList;

                CombineLODsVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}

                virtual void apply(osg::LOD& lod);

                void combineLODs();

        };
 
        /** Optimize State in the scene graph by removing duplicate state,
          * replacing it with shared instances, both for StateAttributes,
          * and whole StateSets.*/
        class OSGUTIL_EXPORT StateVisitor : public osg::NodeVisitor
        {
            public:

                /// default to traversing all children.
                StateVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {}

                /** empty visitor, make it ready for next traversal.*/        
                virtual void reset();

                virtual void apply(osg::Node& node);

                virtual void apply(osg::Geode& geode);

                void optimize();

            protected:

                void addStateSet(osg::StateSet* stateset,osg::Object* obj);

                typedef std::set<osg::Object*>              ObjectSet;
                typedef std::map<osg::StateSet*,ObjectSet>  StateSetMap;

                StateSetMap _statesets;

        };
        
        class OSGUTIL_EXPORT CheckGeometryVisitor : public osg::NodeVisitor
        {
            public:

                /// default to traversing all children.
                CheckGeometryVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {}

                virtual void apply(osg::Geode& geode) { checkGeode(geode); }

                static void checkGeode(osg::Geode& geode);

        };
        
        class OSGUTIL_EXPORT MergeGeometryVisitor : public osg::NodeVisitor
        {
            public:

                /// default to traversing all children.
                MergeGeometryVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {}

                virtual void apply(osg::Geode& geode) { mergeGeode(geode); }
                virtual void apply(osg::Billboard&) { /* don't do anything*/ }

                static bool mergeGeode(osg::Geode& geode);

                static bool geometryContainsSharedArrays(osg::Geometry& geom);

                static bool mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs);

                static bool mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs);
                static bool mergePrimitive(osg::DrawArrayLengths& lhs,osg::DrawArrayLengths& rhs);
                static bool mergePrimitive(osg::DrawElementsUByte& lhs,osg::DrawElementsUByte& rhs);
                static bool mergePrimitive(osg::DrawElementsUShort& lhs,osg::DrawElementsUShort& rhs);
                static bool mergePrimitive(osg::DrawElementsUInt& lhs,osg::DrawElementsUInt& rhs);

        };
};

}

#endif
