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

#include "Common"
#include "Tile"
#include "CustomTerrainTechnique"
#include "OSGTileFactory"
#include <osgEarth/Locators>
#include <osgEarth/Profile>
#include <osgEarth/TerrainOptions>
#include <osgEarth/Map>
#include <osgEarth/ThreadingUtils>
#include <list>
#include <queue>
#include <vector>

namespace osgEarth_engine_osgterrain
{
    class TileFactory;

    using namespace osgEarth;

    /**
     */
    class TerrainNode : public osg::Group //osgTerrain::Terrain
    {
    public:
        TerrainNode(
            const MapFrame& update_mapf,
            const MapFrame& cull_mapf,
            OSGTileFactory* factory,
            bool            quickReleaseGLObjects );

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

        virtual const char* className() const { return "TerrainNode"; }

    public:

        OSGTileFactory* getTileFactory() { return _tileFactory.get(); }

        bool getQuickReleaseGLObjects() const { return _quickReleaseGLObjects; }

        const MapFrame& getUpdateThreadMapFrame() { return _update_mapf; }

        const MapFrame& getCullThreadMapFrame()   { return _cull_mapf;   }

        virtual Tile* createTile( const TileKey& key, GeoLocator* locator ) const;
        
        void setTechniquePrototype( TerrainTechnique* tech );

        TerrainTechnique* cloneTechnique() const;

        void setSampleRatio( float value );

        float getSampleRatio() const { return _sampleRatio; }

        void setVerticalScale( float value );

        float getVerticalScale() const { return _verticalScale; }

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

    protected:

	    virtual ~TerrainNode();

        // subclass can override to notify of running tasks
        virtual unsigned getNumActiveTasks() const { return 0; }

        // subclass can override this to perform addition UPDATE traversal operations
        virtual void updateTraversal( osg::NodeVisitor& nv ) { }

        typedef std::map< osgTerrain::TileID, osg::ref_ptr<Tile> > TileTable;

        typedef std::queue< osg::ref_ptr<Tile> >  TileQueue;
        typedef std::list< osg::ref_ptr<Tile> >   TileList;
        typedef std::vector< osg::ref_ptr<Tile> > TileVector;
        typedef std::queue< osgTerrain::TileID >  TileIDQueue;

        Threading::ReadWriteMutex _tilesMutex;
        TileTable                 _tiles;
        TileList                  _tilesToShutDown;
        TileVector                _tilesToRelease;
        Threading::Mutex          _tilesToReleaseMutex;

        float _sampleRatio;
        float _verticalScale;

    public:

        void releaseGLObjectsForTiles( osg::State* state );

        void registerTile( Tile* newTile );

        /** Gets a thread-safe copy of the entire tile list */
        void getTiles( TileVector& out_tiles );

        /** Fetches a tile from the repo */
        template<typename T>
        void getTile(const osgTerrain::TileID& id, osg::ref_ptr<T>& out_tile, bool lock =true ) const {
            if ( lock ) {
                Threading::ScopedReadLock lock( const_cast<TerrainNode*>(this)->_tilesMutex );
                TileTable::const_iterator i = _tiles.find( id );
                out_tile = i != _tiles.end()? static_cast<T*>(i->second.get()) : 0L;
            }
            else {
                TileTable::const_iterator i = _tiles.find( id );
                out_tile = i != _tiles.end()? static_cast<T*>(i->second.get()) : 0L;
            }
        }

    protected:

        osg::ref_ptr<OSGTileFactory> _tileFactory;
        osg::ref_ptr<const Profile>  _profile;

        bool _alwaysUpdate;
        int  _onDemandDelay; // #frames

        void setDelay( unsigned frames );
        void decDelay();

	    bool _registeredWithReleaseGLCallback;

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

        bool _quickReleaseGLObjects;
        bool _quickReleaseCallbackInstalled;

        osg::ref_ptr<TerrainTechnique> _techPrototype;
    };

} // namespace osgEarth_engine_osgterrain

#endif // OSGEARTH_ENGINE_OSGTERRAIN_STANDARD_TERRAIN
