/* -*-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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * 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/>
 */

#ifndef OSGEARTHFEATURES_FEATURE_MODEL_SOURCE_H
#define OSGEARTHFEATURES_FEATURE_MODEL_SOURCE_H 1

#include <osgEarth/Common>
#include <osgEarth/FeatureSource>
#include <osgEarth/FeatureSourceIndexNode>
#include <osgEarth/FeatureDisplayLayout>
#include <osgEarth/GeometryCompiler>
#include <osgEarth/StyleSheet>
#include <osgEarth/FadeEffect>
#include <osgEarth/ModelSource>
#include <osgEarth/Map>
#include <osgEarth/LayerReference>
#include <osg/Node>

namespace osgEarth
{    
    class OSGEARTH_EXPORT FeatureModelOptions
    {
    public: //properties
        
        OE_OPTION_LAYER(StyleSheet, styleSheet);

        /** Maximum span of a generated edge, in degrees. For geocentric maps only */
        optional<double>& maxGranularity() { return _maxGranularity_deg; }
        const optional<double>& maxGranularity() const { return _maxGranularity_deg; }

        /** Whether to explicity set/clear GL lighting on the result */
        optional<bool>& enableLighting() { return _lit; }
        const optional<bool>& enableLighting() const { return _lit; }

        /** Information that controls paging and tiling of the dataset */
        optional<FeatureDisplayLayout>& layout() { return _layout; }
        const optional<FeatureDisplayLayout>& layout() const { return _layout; }

        optional<bool>& clusterCulling() { return _clusterCulling; }
        const optional<bool>& clusterCulling() const { return _clusterCulling; }

        /** Expression that will assign a node name to geometry built from each feature.
            Note; this may disable various scene graph optimizations. */
        optional<StringExpression>& featureName() { return _featureNameExpr; }
        const optional<StringExpression>& featureName() const { return _featureNameExpr; }

        /** Whether to index feature data for use with the global ObjectIndex (default = yes)
            for the purposes of search, picking, etc. */
        optional<FeatureSourceIndexOptions>& featureIndexing() { return _featureIndexing; }
        const optional<FeatureSourceIndexOptions>& featureIndexing() const { return _featureIndexing; }

        /** Whether to activate backface culling (default = yes) */
        optional<bool>& backfaceCulling() { return _backfaceCulling; }
        const optional<bool>& backfaceCulling() const { return _backfaceCulling; }

        /** Whether to activate alpha blending (default = yes) */
        optional<bool>& alphaBlending() { return _alphaBlending; }
        const optional<bool>& alphaBlending() const { return _alphaBlending; }

        /** Fading properties */
        optional<FadeOptions>& fading() { return _fading; }
        const optional<FadeOptions>& fading() const { return _fading; }

        /** Whether to enable caching of actual OSG nodes. default = false. */
        optional<bool>& nodeCaching() { return _nodeCaching; }
        const optional<bool>& nodeCaching() const { return _nodeCaching; }

        /** Debug: whether to enable a session-wide resource cache (default=true) */
        optional<bool>& sessionWideResourceCache() { return _sessionWideResourceCache; }
        const optional<bool>& sessionWideResourceCache() const { return _sessionWideResourceCache; }

        /** Options feature filters */
        OE_OPTION_VECTOR(ConfigOptions, filters);

    public:
        FeatureModelOptions(const ConfigOptions& co =ConfigOptions());

        void fromConfig(const Config& conf);
        Config getConfig() const;

    protected:
        optional<FeatureDisplayLayout>      _layout;
        optional<StringExpression>          _featureNameExpr;
        optional<bool>                      _lit;
        optional<double>                    _maxGranularity_deg;
        optional<bool>                      _clusterCulling;
        optional<bool>                      _backfaceCulling;
        optional<bool>                      _alphaBlending;
        optional<FadeOptions>               _fading;
        optional<FeatureSourceIndexOptions> _featureIndexing;
        optional<bool>                      _sessionWideResourceCache;
        optional<bool>                      _nodeCaching;
    };

