/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2008-2010 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/FeatureGridder>
#include <osgEarthFeatures/FeatureDisplayLayout>
#include <osgEarthFeatures/GeometryCompiler>
#include <osgEarthSymbology/Style>
#include <osgEarth/ModelSource>
#include <osgEarth/Map>
#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>& levels() { return _levels; }
        const optional<FeatureDisplayLayout>& levels() const { return _levels; }

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

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

    public: // deprecated

        /** @deprecated - use levels() instead */
        optional<GriddingPolicy>& gridding() { return _gridding; }
        const optional<GriddingPolicy>& gridding() const { return _gridding; }

        /** @deprecated - use the ConvertTypeFilter instead */
        optional<Symbology::Geometry::Type>& geometryTypeOverride() { return _geomTypeOverride; }
        const optional<Symbology::Geometry::Type>& geometryTypeOverride() const { return _geomTypeOverride; }

    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 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>  _levels;
        optional<StringExpression>      _featureNameExpr;
        optional<bool>                  _lit;
        optional<double>                _maxGranularity_deg;
        optional<bool>                  _mergeGeometry;
        optional<bool>                  _clusterCulling;

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

        /** @deprecated */
        optional<GriddingPolicy>        _gridding;
        optional<Geometry::Type>        _geomTypeOverride;
        
        //optional<Symbology::StyleSheet> _styles;
    };

    /**
     * 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 ) { return new osg::Group(); }
    };

    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:

        //override 
        virtual void initialize( const std::string& referenceURI, const osgEarth::Map* map );

        // override
        virtual osg::Node* createNode( ProgressCallback* progress );
    
        virtual FeatureNodeFactory* createFeatureNodeFactory() =0;


    public: // properties:

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

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

        virtual const FeatureModelSourceOptions& getFeatureModelOptions() const { return _options; }

        const osgEarth::Map* getMap() const { return _map.get(); }

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

    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::ref_ptr<const Map>           _map;
        const FeatureModelSourceOptions   _options;
        osg::ref_ptr<FeatureNodeFactory>  _factory;

    };

} } // namespace osgEarth::Features

#endif // OSGEARTHFEATURES_FEATURE_SOURCE_H

