/* -*-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.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* 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/>
*/
#pragma once

#include <osgEarth/Common>
#include <osgEarth/Profile>
#include <osgEarth/Progress>
#include <osgEarth/Map>
#include <osgEarth/Callbacks>
#include <osgEarth/SceneGraphCallback>
#include <osgEarth/CullingUtils>
#include <osg/Group>

namespace osgEarth { namespace Util
{
    using namespace osgEarth;
    class PagedNode2;

    class OSGEARTH_EXPORT SimplePager : public osg::Group
    {
    public:
        SimplePager(const osgEarth::Map* map, const osgEarth::Profile* dataProfile);

        //! Initializes the graph. If you override this, be sure to call
        //! buildRootNode and add that as a child of whatever you create.
        virtual void build();

        //! Whether this pager uses additive lods.
        bool getAdditive() const { return _additive; }
        void setAdditive(bool additive) { _additive = additive; }

        //! Tell the pager to stop paging and cancel pending requests
        void setDone();

        unsigned int getMinLevel() const { return _minLevel; }
        void setMinLevel(unsigned int minLevel) { _minLevel = minLevel; }

        unsigned int getMaxLevel() const { return _maxLevel; }
        void setMaxLevel(unsigned int maxLevel) { _maxLevel = maxLevel; }

        float getRangeFactor() const { return _rangeFactor; }
        void setRangeFactor(float value);

        float getMaxRange() const { return _maxRange; }
        void setMaxRange(float value);

        float getMaxPixels() const { return _maxPixels; }
        void setMaxPixels(float value);

        float getMinPixels() const { return _minPixels; }
        void setMinPixels(float value);

        void setLODMethod(const LODMethod& value);
        LODMethod getLODMethod() const { return _useRange ? LODMethod::CAMERA_DISTANCE : LODMethod::SCREEN_SPACE; }

        float getPriorityScale() const { return _priorityScale; }
        void setPriorityScale(float value) { _priorityScale = value; }

        float getPriorityOffset() const { return _priorityOffset; }
        void setPriorityOffset(float value) { _priorityOffset = value; }

        void setEnableCancelation(bool value);
        bool getEnableCancelation() const;

        //! Whether to install cluster culling for geographic nodes
        void setClusterCullingEnabled(bool value);
        bool getClusterCullingEnabled() const;

        //! Whether to use the bounds of loaded data to compute the paging bounds of that
        //! data's children. Default = false. (By default, it will use the tile key bounds
        //! and the built-in ElevationRanges to compute paging bounds.)
        //! This is optimal when you know every level will be populated by geometry that
        //! fits approximately inside its parent's bounds (e.g., terrain tiles).
        void setUsePayloadBoundsForChildren(bool value);
        bool getUsePayloadBoundsForChildren() const;

        using CreateNodeFunction = std::function<osg::ref_ptr<osg::Node>(const TileKey& key, ProgressCallback* progress)>;
        void setCreateNodeFunction(CreateNodeFunction func) { _createNodeFunction = func; }

        using CreatePagedNodeFunction = std::function<PagedNode2*(const TileKey&)>;
        void setCreatePagedNodeFunction(CreatePagedNodeFunction func) { _createPagedNodeFunction = func; }

        //! Scene graph callbacks for notification of changes. Call before calling build().
        void setSceneGraphCallbacks(SceneGraphCallbacks* value) { _sceneGraphCallbacks = value; }
        SceneGraphCallbacks* getSceneGraphCallbacks() const { return _sceneGraphCallbacks.get(); }

        //! Gets the profile for the SimplePager.
        const osgEarth::Profile* getProfile() const;

        //! Creates a payload for the given TileKey.
        //! Doesn't do anything with paging, just gets the raw data.
        //! Subclasses override this method to provide their data for TileKeys, OR you can
        //! call setCreateNodeFunction and createNode will call that.
        virtual osg::ref_ptr<osg::Node> createNode(const TileKey& key, ProgressCallback* progress);

        //! Callback fired when a node is created.
        Callback<void(const TileKey&, osg::Node*)> onCreateNode;

    public:

        //! (INTERNAL) Creates a group containing 4 PagedNodes,
        //! one for each child of the given TileKey.
        osg::ref_ptr<osg::Node> createPagedChildrenOf(const TileKey& parent, ProgressCallback* progress);

        void traverse(osg::NodeVisitor&) override;

    protected:

        //! Gets the bounding sphere for a given TileKey.
        osg::BoundingSphered getBounds(const TileKey& key) const;

        //! The root node is a special node. If you override this method,
        //! be sure to all this to include it in the scene graph.
        osg::ref_ptr<osg::Node> buildRootNode();

        //! Creates a paging data for the given TileKey
        osg::ref_ptr<osg::Node> createChildNode(const TileKey& key, ProgressCallback* progress);

        bool _additive = false;
        bool _useRange = true;
        double _rangeFactor = 6.0;
        float _maxRange = FLT_MAX;
        float _minPixels = 0.0f;
        float _maxPixels = FLT_MAX;
        unsigned int _minLevel = 0;
        unsigned int _maxLevel = 30;
        float _priorityScale = 1.0f;
        float _priorityOffset = 0.0f;
        bool _canCancel = true;
        bool _done = false;
        bool _clusterCullingEnabled = true;
        bool _usePayloadBoundsForChildren = false;

        osg::observer_ptr<const osgEarth::Map> _map;
        osg::ref_ptr<const Profile> _profile;
        osg::ref_ptr<const Profile> _mapProfile;
        osg::ref_ptr<SceneGraphCallbacks> _sceneGraphCallbacks;
        CreateNodeFunction _createNodeFunction;
        CreatePagedNodeFunction _createPagedNodeFunction;

        void fire_onCreateNode(const TileKey& key, osg::Node* node);
    };

} } // namespace osgEarth::Util