package org.vfny.geoserver.global;

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

import org.geotools.data.DataAccess;
import org.geotools.data.FeatureListener;
import org.geotools.data.FeatureLocking;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.Query;
import org.geotools.data.QueryCapabilities;
import org.geotools.data.ResourceInfo;
import org.geotools.feature.FeatureCollection;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

public class PrefetchingFeatureSource<T extends FeatureType, F extends Feature> implements FeatureSource<T, F> {
    FeatureSource<T, F> delegate;
    int queueDepth;
    
    public PrefetchingFeatureSource(FeatureSource<T, F> delegate, int queueDepth) {
        this.delegate = delegate;
        this.queueDepth = queueDepth;
    }

    public void addFeatureListener(FeatureListener listener) {
        delegate.addFeatureListener(listener);
    }

    public ReferencedEnvelope getBounds() throws IOException {
        return delegate.getBounds();
    }

    public ReferencedEnvelope getBounds(Query query) throws IOException {
        return delegate.getBounds(query);
    }

    public int getCount(Query query) throws IOException {
        return delegate.getCount(query);
    }

    public DataAccess<T, F> getDataStore() {
        return delegate.getDataStore();
    }

    public FeatureCollection<T, F> getFeatures() throws IOException {
        return new PrefetchingFeatureCollection<T, F>(delegate.getFeatures(), queueDepth);
    }

    public FeatureCollection<T, F> getFeatures(Filter filter) throws IOException {
        return new PrefetchingFeatureCollection<T, F>(delegate.getFeatures(filter), queueDepth);
    }

    public FeatureCollection<T, F> getFeatures(Query query) throws IOException {
        return new PrefetchingFeatureCollection<T, F>(delegate.getFeatures(query), queueDepth);
    }

    public ResourceInfo getInfo() {
        return delegate.getInfo();
    }

    public Name getName() {
        return delegate.getName();
    }

    public QueryCapabilities getQueryCapabilities() {
        return delegate.getQueryCapabilities();
    }

    public T getSchema() {
        return delegate.getSchema();
    }

    public Set<Key> getSupportedHints() {
        return delegate.getSupportedHints();
    }

    public void removeFeatureListener(FeatureListener listener) {
        delegate.removeFeatureListener(listener);
    }
    
    public static <T extends FeatureType, F extends Feature>
       FeatureSource<T, F> create(FeatureSource <T, F> featureSource, int queueDepth) {
        if(queueDepth <= 1)
            return featureSource;
        
            if (featureSource instanceof FeatureLocking) {
                return new PrefetchingFeatureLocking<T, F>(
                        (FeatureLocking<T, F>) featureSource, queueDepth);
            } else if (featureSource instanceof FeatureStore) {
                return new PrefetchingFeatureStore<T, F>(
                        (FeatureStore<T, F>) featureSource, queueDepth);
            }

            return new PrefetchingFeatureSource<T, F>(featureSource, queueDepth);
        }
}
