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

#include <osgEarth/TerrainEngineNode>
#include <osgEarth/Map>
#include <osgEarth/Revisioning>
#include <osgEarth/TaskService>

#include "OSGTerrainOptions"
#include "OSGTileFactory"
#include "KeyNodeFactory"
#include "TileBuilder"

#include <osg/Geode>
#include <osg/NodeCallback>

namespace osgEarth_engine_osgterrain
{
    using namespace osgEarth;

    class OSGTerrainEngineNode : public TerrainEngineNode
    {
    public:
        OSGTerrainEngineNode();
        META_Node(osgEarth,OSGTerrainEngineNode);
        virtual ~OSGTerrainEngineNode();

    public:
        osg::Node* createNode(const TileKey& key);

    public: // TerrainEngineNode overrides    
        virtual void preInitialize( const Map* map, const TerrainOptions& options );
        virtual void postInitialize( const Map* map, const TerrainOptions& options );
        virtual void validateTerrainOptions( TerrainOptions& options );
        virtual const TerrainOptions& getTerrainOptions() const { return _terrainOptions; }
        virtual void traverse( osg::NodeVisitor& );
        virtual osg::BoundingSphere computeBound() const;    

        // for standalone tile creation outside of a terrain
        osg::Node* createTile(const TileKey& key);

    public: // MapCallback adapter functions
        void onMapInfoEstablished( const MapInfo& mapInfo ); // not virtual!
        void onMapModelChanged( const MapModelChange& change ); // not virtual!

        UID getUID() const;
        OSGTileFactory* getTileFactory() const { return _tileFactory.get(); }
        class TerrainNode* getTerrainNode() const { return _terrain; }

    public: // statics    
        static void registerEngine( OSGTerrainEngineNode* engineNode );
        static void unregisterEngine( UID uid );
        static void getEngineByUID( UID uid, osg::ref_ptr<OSGTerrainEngineNode>& output );

    public:
        class ElevationChangedCallback : public ElevationLayerCallback
        {
        public:
            ElevationChangedCallback( OSGTerrainEngineNode* terrain );

           virtual void onVisibleChanged( TerrainLayer* layer );

            OSGTerrainEngineNode* _terrain;
            friend class OSGTerrainEngineNode;
        };

    protected:
	    virtual void onVerticalScaleChanged();

    private:
        void init();
        void syncMapModel();
        void installTerrainTechnique();

        /**
         * Reloads all the tiles in the terrain due to a data model change
         */
        void refresh();


        void addImageLayer( ImageLayer* layer );
        void addElevationLayer( ElevationLayer* layer );

        void removeImageLayer( ImageLayer* layerRemoved );
        void removeElevationLayer( ElevationLayer* layerRemoved );

        void moveImageLayer( unsigned int oldIndex, unsigned int newIndex );
        void moveElevationLayer( unsigned int oldIndex, unsigned int newIndex );
        
        void updateElevation( Tile* tile );
        void installShaders();
        void updateTextureCombining();

    private:
        osg::ref_ptr<OSGTileFactory>         _tileFactory;
        //class CustomTerrainNode* _terrain;
        class TerrainNode*               _terrain;
        UID                                  _uid;
        osgEarth::Drivers::OSGTerrainOptions _terrainOptions;
        Revision                             _shaderLibRev;
        osg::ref_ptr<TaskServiceManager>     _taskServiceMgr;

        osg::ref_ptr< ElevationChangedCallback > _elevationCallback;

        // store a separate map frame for each of the traversal threads
        MapFrame* _update_mapf; // map frame for the main/update traversal thread
        MapFrame* _cull_mapf;   // map frame for the cull traversal thread

        osg::ref_ptr<TaskService>    _tileService;
        osg::ref_ptr<KeyNodeFactory> _keyNodeFactory;
        osg::ref_ptr<TileBuilder>    _tileBuilder;

        osg::Timer _timer;
        unsigned   _tileCount;
        double     _tileCreationTime;
        bool       _isStreaming;

        OSGTerrainEngineNode( const OSGTerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) { }
    };

} // namespace osgEarth_engine_osgterrain

#endif // OSGEARTH_ENGINE_OSGTERRAIN_ENGINE_NODE_H
