/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
 * Copyright 2020 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_PATCH_LAYER_H
#define OSGEARTH_PATCH_LAYER_H 1

#include <osgEarth/VisibleLayer>
#include <osgEarth/GeoData>
#include <osg/RenderInfo>
#include <osg/Texture>

namespace osgEarth
{
    /**
     * PatchLayer is a layer that can render terrain tiles using either
     * a geometry shader patch (GL_PATCHES) or a custom draw callback.
     */
    class OSGEARTH_EXPORT PatchLayer : public VisibleLayer
    {
    public: // serialization
        class OSGEARTH_EXPORT Options : public VisibleLayer::Options {
        public:
            META_LayerOptions(osgEarth, Options, VisibleLayer::Options);
            virtual Config getConfig() const;
        private:
            void fromConfig(const Config& conf);
        };

    public:
        META_Layer(osgEarth, PatchLayer, Options, VisibleLayer, Patch);

        //! Create a node for a given tilekey
        //! (Not yet implemented)
        virtual GeoNode createNode(const TileKey& key, ProgressCallback*);

        //! API providing access to patch geometry 
        class OSGEARTH_EXPORT TileInfoProvider
        {
        public:
            //! Bounding box of the actual tile geometry (could be cropped by a mask)
            virtual const osg::BoundingBox& getGeomBoundingBox() const = 0;

            //! Bounding box of the full tile
            virtual const osg::BoundingBox& getTileBoundingBox() const = 0;
        };

        class OSGEARTH_EXPORT TileBatch
        {
        public:
            virtual void drawTiles(osg::RenderInfo& ri) const = 0;
            virtual void visitTiles(osg::RenderInfo& ri) const = 0;
            virtual std::size_t size() const = 0;
            virtual std::size_t getBatchID() const = 0;
        };

        //! Internal
        struct DrawContext
        {
            const TileKey* _key;
            const osg::BoundingBox* _tileBBox;
            const osg::RefMatrix* _modelViewMatrix;
            int _revision;
            DrawContext() : _tileBBox(NULL), _key(NULL), _modelViewMatrix(NULL), _revision(-1) { }
        };

        /**
         * Callback that the terrain engine will call for custom tile rendering.
         */
        class DrawCallback : public osg::Referenced
        {
        public:
            virtual void visitTileBatch(osg::RenderInfo& ri, const TileBatch* tiles) { }
            virtual void visitTile(osg::RenderInfo& ri, const DrawContext& tile) { }
        };

        /**
         * Callback that the terrain engine will call to decide whether to 
         * render a tile in this layer.
         */
        struct AcceptCallback : public osg::Referenced
        {
            /** Whether to accept the entire layer. Innvoked during Cull. */
            virtual bool acceptLayer(osg::NodeVisitor& nv, const osg::Camera* camera) const { return true; }

            /** Whether to accept a specific tile. Invoked during Cull. */
            virtual bool acceptKey(const TileKey& key) const { return true; }
        };

    public:

        /**
         * Draw callback to use to render tiles in this layer
         */
        void setDrawCallback(DrawCallback* value) { _drawCallback = value; }
        DrawCallback* getDrawCallback() const { return _drawCallback.get(); }

        /**
         * Terrain LOD at which to render tiles in this patch layer
         */
        void setAcceptCallback(AcceptCallback* value) { _acceptCallback = value; }
        AcceptCallback* getAcceptCallback() const { return _acceptCallback.get(); }

        unsigned getNumPrePassStateSets() const { return _prePassStateSets.size(); }
        osg::StateSet* getPrePassStateSet(unsigned i) const { return _prePassStateSets[i].get(); }


    protected:

        // default destructor
        virtual ~PatchLayer() { }

        //! post-ctor initialization, chain to super/subclasses.
        virtual void init();

        //! Optional implementation of createNode.
        //! (Not yet implemented)
        virtual GeoNode createNodeImplementation(const TileKey& key, ProgressCallback* progress) const {
            return GeoNode::INVALID;
        }

        std::vector< osg::ref_ptr<osg::StateSet> > _prePassStateSets;

    private:
        osg::ref_ptr<DrawCallback> _drawCallback;
        osg::ref_ptr<AcceptCallback> _acceptCallback;
    };

    typedef std::vector< osg::ref_ptr<PatchLayer> > PatchLayerVector;

} // namespace osgEarth

#endif // OSGEARTH_PATCH_LAYER_H
