/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
* Copyright 2008-2014 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_DRIVERS_REX_TERRAIN_ENGINE_GEOMETRY_POOL
#define OSGEARTH_DRIVERS_REX_TERRAIN_ENGINE_GEOMETRY_POOL 1

#include "Common"
#include "MaskGenerator"
#include "RexTerrainEngineOptions"
#include <osgEarth/MapInfo>
#include <osgEarth/TileKey>
#include <osgEarth/ThreadingUtils>
#include <osgEarth/ResourceReleaser>
#include <osg/Geometry>

namespace osgEarth { namespace Drivers { namespace RexTerrainEngine
{
    using namespace osgEarth;

    /**
     * Pool of terrain tile geometries.
     *
     * In a geocentric map, every tile at a particular LOD and a particular latitudinal
     * (north-south) extent shares exactly the same geometry; each tile is just shifted
     * and rotated differently. Therefore we can use the same Geometry for all tiles that
     * share the same LOD and same min/max latitude in a geocentric map. In a projected
     * map, all tiles at a given LOD share the same geometry regardless of extent, so eve
     * more sharing is possible.
     *
     * This object creates and returns geometries based on TileKeys, sharing instances
     * whenever possible. Concept adapted from OSG's osgTerrain::GeometryPool.
     */
    class GeometryPool : public osg::Group
    {
    public:
        /** Construct the geometry pool */
        GeometryPool(const RexTerrainEngineOptions& options);

        /** Sets an object to release unused GL resources */
        void setReleaser(ResourceReleaser* releaser);

    public:
        /**
         * Hashtable key for unique (and therefore shareable) geometries.
         */
        struct GeometryKey
        {
            GeometryKey() :
                lod(-1),
                yMin(0.0),
                patch(false),
                size(0u)
                {
                }

            bool operator < (const GeometryKey& rhs) const
            {
                if (lod < rhs.lod) return true;
                if (lod > rhs.lod) return false;
                if (yMin < rhs.yMin) return true;
                if (yMin > rhs.yMin) return false;
                if (size < rhs.size) return true;
                if (size > rhs.size) return false;
                return patch == false && rhs.patch == true;
            }

            int      lod;
            double   yMin;
            bool     patch;
            unsigned size;
        };

        typedef std::map<GeometryKey, osg::ref_ptr<osg::Geometry> > GeometryMap;

        /**
         * Gets the Geometry associated with a tile key, creating a new one if
         * necessary and storing it in the pool.
         */
        void getPooledGeometry(
            const TileKey&               tileKey,
            const MapInfo&               mapInfo,
            osg::ref_ptr<osg::Geometry>& out,
            MaskGenerator*               maskSet=0L);

        /**
         * The number of elements (incides) in the terrain skirt, if applicable
         */
        int getNumSkirtElements() const;

    public: // osg::Node

        /** Perform an update traversal to check for unused resources. */
        void traverse(osg::NodeVisitor& nv);

    protected:
        virtual ~GeometryPool() { }

        mutable Threading::Mutex       _geometryMapMutex;
        GeometryMap                    _geometryMap;
        unsigned                       _tileSize;
        const RexTerrainEngineOptions& _options; 
        osg::ref_ptr<ResourceReleaser> _releaser;

        mutable osg::ref_ptr<osg::Vec3Array> _sharedTexCoords;
        
        void createKeyForTileKey(
            const TileKey& tileKey, 
            unsigned       size,
            const MapInfo& mapInfo,
            GeometryKey&   out) const;

        osg::Geometry* createGeometry(
            const TileKey& tileKey,
            const MapInfo& mapInfo,
            MaskGenerator* maskSet ) const;

        bool _enabled;
        bool _debug;
    };

} } } // namespace osgEarth::Drivers::RexTerrainEngine

#endif // OSGEARTH_DRIVERS_REX_TERRAIN_ENGINE_GEOMETRY_POOL
