/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2011, 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.complex.filter;

import java.util.List;

import org.geotools.data.complex.FeatureTypeMapping;
import org.geotools.data.complex.filter.XPath.StepList;
import org.geotools.filter.FilterCapabilities;
import org.geotools.filter.visitor.PostPreProcessFilterSplittingVisitor;
import org.opengis.filter.Id;
import org.opengis.filter.expression.Add;
import org.opengis.filter.expression.BinaryExpression;
import org.opengis.filter.expression.Divide;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.Multiply;
import org.opengis.filter.expression.NilExpression;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.expression.Subtract;

/**
 * @author Niels Charlier (Curtin University of Technology)
 */
public class ComplexFilterSplitter extends PostPreProcessFilterSplittingVisitor {
    
    public class CapabilitiesExpressionVisitor implements ExpressionVisitor {
        
        protected boolean capable = true;
        
        public boolean isCapable(){
            return capable;
        }

        public Object visit(NilExpression expr, Object extraData) {
            return null;
        }

        public Object visit(Add expr, Object extraData) {
            visitMathExpression(expr);
            return null;
        }        

        public Object visit(Subtract expr, Object extraData) {
            visitMathExpression(expr);
            return null;
        }

        public Object visit(Divide expr, Object extraData) {
            visitMathExpression(expr);
            return null;
        }

        public Object visit(Multiply expr, Object extraData) {
            visitMathExpression(expr);
            return null;
        }
        
        public Object visit(Function expr, Object extraData) {
            for (int i = 0; i < expr.getParameters().size(); i++) {
                ((Expression)expr.getParameters().get(i)).accept(this, null);
            }
            
            capable = capable && fcs.supports(expr.getClass());
            return null;
        }

        public Object visit(Literal expr, Object extraData) {
            return null;
        }

        public Object visit(PropertyName expr, Object extraData) {
            return null;
        }
        
        private void visitMathExpression(BinaryExpression expression) {           
            expression.getExpression1().accept(this, null);
            expression.getExpression2().accept(this, null);
            
            capable = capable && fcs.supports(expression.getClass());
        }

    }

    private FeatureTypeMapping mappings;
    
    public ComplexFilterSplitter(FilterCapabilities fcs, FeatureTypeMapping mappings) {
        super(fcs, null, null);
        this.mappings = mappings;
    }
    
    public Object visit(Id filter, Object notUsed) {
        
        CapabilitiesExpressionVisitor visitor = new CapabilitiesExpressionVisitor();
        mappings.getFeatureIdExpression().accept(visitor, null);
        
        if (visitor.isCapable()) {
            super.visit(filter, notUsed);
        } else {
            postStack.push(filter);
        }
        
        return null;
    }
    
    public Object visit(PropertyName expression, Object notUsed) {
        
        // break into single steps
        StepList exprSteps = XPath.steps(mappings.getTargetFeature(), expression.getPropertyName(), expression.getNamespaceContext());

        if (exprSteps.containsPredicate()) {
            postStack.push(expression);
            return null;
        }
        
        if (exprSteps.size() > 1) {
            List<Expression> matchingMappings = mappings.findMappingsFor(exprSteps);
    
            if (matchingMappings.isEmpty()) {
                postStack.push(expression);
                return null;
            } else {
                for (Expression expr : matchingMappings) {
                    CapabilitiesExpressionVisitor visitor = new CapabilitiesExpressionVisitor();
                    expr.accept(visitor, null);
                    
                    if (!visitor.isCapable()) {
                        postStack.push(expression);
                        return null;
                    }
                }
            }
        }
        
        return super.visit(expression, notUsed);
        
    }

}

@@ -4375,48 +4375,8 @@
    %0A
-        if (exprSteps.size() %3E 1) %7B%0A    
     
@@ -4452,25 +4452,17 @@
 Steps);%0A
-    %0A    
+%0A
         
@@ -4499,36 +4499,32 @@
 ) %7B%0A            
-    
 postStack.push(e
@@ -4527,36 +4527,32 @@
 sh(expression);%0A
-    
             retu
@@ -4564,28 +4564,24 @@
 ll;%0A        
-    
 %7D else %7B%0A   
@@ -4585,28 +4585,24 @@
             
-   
- 
 for (Express
@@ -4628,28 +4628,24 @@
 Mappings) %7B%0A
-    
             
@@ -4737,28 +4737,24 @@
             
-    
 expr.accept(
@@ -4773,32 +4773,9 @@
 l);%0A
-                    %0A   
+%0A
     
@@ -4782,25 +4782,24 @@
             
- 
 if (!visitor
@@ -4806,36 +4806,32 @@
 .isCapable()) %7B%0A
-    
                 
@@ -4874,36 +4874,32 @@
                 
-    
 return null;%0A   
@@ -4899,30 +4899,8 @@
 ll;%0A
-                    %7D%0A
     

fix complex filter splitter