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

#include "Tile"
#include <osgEarth/TaskService>

namespace osgEarth_engine_osgterrain
{
    class TileFactory;
    class StreamingTerrain;

    using namespace osgEarth;

    //------------------------------------------------------------------------

    class StreamingTile : public Tile
    {
    public:
        // Please refer the StreamingTerrain::refreshFamily() method for more info on this structure.
        struct Relative
        {
            int getImageLOD(unsigned int layerID)
            {
                LayerIDtoLODMap::iterator itr = imageLODs.find(layerID);
                if (itr != imageLODs.end()) return itr->second;
                return -1;
            }
            
            typedef std::map<unsigned int, int> LayerIDtoLODMap;

            bool               expected;
            int                elevLOD;
            LayerIDtoLODMap    imageLODs;
            osgTerrain::TileID tileID;

            enum Direction {
                PARENT =0,
                WEST   =1,
                NORTH  =2,
                EAST   =3,
                SOUTH  =4
            };

            Relative() { }
        };

    public:
        StreamingTile( const TileKey& key, GeoLocator* keyLocator, bool quickReleaseGLObjects );

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

        // Updates and services this tile's image request tasks
        void servicePendingImageRequests( const MapFrame& mapf, int stamp );

        // Updates and services this tile's heightfield request tasks
        void servicePendingElevationRequests( const MapFrame& mapf, int stamp, bool tileTableLocked );

        // returns TRUE if the tile was modified as a result of a completed request.
        bool serviceCompletedRequests( const MapFrame& mapf, bool tileTableLocked );

        /** Setting this hint tells the tile whether it should bother trying to load elevation data. */
        void setHasElevationHint( bool hasElevation );

        /** Gets whether the tile's real (not placeholder) elevation data has been loaded. */
        bool isElevationLayerUpToDate() const { return _elevationLayerUpToDate; }

        /** Gets or sets the LOD of this tile's current heightfield data. */
        int getElevationLOD() const { return _elevationLOD; }
        void setElevationLOD( int lod );

        /** Gets the terrain object to which this tile belongs. */
        class StreamingTerrainNode* getStreamingTerrain();
        const class StreamingTerrainNode* getStreamingTerrain() const;

        // updates one image layer
        void updateImagery( ImageLayer* layer, const MapFrame& mapf, OSGTileFactory* factory );

        // are we using a task service for tile generation?
        bool getUseTileGenRequest() const { return _useTileGenRequest; }

        // gets this tile's relatives
        Relative* getFamily() { return _family; }

        // marks a request to regenerate the tile based on the specified change(s).
        void queueTileUpdate( TileUpdate::Action action, int index =-1 );
        
        void applyImmediateTileUpdate( TileUpdate::Action action, int index =-1 );

        // marks any pending or running requests for cancelation, and returns true if they
        // are all canceled.
        virtual bool cancelActiveTasks(); //override

        void resetElevationRequests( const MapFrame& mapf );

    protected:

        virtual ~StreamingTile();

    private:

        bool _requestsInstalled;
        bool _hasElevation;
        bool _elevationLayerDirty;
        bool _colorLayersDirty;
        bool _elevationLayerRequested;
        bool _elevationLayerUpToDate;
        int  _elevationLOD;
        bool _useTileGenRequest;
        bool _sequentialImagery;

        typedef std::queue<TileUpdate> TileUpdateQueue;
        TileUpdateQueue _tileUpdates;

        TaskRequestList _requests;
        osg::ref_ptr<TaskRequest> _elevRequest;
        osg::ref_ptr<TaskRequest> _elevPlaceholderRequest;
        osg::ref_ptr<TaskRequest> _tileGenRequest;

        Relative _family[5];

        /** Deals with completed requests during the UPDATE traversal. */
        void installRequests( const MapFrame& mapf, int stamp );
        bool readyForNewElevation();
        bool readyForNewImagery(osgEarth::ImageLayer* layer, int currentLOD);
    };

} // namespace osgEarth_engine_osgterrain

#endif // OSGEARTH_ENGINE_OSGTERRAIN_STREAMING_TILE
