/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2008-2013 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 <osgEarthFeatures/Common>
#include <osgEarthFeatures/FeatureSource>
#include <osgEarthFeatures/FeatureSourceIndexNode>
#include <osgEarthFeatures/FeatureDisplayLayout>
#include <osgEarthFeatures/GeometryCompiler>
#include <osgEarthSymbology/Style>
#include <osgEarth/FadeEffect>
#include <osgEarth/ModelSource>
#include <osgEarth/Map>
#include <osgEarth/CachePolicy>
#include <osg/Node>
#include <osgDB/ReaderWriter>
#include <list>

namespace osgEarth { namespace Features
{
    using namespace osgEarth;
    using namespace osgEarth::Symbology;

    class OSGEARTHFEATURES_EXPORT FeatureModelSourceOptions : public ModelSourceOptions
    {
    public: //properties

        optional<FeatureSourceOptions>& featureOptions() { return _featureOptions; }
        const optional<FeatureSourceOptions>& featureOptions() const { return _featureOptions; }

        osg::ref_ptr<StyleSheet>& styles() { return _styles; }
        const osg::ref_ptr<StyleSheet>& styles() const { return _styles; }

        /** 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 merge geometry from multiple features */
        optional<bool>& mergeGeometry() { return _mergeGeometry; }
        const optional<bool>& mergeGeometry() const { return _mergeGeometry; }

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

        optional<FeatureDisplayLayout>& layout() { return _layout; }
        const optional<FeatureDisplayLayout>& layout() const { return _layout; }

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

        optional<StringExpression>& featureName() { return _featureNameExpr; }
        const optional<StringExpression>& featureName() const { return _featureNameExpr; }

        /** Whether to create feature indexes (unset = no) */
        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; }

        /** Explicity caching policy for data from the underlying feature source */
        optional<CachePolicy>& cachePolicy() { return _cachePolicy; }
        const optional<CachePolicy>& cachePolicy() const { return _cachePolicy; }

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

    public:
        /** A live feature source instance to use. Note, this does not serialize. */
        osg::ref_ptr<FeatureSource>& featureSource() { return _featureSource; }
        const osg::ref_ptr<FeatureSource>& featureSource() const { return _featureSource; }

    public:
        FeatureModelSourceOptions( const ConfigOptions& rhs =ConfigOptions() );

        virtual ~FeatureModelSourceOptions() { }

        virtual Config getConfig() const;

    protected:
        virtual void mergeConfig( const Config& conf ) {
            ModelSourceOptions::mergeConfig( conf );
            fromConfig( conf );
        }

    private:
        void fromConfig( const Config& conf );

        optional<FeatureSourceOptions>      _featureOptions;
        optional<FeatureDisplayLayout>      _layout;
        optional<StringExpression>          _featureNameExpr;
        optional<bool>                      _lit;
        optional<double>                    _maxGranularity_deg;
        optional<bool>                      _mergeGeometry;
        optional<bool>                      _clusterCulling;
        optional<bool>                      _backfaceCulling;
        optional<bool>                      _alphaBlending;
        optional<CachePolicy>               _cachePolicy;
        optional<FadeOptions>               _fading;
        optional<FeatureSourceIndexOptions> _featureIndexing;

        osg::ref_ptr<StyleSheet>            _styles;
        osg::ref_ptr<FeatureSource>         _featureSource;
    };


    /**
     * 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 OSGEARTHFEATURES_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 ) =0;

        /**
         * 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 OSGEARTHFEATURES_EXPORT GeomFeatureNodeFactory : public FeatureNodeFactory
    {
    public:
        GeomFeatureNodeFactory( const GeometryCompilerOptions& options = GeometryCompilerOptions() );

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

    public:
        GeometryCompilerOptions _options;
    };


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


    public: // ModelSource

        virtual void initialize( const osgDB::Options* dbOptions =0L );

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

    public:

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


        /**
         * Creates an implementation-specific data object to be passed to buildNodeForStyle
         * @deprecated
         */
        //virtual osg::Referenced* createBuildData() { return NULL; }

    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 "osgEarthFeatures"; }

    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<const osgDB::Options> _dbOptions;

    };

} } // namespace osgEarth::Features

#endif // OSGEARTHFEATURES_FEATURE_SOURCE_H

