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

#ifndef OSGEARTH_PROGRESS_H
#define OSGEARTH_PROGRESS_H 1

#include <osgEarth/Common>
#include <osgEarth/Threading>
#include <osg/observer_ptr>

namespace osgEarth
{
    /**
    * ProgressCallback is a general purpose interface for functions that need to report progress
    * or handle cancelation of a task.
    */
    class OSGEARTH_EXPORT ProgressCallback : public osg::Referenced, public Cancelable
    {
    public:
        //! Creates a new ProgressCallback
        ProgressCallback();

        //! Creates a new ProgressCallback that proxies an existing Cancelable
        ProgressCallback(Cancelable* c);

        //! Creates a new ProgressCallback that proxies an existing Cancelable
        //! and adds a new predicate
        ProgressCallback(Cancelable* c, std::function<bool()> shouldCancel);

        /**
         * Report an error and set the canceled flag to true.
         */
        virtual void reportError(const std::string& msg);

        /**
        * Callback function that will be called.
        * @param current
        *        The amount of work done in the current stage
        * @param total
        *        The total amount of work to be done in the current stage
        * @param stage
        *        Stage of the operation we're currently in
        * @param totalStages
        *        Total number of stages in the operation
        * @param msg
        *        Description of what is being done. Useful when total is unknown.
        * @param returns
        *        Returns true if the current task should be cancelled, false otherwise.
        */
        virtual bool reportProgress(
            double             current, 
            double             total, 
            unsigned           currentStage,
            unsigned           totalStages,
            const std::string& msg );

        /**
         * Convenience functions
         */
        bool reportProgress( double current, double total, const std::string& msg ) {
            return reportProgress(current, total, 0, 1, msg );
        }
        bool reportProgress( double current, double total ) {
            return reportProgress(current, total, 0, 1, "" );
        }

        /**
         * called when the process starts
         */
        virtual void onStarted() { }

        /**
         * called when the process completed 
         */
        virtual void onCompleted() { }

        /**
         * Sets the cancelation flag
         */
        void cancel();

        /**
         * Whether the associated task was canceled for some reason.
         * Cancelation is NOT an error condition. It means that either
         * the results of the task are no longer required, or that a
         * recoverable problem occurred and the task is eligible to be
         * tried again later (e.g., HTTP timeout)
         */
        bool canceled() const override;

        bool isCanceled() const { return canceled(); } // backwards compatibility

        //! Resets the cancelation flag
        void reset();

        //! Read or write a status message
        std::string& message() { return _message; }
        const std::string& message() const { return _message; }

        //! Sets a retry delay in the event of cancelation
        void setRetryDelay(float value_seconds) { _retryDelay_s = value_seconds; }
        float getRetryDelay() const { return _retryDelay_s; }

    protected:
        virtual ~ProgressCallback() { }

        Cancelable* _cancelable;
        std::string _message;
        mutable bool _canceled;
        mutable float _retryDelay_s;
        mutable std::function<bool()> _cancelPredicate;

        //! Override this to tell the Progress to cancel.
        virtual bool shouldCancel() const { return false; }
    };

    /**
     * Progress callback that will invoke cancelation if
     * a referenced object gets destructed.
     */
    class OSGEARTH_EXPORT ObserverProgressCallback : public ProgressCallback
    {
    public:
        ObserverProgressCallback(osg::Referenced* host) :
            _host(host) { }

    protected:
        bool shouldCancel() const override {
            return _host.valid() == false;
        }

    private:
        osg::observer_ptr<osg::Referenced> _host;
    };

}

#endif
