//C++ header - Open Scene Graph - Copyright (C) 1998-2001 Robert Osfield
//Distributed under the terms of the GNU Library General Public License (LGPL)
//as published by the Free Software Foundation.

#ifndef OSGUTIL_OPTIMIZER
#define OSGUTIL_OPTIMIZER

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

#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_REDUNDENT_NODES = 0x2,
            COMBINE_ADJACENT_LODS = 0x4,
            SHARE_DUPLICATE_STATE = 0x8,
            ALL_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS |
                                REMOVE_REDUNDENT_NODES |
                                COMBINE_ADJACENT_LODS |
                                SHARE_DUPLICATE_STATE
        };

        /** 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 redundent transforms.*/        
        class OSGUTIL_EXPORT FlattenStaticTransformsVisitor : public osg::NodeVisitor
        {
            public:



                FlattenStaticTransformsVisitor(bool ignoreDynamicTransforms=true):
                    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
                    _ignoreDynamicTransforms(ignoreDynamicTransforms) {}

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

                void removeTransforms();

            protected:
            
                
                typedef std::vector<osg::Transform*> TransformStack;
                typedef std::vector<osg::Matrix>     MatrixStack;

                struct TransformStruct
                {
                    typedef std::set<osg::Object*> ObjectSet;
                    
                    TransformStruct():_canBeApplied(true) {}
                    
                    void add(osg::Object* obj) { _objectSet.insert(obj); }
                    
                    bool        _canBeApplied;
                    ObjectSet   _objectSet;
                };

                struct ObjectStruct
                {
                    typedef std::set<osg::Transform*> TransformSet;
    
                    ObjectStruct():_canBeApplied(true),_matrixSet(false),_moreThanOneMatrixRequired(false) {}
                    
                    void add(TransformStack& transforms,osg::Matrix& matrix)
                    {
                        _transformSet.insert(transforms.begin(),transforms.end());
                        if (!_matrixSet)
                        {
                            _matrixSet = true;
                            _moreThanOneMatrixRequired = false;
                            _matrix = matrix;
                        }
                        else if (_matrix!=matrix)
                        {
                            _moreThanOneMatrixRequired = true;
                        }
                        
                    }

                    bool            _canBeApplied;
                    bool            _matrixSet;
                    bool            _moreThanOneMatrixRequired;
                    osg::Matrix     _matrix;
                    TransformSet    _transformSet;
                    
                };    
            
                typedef std::map<osg::Transform*,TransformStruct>   TransformMap;
                typedef std::map<osg::Object*,ObjectStruct>         ObjectMap;
                
                void disableObject(osg::Object* object)
                {
                    disableObject(_objectMap.find(object));
                }
                
                void disableObject(ObjectMap::iterator itr);
                void disableTransform(osg::Transform* transform);
                void doTransform(osg::Object* obj,osg::Matrix& matrix);
                                                
                bool            _ignoreDynamicTransforms;
                MatrixStack     _matrixStack;
                TransformStack  _transformStack;
                
                TransformMap    _transformMap;
                ObjectMap       _objectMap;
                

        };

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

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

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

                virtual void apply(osg::Group& group);
                
                void removeRedundentNodes();

        };

        /** 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;

        };

};

}

#endif
