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

#error
#include "Common"
#include "CustomTile"
#include "OSGTileFactory"
#include <osgEarth/TaskService>
#include <osgEarth/Locators>
#include <osgEarth/Profile>
#include <osgEarth/TerrainOptions>
#include <osgEarth/Map>
#include <osgEarth/ThreadingUtils>
#include <osgTerrain/Terrain>
#include <osgTerrain/TerrainTile>
#include <list>
#include <queue>
#include <vector>

class TileFactory;
class CustomTerrain;
class CustomTile;
class CustomTileVector;

#if 0
typedef std::list< osg::ref_ptr<osgTerrain::TerrainTile> > TerrainTileList;

/**
 * Callback object for linstening to terrain events.
 */
struct OSGEARTH_EXPORT TerrainCallback : public osg::Referenced
{
    virtual void onTerrainTilesUpdated( const TerrainTileList& tiles ) { }
};
typedef std::list< osg::ref_ptr<TerrainCallback> > TerrainCallbackList;
#endif

using namespace osgEarth;

/**
 * An osgTerrain derivation that tracks a revision number. When the revision number
 * changes, this incidates to member tiles that something substantial (like the 
 * map model) has changed and that they need to regenreate, update, or otherwise
 * accomodate the change.
 *
 * Individual tiles have their own Revision numbers, which are unrelated. These
 * change with every change to the physical tile.
 */
class CustomTerrain : public osgTerrain::Terrain
{
public:
    CustomTerrain(
        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 "CustomTerrain"; }

public:
    /**
     * Bumps the version number up by one. Versioned Tiles attached to this
     * terrain will detect the mismatch and regenerate themselves.
     */
    void incrementRevision();

    /**
     * Gets the current terrain configuration revision.
     */
    int getRevision() const;

    OSGTileFactory* getTileFactory();
    bool getQuickReleaseGLObjects() const { return _quickReleaseGLObjects; }

    TaskService* getImageryTaskService(int layerId);
    TaskService* getElevationTaskService();
    TaskService* getTileGenerationTaskSerivce();

    const MapFrame& getUpdateThreadMapFrame() { return _update_mapf; }
    const MapFrame& getCullThreadMapFrame() { return _cull_mapf; }

    /**
     * Gets the total number of tasks remaining in all of the TaskServices this CustomTerrain is managing
     */
    unsigned int getNumTasksRemaining() const;

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

    /**
     * Updates the catalog of task service threads - this gets called by the OSGTerrainEngine
     * in response to a change in the Map's data model. The map frame is that of the terrain
     * engine.
     */
    void updateTaskServiceThreads( const MapFrame& mapf );

    bool updateBudgetRemaining() const;

    //void addTerrainCallback( TerrainCallback* cb );

protected:

	~CustomTerrain();

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

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

    Threading::ReadWriteMutex _tilesMutex;
    TileTable  _tiles;
    TileList   _tilesToShutDown;
    TileQueue  _tilesToRelease;

    Threading::Mutex _tilesToReleaseMutex;

public:
    void releaseGLObjectsForTiles(osg::State*);

    void registerTile( CustomTile* newTile );

    void getCustomTile( const osgTerrain::TileID&, osg::ref_ptr<CustomTile>& out_tile, bool lock =true );
    void getCustomTiles( TileVector& out_tiles );

    const LoadingPolicy& getLoadingPolicy() const;

private:

    TaskService* createTaskService( const std::string& name, int id, int numThreads );
    TaskService* getTaskService( int id );

    void refreshFamily( 
        const MapInfo& info, const TileKey& key, Relative* family, bool tileTableLocked );

    int _revision; 
    OpenThreads::Mutex _revisionMutex;

    osg::ref_ptr<OSGTileFactory> _tileFactory;
    typedef std::map< int, osg::ref_ptr< TaskService > > TaskServiceMap;
    TaskServiceMap _taskServices;

    osg::ref_ptr<const Profile> _profile;
    OpenThreads::Mutex _taskServiceMutex;

    bool _alwaysUpdate;
    int _numLoadingThreads;
    int _onDemandDelay; // #frames
    void setDelay( unsigned frames );
    void decDelay();

	bool _registeredWithReleaseGLCallback;
    //TerrainCallbackList _terrainCallbacks;

    LoadingPolicy _loadingPolicy;

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

    UID _elevationTaskServiceUID;
};

#endif // OSGEARTH_ENGINE_OSGTERRAIN_CUSTOM_TERRAIN
