/* osgEarth
 * Copyright 2025 Pelican Mapping
 * MIT License
 */

#ifndef OSGEARTH_FADE_EFFECT_H
#define OSGEARTH_FADE_EFFECT_H 1

#include <osgEarth/Common>
#include <osgEarth/Config>
#include <osgEarth/Containers>
#include <osg/Group>
#include <osg/Uniform>

namespace osgEarth
{
    /**
     * Options for geometry fading properties.
     */
    class OSGEARTH_EXPORT FadeOptions
    {
    public:
        /** Construct new default fade options */
        FadeOptions(const Config& conf =Config());

        /** Time to fade in a new graph (seconds) */
        optional<float>& duration() { return _duration; }
        const optional<float>& duration() const { return _duration; }

        /** Range at which geometry becomes invisible (m) */
        optional<float>& maxRange() { return _maxRange; }
        const optional<float>& maxRange() const { return _maxRange; }

        /** Distance over which to fade out geometry (from max range) */
        optional<float>& attenuationDistance() { return _attenDist; }
        const optional<float>& attenuationDistance() const { return _attenDist; }

    public:
        virtual ~FadeOptions() { }
        Config getConfig() const;

    private:
        optional<float> _duration;
        optional<float> _maxRange;
        optional<float> _attenDist;
    };
}

OSGEARTH_SPECIALIZE_CONFIG(osgEarth::FadeOptions);

namespace osgEarth { namespace Util
{
    /**
     * Decorator node that will only display it's children when the camera is within a given elevation range
     */
    class OSGEARTH_EXPORT FadeEffect : public osg::Group
    {
    public:
        /**
         * Creates a uniform that set the start time for a timed fade. Typically you
         * will set this right after adding the node to the scene graph. The value
         * is a FLOAT in seconds. You can apply this uniform to the FadeEffect node
         * or elsewhere in the scene graph.
         */
        static osg::Uniform* createStartTimeUniform();

    public:
        FadeEffect();
        virtual ~FadeEffect() { }

        /** Time over which to fade in the subgraph */
        void setFadeDuration( float seconds );
        float getFadeDuration() const;

        /** Camera range at which subgraph is completely faded out */
        void setMaxRange( float range );
        float getMaxRange() const;

        /** Distance over which to fade out the subgraph */
        void setAttenuationDistance( float dist );
        float getAttenuationDistance() const;

    private:
        osg::ref_ptr<osg::Uniform> _fadeDuration;
        osg::ref_ptr<osg::Uniform> _maxRange;
        osg::ref_ptr<osg::Uniform> _attenDist;
    };


    class OSGEARTH_EXPORT FadeLOD : public osg::Group
    {
    public:
        FadeLOD();
        virtual ~FadeLOD() { }

        void setMinPixelExtent( float value ) { _minPixelExtent = value; }
        float getMinPixelExtent() const { return _minPixelExtent; }

        void setMaxPixelExtent( float value ) { _maxPixelExtent = value; }
        float getMaxPixelExtent() const { return _maxPixelExtent; }

        void setMinFadeExtent( float value ) { _minFadeExtent = value; }
        float getMinFadeExtent() const { return _minFadeExtent; }

        void setMaxFadeExtent( float value ) { _maxFadeExtent = value; }
        float getMaxFadeExtent() const { return _maxFadeExtent; }

    public: // osg::Node

        virtual void traverse(osg::NodeVisitor& nv );

    protected:
        struct PerViewData
        {
            osg::ref_ptr<osg::StateSet> _stateSet;
            osg::ref_ptr<osg::Uniform>  _opacity;
        };
        PerObjectMap<osg::NodeVisitor*, PerViewData> _perViewData;

        float _minPixelExtent;
        float _maxPixelExtent;
        float _minFadeExtent;
        float _maxFadeExtent;
    };

} }

#endif // OSGEARTH_FADE_EFFECT_H
