/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 * 
 *    (C) 2008, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library 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;
 *    version 2.1 of the License.
 *
 *    This library 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.
 */
package org.geotools.data;

import java.awt.RenderingHints;
import java.io.IOException;
import java.util.Set;

import org.geotools.factory.Hints;
import org.geotools.feature.FeatureCollection;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.Feature;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;


/**
 * This class provides a high-level API for operations on feature data. Typically,
 * when working with a data source such as a shapefile or database table you will
 * initially create a {@code DataStore} object to connect
 * to the physical source and then retrieve a {@code FeatureSource} to work with
 * the feature data, as in this excerpt from the GeoTools Quickstart example
 * (<a href="http://geotools.org/quickstart.html">http://geotools.org/quickstart.html</a>)
 * <pre><code>
 *     File file = ...
 *     FileDataStore store = FileDataStoreFinder.getDataStore(file);
 *     FeatureSource featureSource = store.getFeatureSource();
 * </code></pre>
 * @see DataStore
 *
 * @author Jody Garnett
 * @author Ray Gallagher
 * @author Rob Hranac, TOPP
 * @author Chris Holmes, TOPP
 * @source $URL: http://svn.osgeo.org/geotools/tags/2.7-M3/modules/library/api/src/main/java/org/geotools/data/FeatureSource.java $
 * @version $Id: FeatureSource.java 35550 2010-05-23 03:42:20Z jive $
 */
public interface FeatureSource<T extends FeatureType, F extends Feature>{
    
    /**
     * Returns the name of the features (strictly, the name of the 
     * {@code AttributeDescriptor} for the features) accessible through this
     * {@code FeatureSource}.
     * <p>
     * The value returned by this method can be different to that returned by
     * {@code featureSource.getSchema().getType().getName()}.
     * This is because there is a distinction between the name applied to features
     * and the name of a feature type. When working with {@code SimpleFeature} and
     * {@code SimpleFeatureType}, for example with a shapefile data source, it is
     * common practice for feature and feature type names to be the same. However, this
     * is not the case more generally. For instance, a database can contain two
     * tables with the same structure. The feature name will refer to the table
     * while the feature type name refers to the schema (table structure).
     *
     * @since 2.5
     * @return the name of the features accessible through this {@code FeatureSource}
     */
    Name getName();
    
    /**
     * Returns information describing this {@code FeatureSource} which may
     * include title, description and spatial parameters. Note that in the
     * returned {@code ResourceInfo} object, the distinction between feature
     * name and schema (feature type) name applies as discussed for
     * {@linkplain #getName()}.
     */
    ResourceInfo getInfo();
    
    /**
     * Returns the data source, as a {@code DataAccess} object, providing
     * this {@code FeatureSource}.
     *
     * @return the data source providing this {@code FeatureSource}
     */
    DataAccess<T, F> getDataStore();

    /**
     * Enquire what what query capabilities this {@code FeatureSource}
     * natively supports. For example, whether queries can return sorted
     * results.
     * 
     * @return the native query capabilities of this {@code FeatureSource}
     * @since 2.5
     */
    QueryCapabilities getQueryCapabilities();
    
    /**
     * Registers a listening object that will be notified of changes to this
     * {@code FeatureSource}.
     *
     * @param listener the new listener
     */
    void addFeatureListener(FeatureListener listener);

    /**
     * Removes an object from this {@code FeatureSource's} listeners.
     *
     * @param listener the listener to remove
     */
    void removeFeatureListener(FeatureListener listener);

    /**
     * Retrieves features, in the form of a {@code FeatureCollection}, based
     * on an OGC {@code Filter}.
     *
     * @param filter the filter to select features; must not be {@code null}
     *        (use {@linkplain Filter#INCLUDE} instead)
     *
     * @return features retrieved by the {@code Filter}
     *
     * @throws IOException if the underlying data source cannot be accessed.
     *
     * @see Filter
     */
    FeatureCollection<T, F> getFeatures(Filter filter) throws IOException;

