/* -*-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_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 <osgEarth/Cache>
#include <osgEarth/CachePolicy>
#include <osgEarth/URI>
#include <osgEarth/Revisioning>

#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 ~FeatureSourceOptions();
        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;
        optional<CachePolicy>    _cachePolicy;
    };

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

        /**
         * 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: // blacklisting.

        /**
         * Adds a feature ID to the blacklist.
         */
        void addToBlacklist( FeatureID fid );

        /**
         * Removes a feature from the blacklist.
         */
        void removeFromBlacklist( FeatureID fid );

        /**
         * Clears the blacklist.
         */
        void clearBlacklist();
        
        /**
         * Checks the blacklist for a feature ID.
         */
        bool isBlacklisted( FeatureID fid ) const; 


    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.
         */
        virtual void initialize( const osgDB::Options* dbOptions =0L ) { }

        /** Dirties the feature profile */
        void dirtyFeatureProfile() { _featureProfile = 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();

        /** Access the raw DB options that came in */
        const osgDB::Options* dbOptions() const { return _dbOptions.get(); }

        /** The URI context (for resolving relative paths) */
        const URIContext& uriContext() const { return _uriContext; }

        /** The Cache passed in via the dbOptions */
        Cache* getCache() const { return _cache.get(); }            

    private:
        const FeatureSourceOptions         _options;
        osg::ref_ptr<const FeatureProfile> _featureProfile;
        Threading::Mutex                   _createMutex;

        osg::ref_ptr<const osgDB::Options> _dbOptions;
        URIContext                         _uriContext;
        osg::observer_ptr<Cache>           _cache;

        Threading::ReadWriteMutex          _blacklistMutex;
        std::set<FeatureID>                _blacklist;

        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

