/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
 * full list of contributors). Published under the Clear BSD license.  
 * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */

/**
 * @requires OpenLayers/Strategy.js
 * @requires OpenLayers/Filter.js
 */

/**
 * Class: OpenLayers.Strategy.Filter
 * Strategy for limiting features that get added to a layer by 
 *     evaluating a filter.  The strategy maintains a cache of
 *     all features until removeFeatures is called on the layer.
 *
 * Inherits from:
 *  - <OpenLayers.Strategy>
 */
OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {
    
    /**
     * APIProperty: filter
     * {<OpenLayers.Filter>}  Filter for limiting features sent to the layer.
     *     Use the <setFilter> method to update this filter after construction.
     */
    filter: null,
    
    /**
     * Property: cache
     * {Array(<OpenLayers.Feature.Vector>)} List of currently cached
     *     features.
     */
    cache: null,
    
    /**
     * Property: caching
     * {Boolean} The filter is currently caching features.
     */
    caching: false,
    
    /**
     * Constructor: OpenLayers.Strategy.Filter
     * Create a new filter strategy.
     *
     * Parameters:
     * options - {Object} Optional object whose properties will be set on the
     *     instance.  Strategy must be constructed with at least a <filter> 
     *     property.
     */
    initialize: function(options) {
        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
        if (!this.filter || !(this.filter instanceof OpenLayers.Filter)) {
            throw new Error("Filter strategy must be constructed with a filter");
        }
    },

    /**
     * APIMethod: activate
     * Activate the strategy.  Register any listeners, do appropriate setup.
     *     By default, this strategy automatically activates itself when a layer
     *     is added to a map.
     *
     * Returns:
     * {Boolean} True if the strategy was successfully activated or false if
     *      the strategy was already active.
     */
    activate: function() {
        var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);
        if (activated) {
            this.cache = [];
            this.layer.events.on({
                "beforefeaturesadded": this.handleAdd,
                "beforefeaturesremoved": this.handleRemove,
                scope: this
            });
        }
        return activated;
    },
    
    /**
     * APIMethod: deactivate
     * Deactivate the strategy.  Clear the feature cache.
     *
     * Returns:
     * {Boolean} True if the strategy was successfully deactivated or false if
     *      the strategy was already inactive.
     */
    deactivate: function() {
        this.cache = null;
        if (this.layer && this.layer.events) {
            this.layer.events.un({
                "beforefeaturesadded": this.handleAdd,
                "beforefeaturesremoved": this.handleRemove,
                scope: this
            });            
        }
        return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);
    },
    
    /**
     * Method: handleAdd
     */
    handleAdd: function(event) {
        if (!this.caching) {
            var features = event.features;
            event.features = [];
            var feature;
            for (var i=0, ii=features.length; i<ii; ++i) {
                feature = features[i];
                if (this.filter.evaluate(feature)) {
                    event.features.push(feature);
                } else {
                    this.cache.push(feature);
                }
            }
        }
    },
    
    /**
     * Method: handleRemove
     */
    handleRemove: function(event) {
        if (!this.caching) {
            this.cache = [];
        }
    },

    /** 
     * APIMethod: setFilter
     * Update the filter for this strategy.  This will re-evaluate
     *     any features on the layer and in the cache.  Only features
     *     for which filter.evalute(feature) returns true will be
     *     added to the layer.  Others will be cached by the strategy.
     *
     * Parameters:
     * filter - <OpenLayers.Filter> A filter for evaluating features.
     */
    setFilter: function(filter) {
        this.filter = filter;
        var previousCache = this.cache;
        this.cache = [];
        // look through layer for features to remove from layer
        this.handleAdd({features: this.layer.features});
        // cache now contains features to remove from layer
        if (this.cache.length > 0) {
            this.caching = true;
            this.layer.removeFeatures(this.cache.slice(), {silent: true});
            this.caching = false;
        }
        // now look through previous cache for features to add to layer
        if (previousCache.length > 0) {
            var event = {features: previousCache};
            this.handleAdd(event);
            // event has features to add to layer
            this.caching = true;
            this.layer.addFeatures(event.features, {silent: true});
            this.caching = false;
        }
    },

    CLASS_NAME: "OpenLayers.Strategy.Filter"

});
