/* osgEarth
* Copyright 2025 Pelican Mapping
* MIT License
*/
#ifndef OSGEARTH_OVERLAY_DRAPING_TECHNIQUE
#define OSGEARTH_OVERLAY_DRAPING_TECHNIQUE

#include <osgEarth/Common>
#include <osgEarth/OverlayDecorator>
#include <osgEarth/DrapeableNode>
#include <osgEarth/DrapingCullSet>
#include <osg/TexGenNode>
#include <osg/Uniform>
#include <vector>

namespace osgEarth
{
    class MapNode;
    class TerrainEngineNode;
}

namespace osgEarth { namespace Util
{
    /**
     * Projects an overlay scene graph onto the main model.
     *
     * The OverlayDecorator is automatically installed in the MapNode and is used
     * to display ModelLayer's whose "overlay" property is set to true.
     *
     * This class is similar in scope to osgSim::OverlayNode, but is optimized
     * for use with osgEarth and geocentric terrains.
     */
    class OSGEARTH_INTERNAL DrapingTechnique : public OverlayTechnique
    {
    public:
        DrapingTechnique();

        /**
         * Explicity sets the texture unit to use. Note! When you add this class
         * to a MapNode, it will automatically allocate a free texture unit; so you
         * usually do NOT need to call this method.
         */
        void setTextureUnit( int unit );
        int getTextureUnit() const { return *_textureUnit; }

        /**
         * The size (resolution in both directions) of the overlay texture. By
         * default, this defaults to 4096 or your hardware's maximum supported
         * texture size, whichever is less.
         */
        void setTextureSize( int texSize );
        int getTextureSize() const { return *_textureSize; }

        /**
         * Whether mipmapping is enabled on the projected overlay texture. Mapmapping
         * will improve the visual appearance, but will use more memory, and will affect
         * performance for overlays that are dynamic. Mipmapping can slow things down
         * a LOT on some GPUs (e.g. Intel GMA)
         */
        void setMipMapping( bool value );
        bool getMipMapping() const { return _mipmapping; }

        /**
         * Whether to enable blending on the RTT camera graph. Default = true. You might
         * want to disable this is you are draping polygons that cover a very large area
         * and curve around the globe; sometimes they suffer from tessellation artifacts
         * when draped.
         */
        void setOverlayBlending( bool value );
        bool getOverlayBlending() const { return _rttBlending; }

        /**
        * Set the blending function sources to use when overlay blending is true.
        * Defaults are GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
        */
        void setOverlayBlendingParams(GLenum src, GLenum dst);

        /**
         * Whether to attach the RTT to camera to the stencil buffer.  Default = false.
         * Some older cards don't have very good support 
         */
        void setAttachStencil( bool value );
        bool getAttachStencil() const;

        /**
         * Ratio of near resolution to far resolution when draping. [value >= 1.0]
         */
        void setResolutionRatio( float value );
        float getResolutionRatio() const;


    public: // OverlayTechnique

        virtual bool hasData(
            OverlayDecorator::TechRTTParams& params) const;

        virtual bool optimizeToVisibleBound() const { return true; }

        void preCullTerrain(
            OverlayDecorator::TechRTTParams& params,
            osgUtil::CullVisitor*            cv );

        void cullOverlayGroup(
            OverlayDecorator::TechRTTParams& params,
            osgUtil::CullVisitor*            cv );

        void onInstall( TerrainEngineNode* engine );

        void onUninstall( TerrainEngineNode* engine );
        
        const osg::BoundingSphere& getBound(
            OverlayDecorator::TechRTTParams& params) const;

    protected:
        virtual ~DrapingTechnique() { }

    private:
        optional<int>                 _explicitTextureUnit;
        optional<int>                 _textureUnit;
        optional<int>                 _textureSize;
        bool                          _mipmapping;
        bool                          _rttBlending;
        bool                          _attachStencil;
        double                        _maxFarNearRatio;
        GLenum _overlaySource = GL_SRC_ALPHA;
        GLenum _overlayDestination = GL_ONE_MINUS_SRC_ALPHA;

        mutable std::shared_ptr<DrapingManager> _drapingManager;
        std::shared_ptr<DrapingManager>& getDrapingManager() { return _drapingManager; }
        friend class osgEarth::MapNode;

        struct TechData : public osg::Referenced
        {
            osg::ref_ptr<osg::Uniform> _texGenUniform;
        };

    private:
        
        void setUpCamera(OverlayDecorator::TechRTTParams& params);
    };

} } 

#endif //OSGEARTH_OVERLAY_DRAPING_TECHNIQUE
