/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
* Copyright 2015 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.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* 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 OSGEARTH_MAPNODE_H
#define OSGEARTH_MAPNODE_H 1

#include <osgEarth/Common>
#include <osgEarth/Map>
#include <osgEarth/MapNodeOptions>
#include <osgEarth/SpatialReference>
#include <osgEarth/TerrainEngineNode>
#include <osgEarth/Extension>

namespace osgEarth
{
    class OverlayDecorator;
    class Terrain;
    class MapNodeCullData;

    /**
     * OSG Node that forms the root of an osgEarth map. This node is a "view" component
     * that renders data from a "Map" data model.
     */
    class OSGEARTH_EXPORT MapNode : public osg::Group
    {
    public:
        /**
         * Attempts to load a MapNOde from a ".earth" file in the arguments list
         */
        static MapNode* load( class osg::ArgumentParser& arguments );

    public:
        /**
         * Creates an empty map node.
         */
        MapNode();

        /**
         * Creates an empty map node.
         * 
         * @param options
         *      Tile creation and rendering properties
         */
        MapNode( const MapNodeOptions& options );

        /**
         * Creates a new map node.
         *
         * @param map
         *      Map data that this map node will render.
         */
        MapNode( Map* map );

        /**
         * Creates a new map node.
         *
         * @param map
         *      Map data that this map node will render.
         * @param options
         *      Tile creation and rendering properties
         */
        MapNode( Map* map, const MapNodeOptions& options );

    public:

        virtual const char* libraryName() const { return "osgEarth"; }
        virtual const char* className() const { return "MapNode"; }

        /**
         * Gets the Map that this MapNode is rendering.
         */
        Map* getMap();
        const Map* getMap() const;

        /**
         * Gets the spatial reference system of the underlying map.
         * Convenience function.
         */
        const SpatialReference* getMapSRS() const;

        /**
         * Gets an interface for querying the in-memory terrain scene graph directly.
         */
        Terrain* getTerrain();
        const Terrain* getTerrain() const;

        /**
         * Finds the topmost Map node in the specified scene graph, or returns NULL if
         * no Map node exists in the graph.
         *
         * @param graph
         *      Node graph in which to search for a MapNode
         * @param travMask
         *      Traversal mask to apply while searching
         */
        static MapNode* findMapNode( osg::Node* graph, unsigned travMask =~0 );
        static MapNode* get( osg::Node* graph, unsigned travMask =~0 ) { return findMapNode(graph, travMask); }

        /**
         * Returns true if the realized terrain model is geocentric, false if
         * it is flat/projected.
         */
        bool isGeocentric() const;

        /**
         * Accesses the group node that contains all the ModelLayers.
         */
        osg::Group* getModelLayerGroup() const;

        /**
         * Accesses the root node for a specific ModelLayer.
         */
        osg::Node* getModelLayerNode( ModelLayer* layer ) const;

        /**
         * Adds a node that decorates the terrain groups
         */
        void addTerrainDecorator( osg::Group* decorator );

        /**
         * Removes a node previously added via addTerrainDecorator.
         */
        void removeTerrainDecorator( osg::Group* decorator );

        /**
         * Gets the overlay decorator in this mapnode. Usually you do not need to
         * access this directly. Instead install a DrapeableNode in the scene graph.
         */
        OverlayDecorator* getOverlayDecorator() { return _overlayDecorator; }

        /**
         * Gets the engine properties associated with this node. The engine
         * properties dictate how the map engine will create scene graph elements.
         */
        const MapNodeOptions& getMapNodeOptions() const;

        /**
         * Gets the underlying terrain engine that renders the terrain surface of the map.
         */
        TerrainEngineNode* getTerrainEngine() const;

        /**
         * Gets the Config object serializing external data. External data is information
         * that osgEarth itself does not control, but that an app can include in the
         * MapNode for the purposes of including it in a .earth file.
         */
        Config& externalConfig() { return _externalConf; }
        const Config& externalConfig() const { return _externalConf; }

        /**
         * Adds an Extension and calls its startup method with this MapNode.
         */
        void addExtension(Extension* extension, const osgDB::Options* options =0L);

        /**
         * Removes an extension, and calls its shutdown method with this MapNode.
         */
        void removeExtension(Extension* extension);

        /**
         * Removes all extensions, calling each one's shutdown method this this MapNode.
         */
        void clearExtensions();

        /**
         * Access the extensions vector.
         */
        const std::vector< osg::ref_ptr<Extension> >& getExtensions() const { return _extensions; }


    public: // special purpose

        /**
         * Constructs a mapnode, optionally specifying whether th intialize the
         * data sources in the map. Typically you would only use this CTOR if you are
         * strictly using the MapNode for serialization.
         */
        MapNode( Map* map, const MapNodeOptions& options, bool initMap );


    public: //override osg::Node

        virtual osg::BoundingSphere computeBound() const;

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

    protected:    

        virtual ~MapNode();

    private:

        unsigned int               _id;
        osg::ref_ptr<Map>          _map;
        osg::ref_ptr< osg::Group > _models;
        osg::ref_ptr< osg::Group > _overlayModels;
        OverlayDecorator*          _overlayDecorator;
        MapNodeOptions             _mapNodeOptions;
        Config                     _externalConf;

        // keep track of nodes created by model layers
        typedef std::map<ModelLayer*, osg::ref_ptr< osg::Node > > ModelLayerNodeMap;
        ModelLayerNodeMap        _modelLayerNodes;
        osg::Group*              _maskLayerNode;
        unsigned                 _lastNumBlacklistedFilenames;
        osg::ref_ptr<TerrainEngineNode> _terrainEngine;
        bool                     _terrainEngineInitialized;
        osg::Group*              _terrainEngineContainer;

        std::vector< osg::ref_ptr<Extension> > _extensions;

    public: // MapCallback proxy

        void onModelLayerAdded( ModelLayer*, unsigned int );
        void onModelLayerRemoved( ModelLayer* );
        void onModelLayerMoved( ModelLayer* layer, unsigned int oldIndex, unsigned int newIndex );

    public:
        struct TileRangeData : public osg::Referenced {
            TileRangeData(double minRange, double maxRange) : _minRange( minRange ), _maxRange( maxRange ) { }
            double _minRange;
            double _maxRange;
        };

    private:

        osg::ref_ptr< MapCallback >        _mapCallback;
    
        void init();

        MapNodeCullData* getCullData(osg::Camera* camera) const;
        typedef std::map<osg::Camera*, osg::ref_ptr<MapNodeCullData> > CullDataMap;
        mutable CullDataMap _cullData;
        mutable Threading::ReadWriteMutex _cullDataMutex;
    };

} // namespace osgEarth

#endif // OSGEARTH_MAPNODE_H