    /**
     * Interface for a class that can create a Node from a set of features and
     * a style definition. You will provide this to a FeatureModeGraph when
     * creating a feature node in a driver.
     */
    class OSGEARTH_EXPORT FeatureNodeFactory : public osg::Referenced
    {
    public:
        /**
         * Render (or update) a list of features into a node according to the specified
         * style.
         */
        virtual bool createOrUpdateNode(
            FeatureCursor*            cursor,
            const Style&              style,
            const FilterContext&      context,
            osg::ref_ptr<osg::Node>&  node,
            const Query&              query)
        {
            // for backwards compatibility with old API
            return createOrUpdateNode(cursor, style, context, node);
        }

        virtual bool createOrUpdateNode(
            FeatureCursor*            cursor,
            const Style&              style,
            const FilterContext&      context,
            osg::ref_ptr<osg::Node>&  node)
        {
            return false;
        }

        /**
         * Creates a group that will contain all the geometry corresponding to a
         * given style. The subclass has the option of overriding this in order to create
         * a custom implementation.
         */
        virtual osg::Group* getOrCreateStyleGroup(
            const Style& style,
            Session*     session );
    };

    
    /**
     * A Feature node factory that invokes the GeometryCompiler.
     */
    class OSGEARTH_EXPORT GeomFeatureNodeFactory : public FeatureNodeFactory
    {
    public:
        GeomFeatureNodeFactory(
            const GeometryCompilerOptions& options);


        bool createOrUpdateNode(       
            FeatureCursor*            features,
            const Style&              style,
            const FilterContext&      context,
            osg::ref_ptr<osg::Node>&  node,
            const Query&                    query);

    public:
        GeometryCompilerOptions _options;
    };

#if 0
    /**
     * A ModelSource that renders Feature data from a FeatureSource.
     * @deprecated
     */
    class OSGEARTH_EXPORT FeatureModelSource : public ModelSource
    {
    public:
        /**
         * Constructs a new feature model source with the provided options.
         */
        FeatureModelSource( const FeatureModelSourceOptions& options =FeatureModelSourceOptions() );

        /**
         * Sets a new read options.
         */
        void setReadOptions(const osgDB::Options* readOptions);


    protected: // ModelSource

        virtual Status initialize(const osgDB::Options* readOptions);

        virtual osg::Node* createNodeImplementation(
            const Map*        map,
            ProgressCallback* progress );

    public:

        /**
         * Abstract - the implementation class must define a feature node factory object
         * that will actually bulid feature geometry.
         */
        virtual FeatureNodeFactory* createFeatureNodeFactory() =0;


    public: // properties:

        /** Sets a feature source. */
        void setFeatureSource( FeatureSource* source );

        /** The underlying feature source. */
        FeatureSource* getFeatureSource() { return _features.get(); }

        /** The options with which this source was created */
        virtual const FeatureModelSourceOptions& getFeatureModelOptions() const { return _options; }

    public: 

        // META_Object specialization:
        virtual osg::Object* cloneType() const { return 0; } // cloneType() not appropriate
        virtual osg::Object* clone(const osg::CopyOp&) const { return 0; } // clone() not appropriate
        virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const FeatureModelSource*>(obj)!=NULL; }
        virtual const char* className() const { return "FeatureModelSource"; }
        virtual const char* libraryName() const { return "osgEarth"; }

    protected:

        /** DTOR is protected to prevent this object from being allocated on the stack */
        virtual ~FeatureModelSource() { }

        osg::ref_ptr<FeatureSource>        _features;
        osg::observer_ptr<const Map>       _map;
        const FeatureModelSourceOptions    _options;
        osg::ref_ptr<FeatureNodeFactory>   _factory;
        osg::ref_ptr<osgDB::Options>       _readOptions;

    };
#endif

} // namespace osgEarth

#endif // OSGEARTHFEATURES_FEATURE_SOURCE_H

