/* osgEarth
* Copyright 2008-2014 Pelican Mapping
* MIT License
*/
#ifndef OSGEARTH_TILE_MESHER
#define OSGEARTH_TILE_MESHER 1

#include <osgEarth/Common>
#include <osgEarth/TileKey>
#include <osgEarth/TerrainOptions>
#include <osgEarth/Feature>

#define VERTEX_VISIBLE       1 // draw it
#define VERTEX_BOUNDARY      2 // vertex lies on a skirt boundary
#define VERTEX_HAS_ELEVATION 4 // not subject to elevation texture
#define VERTEX_SKIRT         8 // it's a skirt vertex (bitmask)
#define VERTEX_CONSTRAINT   16 // part of a non-morphable constraint

namespace osgEarth
{
    /**
     * Represents a single edit operation driven by a feature collection
     */
    struct MeshConstraint
    {
        FeatureList features;
        bool hasElevation = false;     // do the features contain valid elevation data in Z?
        bool removeInterior = false;   // should we remove triangles inside polygons?
        bool removeExterior = false;   // should we remove triangles outside polygons?
        bool fillElevations = false;   // should we assign elevations to triangles?
    };

    //! Collection of mesh constraints
    using MeshConstraints = std::vector<MeshConstraint>;

    /**
    * Geometry components for a tile (created by the TileMesher).
    */
    struct OSGEARTH_EXPORT TileMesh
    {
        osg::Matrix localToWorld;
        osg::ref_ptr<osg::Vec3Array> verts;
        osg::ref_ptr<osg::Vec3Array> normals;
        osg::ref_ptr<osg::Vec3Array> uvs;
        osg::ref_ptr<osg::Vec3Array> vert_neighbors;
        osg::ref_ptr<osg::Vec3Array> normal_neighbors;
        osg::ref_ptr<osg::DrawElements> indices;
        bool hasConstraints = false;

        TileMesh() { }
        TileMesh(const TileMesh& m);
        TileMesh(TileMesh&& m);
        TileMesh& operator = (const TileMesh&);
    };

    /**
    * Creates a mesh for a TileKey, optionally including "edits"
    * created by constrait feature data.
    */
    class OSGEARTH_EXPORT TileMesher
    {
    public:
        //! Construct a tile mesher
        TileMesher();
        
        //! Configure this mesher with some terrain options
        void setTerrainOptions(const TerrainOptionsAPI& options);

        //! Create a tile mesh, optionally with a collection of edits.
        //! @param key TileKey for which to create the mesh
        //! @param constraints Constraints to apply to alter the mesh.
        //!     If empty, the method will return a regular gridded mesh.
        //! @param progress Cancelation interface
        //! @return A tile mesh
        TileMesh createMesh(
            const TileKey& key,
            const MeshConstraints& constraints,
            Cancelable* progress) const;

        //! Apply edits to an existing mesh and return a new mesh.
        //! @param key TileKey for which to create the mesh
        //! @param input Existing mesh to constrain
        //! @param constraints Constraints to apply to alter the existing mesh
        //! @param progress Cancelation interface
        //! @return A tile mesh
        TileMesh createMesh(
            const TileKey& key,
            const TileMesh& input,
            const MeshConstraints& constraints,
            Cancelable* progress) const;

        //! Creates a primitive set that represents triangles for
        //! a tile mesh without any edits.
        osg::DrawElements* getOrCreateStandardIndices() const;
            

    protected:
        mutable osg::ref_ptr<osg::DrawElements> _standardIndices;
        mutable Mutex _mutex;
        TerrainOptionsAPI _options;

        TileMesh createMeshStandard(
            const TileKey& key,
            Cancelable* progress) const;

        TileMesh createMeshWithConstraints(
            const TileKey& key,
            const TileMesh& mesh,
            const MeshConstraints& edits,
            Cancelable* progress) const;
    };
}

#endif // OSGEARTH_TILE_MESHER
