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

#include "CustomTerrainTechnique"
#include "TransparentLayer"

#include <osg/MatrixTransform>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Texture2DArray>
#include <osg/Version>
#include <osg/Uniform>

#include <osgTerrain/Locator>
#include <osgEarth/Export>
#include <osgEarth/Progress>
#include <osgEarth/TextureCompositor>
#include <osgEarth/ThreadingUtils>

#include <queue>

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

/**
 * A terrain technique that uses a single texture unit by compositing image layer textures
 * on the GPU.
 * 
 * This technique works by creating a single "mosaic" texture and copying each image layer's 
 * texture into that mosaic. It then creates a uniform array that conveys the relative offset
 * and scale information of each sub-texture to a shader. The shader then composites the
 * approprate mosaic texels on the GPU.
 *
 * Limitations:
 *
 * This technique is limited by the maximum texture size your GPU will support, since it 
 * creates a single mosaic texture. For example, if your GPU's max texture size is 2048,
 * this technique can support 64 256-pixel layers.
 */
class SinglePassTerrainTechnique : public CustomTerrainTechnique
{
public:
    SinglePassTerrainTechnique( TextureCompositor* compositor =0L ); //osgTerrain::Locator* locator =0L );

    SinglePassTerrainTechnique(const SinglePassTerrainTechnique&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
    META_Object(osgEarth, SinglePassTerrainTechnique);


public: /* overrides */
#if OSG_MIN_VERSION_REQUIRED(2,9,8)        
    virtual void init(int dirtyMask, bool assumeMultiThreaded);
#else
    virtual void init();
#endif

    void compile( const TileUpdate& updateSpec, ProgressCallback* progress );

    // returns TRUE if a swap occurred and a new subgraph is now in place.
    bool applyTileUpdates();

    /** Traverse the terrain subgraph.*/
    virtual void traverse(osg::NodeVisitor& nv);

public:

    /**
    * Sets a factor by which to scale elevation height values. By default, this object
    * will get the vertical scale from the osgTerrain::Terrain with which the tile is
    * associated. Setting this value overrides that (or sets it if there is no terrain).
    */
    void setVerticalScaleOverride( float value );

    /**
    * Gets the overriden vertical scale value.
    */
    float getVerticalScaleOverride() const;

    /**
     * Sets whether to try to optimize the triangle orientation based on the elevation values.
     *  If false, 
     */
    void setOptimizeTriangleOrientation(bool optimizeTriangleOrientation);
    bool getOptimizeTriangleOrientation() const;

    /** If State is non-zero, this function releases any associated OpenGL objects for
    * the specified graphics context. Otherwise, releases OpenGL objects
    * for all graphics contexts. */
    virtual void releaseGLObjects(osg::State* = 0) const;

protected:

    void calculateSampling( unsigned int& out_rows, unsigned int& out_cols, double& out_i, double& out_j );

private:

    virtual ~SinglePassTerrainTechnique();

    OpenThreads::Mutex _compileMutex;
    //OpenThreads::Mutex                  _writeBufferMutex;
    osg::ref_ptr<osg::MatrixTransform> _transform;
    osg::ref_ptr<osg::Geode> _backGeode;
    osg::ref_ptr<osg::Uniform> _imageLayerStampUniform;
    osg::Vec3d _centerModel;
    float _verticalScaleOverride;
    osg::ref_ptr<GeoLocator> _masterLocator;

    int  _initCount;
    bool _pendingFullUpdate;    
    bool _pendingGeometryUpdate;

    TileUpdate _lastUpdate;

    struct ImageLayerUpdate {
        GeoImage _image;
        UID      _layerUID;
        //int      _layerIndex;
    };
    typedef std::queue<ImageLayerUpdate> ImageLayerUpdates;
    ImageLayerUpdates _pendingImageLayerUpdates;

    // associates each texture index with a layer UID.
    typedef std::map< UID, int > LayerUIDtoIndexMap;
    LayerUIDtoIndexMap _layerUIDtoIndexMap;

    GeoExtent _tileExtent;

    bool _optimizeTriangleOrientation;

    osg::ref_ptr<const TextureCompositor> _texCompositor;
    bool _frontGeodeInstalled;

private:

    osg::Vec3d computeCenterModel();
    GeoImage createGeoImage( const CustomColorLayer& layer ) const; //const osgTerrain::Layer* imageLayer ) const;
    osg::Geode* createGeometry( const CustomTileFrame& tilef );
    osg::StateSet* createStateSet( const CustomTileFrame& tilef );
    void prepareImageLayerUpdate( int layerIndex, const CustomTileFrame& tilef );
    //Threading::ReadWriteMutex& getMutex();
    inline osg::Geode* getFrontGeode() { return static_cast<osg::Geode*>( _transform->getChild(0) ); }
    //const CustomColorLayer* getLayerByUID( UID uid ) const;
    int getIndexOfColorLayerWithUID( UID uid ) const;

    

};

#endif // OSGEARTH_ENGINE_OSGTERRAIN_SINGLE_PASS_TERRAIN_TECHNIQUE
