/* -*-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_SOURCE_H
#define OSGEARTHFEATURES_FEATURE_SOURCE_H 1

#include <osgEarthFeatures/Common>
#include <osgEarthFeatures/Feature>
#include <osgEarthFeatures/FeatureCursor>
#include <osgEarthSymbology/Geometry>
#include <osgEarthSymbology/Query>
#include <osgEarthFeatures/Filter>
#include <osgEarthSymbology/Style>
#include <osgEarth/Profile>
#include <osgEarth/GeoData>
#include <osgDB/ReaderWriter>
#include <OpenThreads/Mutex>
#include <list>

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

    /**
     * Configuration options for creating a FeatureSource.
     */
    class OSGEARTHFEATURES_EXPORT FeatureSourceOptions : public DriverConfigOptions
    {
    public: // properties

        /** Name of this feature source. */
        optional<std::string>& name() { return _name; }
        const optional<std::string>& name() const { return _name; }

        /** List of filteres to apply to features read from this source */
        FeatureFilterList& filters() { return _filters; }
        const FeatureFilterList& filters() const { return _filters; }

        /** Opens the feature data for writing, if supported */
        optional<bool>& openWrite() { return _openWrite; }
        const optional<bool>& openWrite() const { return _openWrite; }

        /** Explicitly overrides profile information contained in the actual data source. */
        optional<ProfileOptions>& profile() { return _profile; }
        const optional<ProfileOptions>& profile() const { return _profile; }


    public:
        FeatureSourceOptions( const ConfigOptions& options =ConfigOptions() );
        virtual Config getConfig() const;

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

    private:
        void fromConfig( const Config& conf );

        FeatureFilterList        _filters;
        optional<std::string>    _name;
        optional< bool >         _openWrite;
        optional<ProfileOptions> _profile;
    };

    /**
     * A FeatureSource is a pluggable object that generates Features, and 
     * optionally, styling information to go along with them.
     */
    class OSGEARTHFEATURES_EXPORT FeatureSource : public virtual Revisioned<osg::Object> // public virtual osg::Object
    {
    public:      
        /**
         * Constructs a new feature source with the provided read/write options.
         */
        FeatureSource( const ConfigOptions& options =ConfigOptions() );

        /**
         * Gets a reference to the metadata that describes features that you can
         * get from this FeatureSource. A valid feature profile indiciates that the
         * feature source successfully initialized.
         */
        const FeatureProfile* getFeatureProfile() const;

        /**
         * Gets the options that were passed into this object's CTOR.
         */
        const FeatureSourceOptions& getFeatureSourceOptions() const { return _options; }

        /**
         * Creates a cursor that iterates over all the features corresponding to the
         * specified query.
         *
         * Caller takes ownership of the returned object.
         */
        virtual FeatureCursor* createFeatureCursor( const Symbology::Query& query =Symbology::Query() ) =0;

        /**
         * Whether this FeatureSource supports inserting and deleting features
         */
        virtual bool isWritable() const { return false; }

        /**
         * Deletes the feature with the given FID
         * @return True on success; false on failure or if the source is read-only
         */
        virtual bool deleteFeature(FeatureID fid) { return false; }

        /**
         * Gets the number of features in this FeatureSource
         * @return
         *      The number of features or -1 if the number of features cannot be determined.
         */
        virtual int getFeatureCount() const { return -1; }

        /**
         * Gets the Feature with the given FID
         * @return
         *     The Feature with the given FID or NULL if not found.
         */
        virtual Feature* getFeature( FeatureID fid ) { return 0L; }

        /**
         * Gets the FeatureSchema for this FeatureSource. If the schema doesn't
         * publish a source, this might be empty.
         */
        virtual const FeatureSchema& getSchema() const;

        /**
         * Inserts the given feature into the FeatureSource
         * @return
         *     True if the feature was inserted, false if not
         */
        virtual bool insertFeature(Feature* feature) { return false; }

        /**
         * Gets the Geometry type of the FeatureSource
         * @return
         *      The Geometry type of the FeatureSource
         */
        virtual Geometry::Type getGeometryType() const { return Geometry::TYPE_UNKNOWN; }


    public: // Styling

        /**
         * Returns true if this source creates features with embedded style information.
         * By default, this is false (features are not expected to carry their own
         * style definitions).
         */
        virtual bool hasEmbeddedStyles() const {
            return false; }

    public:

        /**
         * Accesses the list of feature filters that will transform features
         * before they are returned in a feature cursor.
         */
        const FeatureFilterList& getFilters() const;

    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 FeatureSource*>(obj)!=NULL; }
        virtual const char* className() const { return "FeatureSource"; }
        virtual const char* libraryName() const { return "osgEarthFeatures"; }


		/**
		 * Initialize the FeatureSource.  The profile should be computed and set here using setProfile()
		 */
		virtual void initialize( const std::string& referenceURI ) =0;

    protected:

        /**
         * Creates and returns a metadata structure describing the features in a named
         * feature class. This method is called by the public function getFeatureProfile()
         * in this same object to create the metadata structure.
         */
        virtual const FeatureProfile* createFeatureProfile() =0;

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

    private:
        const FeatureSourceOptions _options;

        osg::ref_ptr<const FeatureProfile> _featureProfile;
        OpenThreads::Mutex _createMutex;

        friend class Map;
        friend class FeatureSourceFactory;
    };

    //--------------------------------------------------------------------

    class OSGEARTHFEATURES_EXPORT FeatureSourceDriver : public osgDB::ReaderWriter
    {
    public:
        virtual const FeatureSourceOptions& getFeatureSourceOptions( const osgDB::ReaderWriter::Options* rwopt ) const;
    };

    //--------------------------------------------------------------------

    /**
     * Factory class that will instantiate a FeatureSource corresponding to a driver name.
     */
    class OSGEARTHFEATURES_EXPORT FeatureSourceFactory
    {
	public:
        static FeatureSource* create( const FeatureSourceOptions& options );
    };

} } // namespace osgEarth::Features

#endif // OSGEARTHFEATURES_FEATURE_SOURCE_H

