/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
* Copyright 2008-2010 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>

class TileFactory;

using namespace osgEarth;

/**
 */
class Terrain : public osg::Group //osgTerrain::Terrain
{
public:
    Terrain(
        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 "Terrain"; }

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 ~Terrain();

    // 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;
};

#endif // OSGEARTH_ENGINE_OSGTERRAIN_STANDARD_TERRAIN