    /**
     * Retrieves features, in the form of a {@code FeatureCollection}, based
     * on a {@code Query}.
     *
     * @param query DataAccess query for requested information, such as typeName,
     *        maxFeatures and filter.
     *
     * @return features retrieved by the {@code Query}
     *
     * @throws IOException if the underlying data source cannot be accessed.
     *
     * @see Query
     */
    FeatureCollection<T, F> getFeatures(Query query) throws IOException;

    /**
     * Retrieves all features in the form of a {@code FeatureCollection}.
     * <p>
     * The following statements are equivalent:
     * <pre><code>
     *     featureSource.getFeatures();
     *     featureSource.getFeatures(Filter.INCLUDE);
     *     featureSource.getFeatures(Query.ALL);
     * </code></pre>
     *
     * @return features retrieved by the {@code Query}
     *
     * @throws IOException if the underlying data source cannot be accessed.
     */
    FeatureCollection<T, F> getFeatures() throws IOException;

    /**
     * Retrieves the schema (feature type) that will apply to features retrieved
     * from this {@code FeatureSource}.
     * <p>
     * For a homogeneous data source such as a shapefile or a database table,
     * this schema be that of all features. For a heterogeneous data source,
     * e.g. a GML document, the schema returned is the lowest common denominator
     * across all features.
     *
     * @return the schema that will apply to features retrieved from this
     *         {@code FeatureSource}
     */
    T getSchema();

    /**
     * Get the spatial bounds of the feature data. This is equivalent
     * to calling <code>getBounds(Query.ALL)</code>.
     * <p>
     * It is possible that this method will return null if the calculation
     * of bounds is judged to be too costly by the implementing class. 
     * In this case, you might call <code>getFeatures().getBounds()</code>
     * instead.
     *
     * @return The bounding envelope of the feature data; or {@code null}
     *         if the bounds are unknown or too costly to calculate.
     *
     * @throws IOException on any errors calculating the bounds
     */
    ReferencedEnvelope getBounds() throws IOException;

    /**
     * Get the spatial bounds of the features that would be returned by
     * the given {@code Query}.
     * <p>
     * It is possible that this method will return null if the calculation
     * of bounds is judged to be too costly by the implementing class.
     * In this case, you might call <code>getFeatures(query).getBounds()</code>
     * instead.
     *
     * @param query the query to select features
     *
     * @return The bounding envelope of the feature data; or {@code null}
     *         if the bounds are unknown or too costly to calculate.
     *
     * @throws IOException on any errors calculating the bounds
     */
    ReferencedEnvelope getBounds(Query query) throws IOException;

    /**
     * Gets the number of the features that would be returned by the given
     * {@code Query}, taking into account any settings for max features and
     * start index set on the {@code Query}.
     * <p>
     * It is possible that this method will return {@code -1} if the calculation
     * of number of features is judged to be too costly by the implementing class.
     * In this case, you might call <code>getFeatures(query).getBounds()</code>
     * instead.
     *
     * @param query the query to select features
     *
     * @return the numer of features that would be returned by the {@code Query};
     *         or {@code -1} if this cannot be calculated.
     *
     * @throws IOException if there are errors getting the count
     */
    int getCount(Query query) throws IOException;

    /**
     * Returns the set of hints that this {@code FeatureSource} supports via {@code Query} requests.
     * <p>
     * Note: the existence of a specific hint does not guarantee that it will always be honored by
     * the implementing class.
     * 
     * @see Hints#FEATURE_DETACHED
     * @see Hints#JTS_GEOMETRY_FACTORY
     * @see Hints#JTS_COORDINATE_SEQUENCE_FACTORY
     * @see Hints#JTS_PRECISION_MODEL
     * @see Hints#JTS_SRID
     * @see Hints#GEOMETRY_DISTANCE
     * @see Hints#FEATURE_2D
     * @return a set of {@code RenderingHints#Key} objects; may be empty but never {@code null}
     */
    public Set<RenderingHints.Key> getSupportedHints();
    
    // FeatureReader getFeatureReader( Query query ); // ask justin for proposal
}