/* -*-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 <osgEarthSymbology/Style>
#include <osgEarth/ModelSource>
#include <osgEarth/Map>
#include <osg/Node>
#include <osgDB/ReaderWriter>
#include <list>

namespace osgEarth { namespace Features
{
    using namespace osgEarth;

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

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

        optional<GriddingPolicy>& gridding() { return _gridding; }
        const optional<GriddingPolicy>& gridding() const { return _gridding; }

        optional<Symbology::StyleCatalog>& styles() { return _styles; }
        const optional<Symbology::StyleCatalog>& styles() const { return _styles; }

        optional<Symbology::Geometry::Type>& geometryTypeOverride() { return _geomTypeOverride; }
        const optional<Symbology::Geometry::Type>& geometryTypeOverride() const { return _geomTypeOverride; }

        /** 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; }

        optional<bool>& enableLighting() { return _lit; }
        const optional<bool>& enableLighting() const { return _lit; }

    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<Symbology::StyleCatalog> _styles;
        optional<GriddingPolicy> _gridding;
        optional<Symbology::Geometry::Type> _geomTypeOverride;
        optional<bool> _lit;
        optional<double> _maxGranularity_deg;

        osg::ref_ptr<FeatureSource> _featureSource;
    };

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


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

    protected:


        /**
         * Creates OSG graph(s) representing the specified feature list.
         *
         * @param style
         *      Styling information for the feature geometry
         * @param features
         *      Features to render
         * @param buildData
         *      Implementation-specific build data (from createBuildData)
         * @param out_newNode 
         *      Populate this field with a pointer to the new node created by this method.
         *      This may be the same as the node returned by the method. If you do NOT
         *      return the node, that means that the method added the node to the scene
         *      graph itself (presumably by adding it to a group that it returned during
         *      a previous invocation).
         *
         * @return Node to add to the scene graph.
         */
        virtual osg::Node* renderFeaturesForStyle(
            const Symbology::Style* style,
            FeatureList& features,
            osg::Referenced* buildData,
            osg::Node** out_newNode =0L ) { return 0L; }


    private:   
        
        /** Creates an OSG graph containing objects rendered with the provided style. The model 
            source calls this once for each style class associated with this model source. This
            method will also grid the input features into cells if necessary, calling
            renderStyleCell() for each cell. */
        osg::Group* gridAndRenderFeaturesForStyle(
            const Symbology::Style* style,
            const Symbology::Query& query,
            osg::Referenced* buildData );


    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() { }

        //StyleCatalog _styleCatalog;
        osg::ref_ptr<FeatureSource> _features;
        osg::ref_ptr<const osgEarth::Map> _map;
        const FeatureModelSourceOptions _options;

    };

} } // namespace osgEarth::Features

#endif // OSGEARTHFEATURES_FEATURE_SOURCE_H

