/* --*-c++-*-- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2008-2013 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

#ifndef OSGEARTHFEATURES_FEATURE_MODEL_GRAPH_H
#define OSGEARTHFEATURES_FEATURE_MODEL_GRAPH_H 1

#include <osgEarthFeatures/Common>
#include <osgEarthFeatures/FeatureModelSource>
#include <osgEarthFeatures/Session>
#include <osgEarthSymbology/Style>
#include <osgEarth/OverlayNode>
#include <osgEarth/NodeUtils>
#include <osgEarth/ThreadingUtils>
#include <osgEarth/DepthOffset>
#include <osg/Node>
#include <set>

namespace osgEarth {
    class ClampableNode;
    class DrapeableNode;
}

namespace osgEarth { namespace Features
{
    using namespace osgEarth;
    using namespace osgEarth::Symbology;

    /**
     * A scene graph that renders feature data.
     * This class will handle all the internals of selecting features, gridding feature
     * data if required, and sorting features based on style. Then for each cell and each
     * style, it will invoke the FeatureNodeFactory to create the actual data for each set.
     */
    class OSGEARTHFEATURES_EXPORT FeatureModelGraph : public osg::Group
    {
    public:
        /**
         * Constructs a new model graph.
         *
         * @param options
         *      Model source options
         * @param factory
         *      Node factory that will be invoked to compile feature data into nodes
         * @param session
         *      Session under which to create elements in this graph
         */
        FeatureModelGraph(
            Session*                         session,
            const FeatureModelSourceOptions& options,
            FeatureNodeFactory*              factory );

        /**
         * Loads and returns a subnode. Used internally for paging.
         */
        osg::Node* load( unsigned lod, unsigned tileX, unsigned tileY, const std::string& uri );

        /**
         * Style sheet associated with this feature graph.
         */
        StyleSheet* getStyles() { return _session->styles(); }

        /**
         * Sets the style sheet to use to render features 
         */
        void setStyles( StyleSheet* styles );

        /**
         * Session associated with this feature graph.
         */
        Session* getSession() { return _session; }

		/**
         * UID given to this feature graph when registered with the pseudo-loader
         */
        UID getUID() const { return _uid; }

        /**
         * Mark the feature graph dirty and in need of regeneration 
         */
        void dirty();

        /**
         * Adds an operation that will run on new nodes that are added by the
         * paging system. Note, the operation only runs after the pager actually
         * added the node to the live scene graph.
         */
        void addPostMergeOperation( NodeOperation* op );

        /**
         * Access to the features levels
         */
        const std::vector<const FeatureLevel*>& getLevels() const { return _lodmap; };

    public: // osg::Node

        virtual void traverse(osg::NodeVisitor& nv);

    protected:

        virtual ~FeatureModelGraph();

        osg::Node* setupPaging();

        osg::Group* buildLevel( 
            const FeatureLevel& level, 
            const GeoExtent&    extent, 
            const TileKey*      key);

        osg::Group* build( 
            const Style&        baseStyle, 
            const Query&        baseQuery, 
            const GeoExtent&    extent, 
            FeatureSourceIndex* index);


    private:
        
        osg::Group* createStyleGroup(
            const Style&        style, 
            const Query&        query, 
            FeatureSourceIndex* index);

        osg::Group* createStyleGroup(
            const Style&         style, 
            FeatureList&         workingSet, 
            const FilterContext& contextPrototype);

        void buildStyleGroups(
            const StyleSelector* selector,
            const Query&         baseQuery,
            FeatureSourceIndex*  index,
            osg::Group*          parent);

        void queryAndSortIntoStyleGroups(
            const Query&            query,
            const StringExpression& styleExpr,
            FeatureSourceIndex*     index,
            osg::Group*             parent);

        osg::Group* getOrCreateStyleGroupFromFactory(
            const Style& style);
       
        osg::BoundingSphered getBoundInWorldCoords( 
            const GeoExtent& extent, 
            const MapFrame*  mapf ) const;

        void buildSubTilePagedLODs(
            unsigned lod, unsigned tileX, unsigned tileY,
            const MapFrame* mapFrame, osg::Group* parent);

        void redraw();

    private:
        FeatureModelSourceOptions        _options;
        osg::ref_ptr<FeatureNodeFactory> _factory;
        osg::ref_ptr<Session>            _session;
        UID                              _uid;
        std::set<std::string>            _blacklist;
        Threading::ReadWriteMutex        _blacklistMutex;
        GeoExtent                        _usableFeatureExtent;
        bool                             _featureExtentClamped;
        GeoExtent                        _usableMapExtent;
        osg::BoundingSphered             _fullWorldBound;
        bool                             _useTiledSource;
        osgEarth::Revision               _revision;
        bool                             _dirty;
        bool                             _pendingUpdate;
        std::vector<const FeatureLevel*> _lodmap;

        osg::Group*                      _overlayInstalled;
        osg::Group*                      _overlayPlaceholder;
        ClampableNode*                   _clampable;
        DrapeableNode*                   _drapeable;
        DepthOffsetAdapter               _depthOffsetAdapter;

        enum OverlayChange {
            OVERLAY_NO_CHANGE,
            OVERLAY_INSTALL_PLACEHOLDER,
            OVERLAY_INSTALL_CLAMPABLE,
            OVERLAY_INSTALL_DRAPEABLE
        };
        OverlayChange                    _overlayChange;

        osg::ref_ptr<RefNodeOperationVector> _postMergeOperations;

        void runPostMergeOperations(osg::Node* node);
        void checkForGlobalStyles(const Style& style);
        void changeOverlay();
    };

} } // namespace osgEarth::Features

#endif // OSGEARTHFEATURES_FEATURE_MODEL_GRAPH_H
