/* -*-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_DROAM_ENGINE_MESH_MANAGER_H
#define OSGEARTH_DROAM_ENGINE_MESH_MANAGER_H 1

#include "Common"
#include "Diamond"
#include "Manifold"
#include "AMRGeometry"
#include <osgEarth/Map>
#include <osgEarth/TaskService>
#include <osg/Geode>
#include <osg/Geometry>
#include <queue>
#include <deque>
#include <list>
#include <set>

using namespace osgEarth;


typedef std::queue< osg::ref_ptr<Diamond> > DiamondQueue;

struct DiamondJob {
    DiamondJob( Diamond* d, float priority ) : _d(d), _priority(priority) { }
    bool operator < ( const DiamondJob& rhs ) const { return _priority < rhs._priority; }
    osg::ref_ptr<Diamond> _d;
    float _priority;
};

typedef std::queue<DiamondJob> DiamondJobQueue;
typedef std::deque<DiamondJob> DiamondJobQDeque;
typedef std::priority_queue<
    DiamondJob, 
    DiamondJobQDeque, 
    std::less<DiamondJobQDeque::value_type> > DiamondPriorityQueue;

struct DiamondJobComparator {
    bool operator()( const DiamondJob& lhs, const DiamondJob& rhs ) {
        return lhs._priority < rhs._priority; }
};

typedef std::set<DiamondJob,DiamondJobComparator> DiamondJobOrderedSet;
typedef std::list<DiamondJob> DiamondJobList;

struct CullSettings 
{
    CullSettings() : _maxRange( 2.0f ), _maxDeviation( 0.0 ) { }
    float _maxRange;
    float _maxDeviation;
};

class MeshManager : public osg::Referenced
{
public:
    /** Construct the manager that will manage the mesh for the specified manifold. */
    MeshManager( Manifold* manifold, Map* map );

public:
    /** Add a shared vertex (and its normal) to the mesh. */
    NodeIndex addNode( const osg::Vec3d& manifoldCoord );
    NodeIndex addNode( const MeshNode& node );

    /** Remove a shared vertex from the mesh. */
    void removeNode( NodeIndex index );

    /** add a diamond to the dirty list, b/c its primitive set needs refreshing */
    void queueForRefresh( Diamond* d );

    /** queue a diamond for a split operation (seed children) */
    void queueForSplit( Diamond* d, float priority );

    /** queue a diamond for a merge operation (delete children) */
    void queueForMerge( Diamond* d, float priority );

    /** queue a diamond for background texture load */
    void queueForImage( Diamond* d, float priority );

    /** process the dirty list, refreshing dirty primitive sets. */
    void update();

    /** gets a vertex */
    inline const MeshNode& node( NodeIndex i ) { return _nodes[i]; }
    //inline const osg::Vec3f& vert( NodeIndex i ) { return _nodes[i]._vertex; }
    //inline const osg::Vec3d& coord( NodeIndex i ) { return _nodes[i]._manifoldCoord; }
    //inline const osg::Vec3f& normal( NodeIndex i ) { return _nodes[i]._normal; }
    //inline const osg::Vec3d& geoCoord( NodeIndex i ) { return _nodes[i]._geodeticCoord; }

public:
    osg::ref_ptr<Manifold> _manifold;
    osg::ref_ptr<Map>      _map;

    DiamondQueue         _dirtyQueue;        // queue for primitive set refresh jobs
    DiamondPriorityQueue _splitQueue;        // queue for diamond split jobs
    DiamondPriorityQueue _mergeQueue;        // queue for diamond merge jobs
    DiamondJobList       _imageQueue;        // queue for texture loading jobs

    std::vector<MeshNode> _nodes;
    std::queue<NodeIndex> _freeList;

    // the minimum level that contains renderable geometry (i.e. the level of the 
    // first quadtree ancestor that will render its quadtree decendants).
    Level _minGeomLevel;

    // The minimum splittable level. This is the lowest level at which diamonds may be split
    // or merged. Only diamonds above this level may be removed.
    Level _minActiveLevel;

    // cannot split beyond this level.
    Level _maxActiveLevel;

    CullSettings _cullSettings;
    int _maxJobsPerFrame;

    osg::ref_ptr<TaskService> _imageService;  // service to load textures.

    osg::ref_ptr<osg::Geode>    _amrGeode;    // geode that hold the AMRGeometry
    osg::ref_ptr<AMRGeometry>   _amrGeom;     // virtual geometry node
    AMRDrawableList             _amrDrawList; // culling result
};

#endif // OSGEARTH_DROAM_ENGINE_MESH_MANAGER_H

