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

#include <osgEarth/AnnotationNode>
#include <osgEarth/MapNode>
#include <osgEarth/GeometryClamper>
#include <osgEarth/StyleSheet>
#include <osgEarth/Feature>
#include <osgEarth/GeometryCompiler>
#include <osg/Polytope>
#include <osgEarth/DrapeableNode>
#include <osgEarth/ClampableNode>

namespace osgEarth
{
    /**
     * Renders a collection of Features.
     * If you change the feature geometry, you must call dirty() to refresh the node.
     */
    class OSGEARTH_EXPORT FeatureNode : public AnnotationNode
    {
    public:
        META_AnnotationNode(osgEarth, FeatureNode);

        /**
         * Construct a new FeatureNode from a single Feature.
         */
        FeatureNode(
            Feature* feature,
            const Style& style = Style(),
            const GeometryCompilerOptions& options = GeometryCompilerOptions(),
            StyleSheet* styleSheet = 0);

        /**
         * Constuct a new FeatureNode from a list of features.
         */
        FeatureNode(
            const FeatureList& features,
            const Style& style = Style(),
            const GeometryCompilerOptions& options = GeometryCompilerOptions(),
            StyleSheet* styleSheet = 0);

         /**
         * Gets the list of features
         */
        FeatureList& getFeatures() { return _features; }

        /**
         * Utility that lets you work on this FeatureNode as a single Feature instead of a list
         */
        Feature* getFeature();

        /**
         * Sets the contents of this FeatureNode to a single feature.
         */
        void setFeature(Feature* feature);

        /**
         * Gets the StyleSheet for the session.
         */
        StyleSheet* getStyleSheet() const;

        /**
         * Sets the StyleSheet for the session.
         */
        void setStyleSheet(StyleSheet* styleSheet);

        /**
         * Gets the FeatureIndexBuilder
         */
        FeatureIndexBuilder* getIndex();

        /**
        * Sets the FeatureIndexBuilder
        */
        void setIndex(FeatureIndexBuilder* index);

        /**
        * Gets the GeometryCompilerOptions
        */
        const GeometryCompilerOptions& getGeometryCompilerOptions() const;

        /**
        * Sets the GeometryCompilerOptions
        */
        void setGeometryCompilerOptions(GeometryCompilerOptions& options);

        /**
         * Force a rebuild of this FeatureNode.  If you modify the features in the features list or add/remove features
         * call this function to rebuild the node.
         */
        void dirty();

    public: // AnnotationNode

        /**
         * Gets the Style for this FeatureNode.
         */
        virtual const Style& getStyle() const;

        /**
         * Sets the style for this FeatureNode.
         * Note:  Do NOT use embedded feature styles if you need to change the style of the FeatureNode at runtime using this method.
         *        You will need to set the style of the features themselves and call init on this FeatureNode if you use embedded styles.
         */
        virtual void setStyle(const Style& style);

    public: // osg::Node

        void traverse(osg::NodeVisitor&) override;

    public: // MapNodeObserver

        void setMapNode(MapNode* mapNode) override;

    public:

        FeatureNode(const Config& conf, const osgDB::Options* options);
        virtual Config getConfig() const;

    protected:

        virtual ~FeatureNode() { }

        using ClampCallback = TerrainCallbackAdapter<FeatureNode>;

        FeatureList _features;
        GeometryCompilerOptions _options;
        osg::Group* _attachPoint = nullptr;
        osg::Polytope _featurePolytope;
        Style _style;
        GeoExtent _extent;
        osg::ref_ptr<ClampCallback> _clampCallback;
        GeometryClamper::LocalData _clamperData;
        osg::ref_ptr<DrapeableNode> _drapeableNode;
        osg::ref_ptr<ClampableNode> _clampableNode;
        osg::ref_ptr<osg::Node> _compiled;
        osg::ref_ptr<StyleSheet>_styleSheet;
        bool _needsRebuild = true;
        bool _clampDirty = false;
        FeatureIndexBuilder* _index = nullptr;

        //! Default subclass constructor
        FeatureNode() { }

        //! Default subclass copy constructor
        FeatureNode(const FeatureNode& rhs, const osg::CopyOp& op)
            : _attachPoint(rhs._attachPoint)
            , _needsRebuild(rhs._needsRebuild)
            , _clampDirty(rhs._clampDirty)
            , _index(rhs._index)
        { }

        void clamp(osg::Node* graph, const Terrain* terrain);

        void build();

    public:

        void onTileUpdate(
            const TileKey&          key,
            osg::Node*              graph,
            TerrainCallbackContext& context);
    };

}

#endif // OSGEARTH_ANNO_FEATURE_NODE_H
