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


#include <osg/NodeVisitor>
#include <osg/Geode>

#include <osgDB/Export>

#include <OpenThreads/Mutex>

#include <set>


namespace osgDB {

    class OSGDB_EXPORT SharedStateManager : public osg::NodeVisitor
    {
    public:

        enum ShareMode
        {
            SHARE_NONE                  = 0,
            SHARE_STATIC_TEXTURES       = 1<<0,
            SHARE_UNSPECIFIED_TEXTURES  = 1<<1,
            SHARE_DYNAMIC_TEXTURES      = 1<<2,
            SHARE_STATIC_STATESETS      = 1<<3,
            SHARE_UNSPECIFIED_STATESETS = 1<<4,
            SHARE_DYNAMIC_STATESETS     = 1<<5,
            SHARE_TEXTURES  = SHARE_STATIC_TEXTURES | SHARE_UNSPECIFIED_TEXTURES,
            SHARE_STATESETS = SHARE_STATIC_STATESETS | SHARE_UNSPECIFIED_STATESETS,
            SHARE_ALL       = SHARE_TEXTURES |
                              SHARE_STATESETS
        };

        SharedStateManager(unsigned int mode = SHARE_ALL);

        META_NodeVisitor(osgDB, SharedStateManager)

        void setShareMode(unsigned int mode);

        unsigned int getShareMode() { return _shareMode; }

        // Call right after each unload and before Registry cache prune.
        void prune();

        // Call right after each load
        void share(osg::Node *node, OpenThreads::Mutex *mt=0);

        void apply(osg::Node& node);

        // Answers the question "Will this state set be eliminated by
        // the SharedStateManager because an equivalent one has been
        // seen already?" Safe to call from the pager thread.
        bool isShared(osg::StateSet* stateSet);

        bool isShared(osg::Texture* texture);

        void releaseGLObjects(osg::State* state ) const;

    protected:

        inline bool shareTexture(osg::Object::DataVariance variance)
        {
            return _shareTexture[variance];
        }

        inline bool shareStateSet(osg::Object::DataVariance variance)
        {
            return _shareStateSet[variance];
        }


        void process(osg::StateSet* ss, osg::Object* parent);
        osg::StateAttribute *find(osg::StateAttribute *sa);
        osg::StateSet *find(osg::StateSet *ss);
        void setStateSet(osg::StateSet* ss, osg::Object* object);
        void shareTextures(osg::StateSet* ss);

        struct CompareStateAttributes
        {
            bool operator()(const osg::ref_ptr<osg::StateAttribute>& lhs,
                            const osg::ref_ptr<osg::StateAttribute>& rhs) const
            {
                return *lhs < *rhs;
            }
        };

        struct CompareStateSets
        {
            bool operator()(const osg::ref_ptr<osg::StateSet>& lhs,
                            const osg::ref_ptr<osg::StateSet>& rhs) const
            {
                return lhs->compare(*rhs, true) < 0;
            }
        };

        // Lists of shared objects
        typedef std::set< osg::ref_ptr<osg::StateAttribute>, CompareStateAttributes > TextureSet;
        TextureSet _sharedTextureList;

        typedef std::set< osg::ref_ptr<osg::StateSet>, CompareStateSets > StateSetSet;
        StateSetSet _sharedStateSetList;

        // Temporary lists just to avoid unnecessary find calls
        typedef std::pair<osg::StateAttribute*, bool> TextureSharePair;
        typedef std::map<osg::StateAttribute*, TextureSharePair> TextureTextureSharePairMap;
        TextureTextureSharePairMap tmpSharedTextureList;

        typedef std::pair<osg::StateSet*, bool> StateSetSharePair;
        typedef std::map<osg::StateSet*, StateSetSharePair> StateSetStateSetSharePairMap;
        StateSetStateSetSharePairMap tmpSharedStateSetList;

        unsigned int    _shareMode;
        bool            _shareTexture[3];
        bool            _shareStateSet[3];

        // Share connection mutex

        OpenThreads::Mutex *_mutex;
        // Mutex for doing isShared queries from other threads
        mutable OpenThreads::Mutex _listMutex;
    };

}

#endif