/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002-2006, Geotools Project Managment Committee (PMC)
 *
 *    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; either
 *    version 2.1 of the License, or (at your option) any later version.
 *
 *    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.filter;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import junit.framework.AssertionFailedError;
import junit.framework.TestCase;

import org.geotools.gml.GMLFilterDocument;
import org.geotools.gml.GMLFilterGeometry;
import org.geotools.util.logging.Logging;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.ParserAdapter;
import org.xml.sax.helpers.XMLFilterImpl;

public class FilterFilterTest extends TestCase {

	public static void testWithoutFunction() throws Exception {
		String filter = "<wfs:GetFeature service=\"WFS\" version=\"1.0.0\" " +
  "outputFormat=\"GML2\" " +
  "xmlns:topp=\"http://www.openplans.org/topp\" " +
  "xmlns:wfs=\"http://www.opengis.net/wfs\" " +
  "xmlns:ogc=\"http://www.opengis.net/ogc\" " +
  "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
  "xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-basic.xsd\">" +
  "<wfs:Query typeName=\"topp:states\">" +
    "<Filter xmlns=\"http://www.opengis.net/ogc\" xmlns:gml=\"http://www.opengis.net/gml\">" +
      "<And>" +
        "<Intersects>" +
          "<PropertyName>the_geom</PropertyName>" +
            "<gml:Polygon>" +
              "<gml:outerBoundaryIs>" +
                "<gml:LinearRing><gml:coordinates decimal=\".\" cs=\",\" ts=\" \">-99.79800943339099,30.41833858217994 -99.79800943339099,30.71813913408305 -99.49820888148788,30.71813913408305 -99.49820888148788,30.41833858217994 -99.79800943339099,30.41833858217994</gml:coordinates>" +
                "</gml:LinearRing>" +
              "</gml:outerBoundaryIs>" +
	    "</gml:Polygon>" +
        "</Intersects>" +
        
          "<BBOX><PropertyName>the_geom</PropertyName>" +
            "<gml:Box><gml:coordinates decimal=\".\" cs=\",\" ts=\" \">-124.731422,24.955967 -66.969849,49.371735</gml:coordinates></gml:Box>" +
          "</BBOX>" +
      "</And>" +
  "</Filter>" +

    "</wfs:Query>" + 
"</wfs:GetFeature>";
		StringReader reader = new StringReader( filter );
		
		InputSource requestSource = new InputSource(reader);

		// instantiante parsers and content handlers
        MyHandler contentHandler = new MyHandler();
        FilterFilter filterParser = new FilterFilter(contentHandler, null);
        GMLFilterGeometry geometryFilter = new GMLFilterGeometry(filterParser);
        GMLFilterDocument documentFilter = new GMLFilterDocument(geometryFilter);

        // read in XML file and parse to content handler
    
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        ParserAdapter adapter = new ParserAdapter(parser.getParser());
        adapter.setContentHandler(documentFilter);
        adapter.parse(requestSource);
        
        assertEquals(contentHandler.filters.size(),1);
        
        LogicFilterImpl f = (LogicFilterImpl) contentHandler.filters.get(0);
        List sub = f.getSubFilters();
        assertEquals(2,sub.size());
        
        Filter f1 = (Filter) sub.get(0);
        Filter f2 = (Filter) sub.get(1);
        
        assertEquals(FilterType.GEOMETRY_INTERSECTS,f1.getFilterType());
        assertEquals(FilterType.GEOMETRY_BBOX, f2.getFilterType());
        
    }

	public void testWithFunction() throws Exception {
		String filter = "<wfs:GetFeature service=\"WFS\" version=\"1.0.0\" " +
		  "outputFormat=\"GML2\" " +
		  "xmlns:topp=\"http://www.openplans.org/topp\" " +
		  "xmlns:wfs=\"http://www.opengis.net/wfs\" " +
		  "xmlns:ogc=\"http://www.opengis.net/ogc\" " +
		  "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
		  "xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-basic.xsd\">" +
		  "<wfs:Query typeName=\"topp:states\">" +
		    "<Filter xmlns=\"http://www.opengis.net/ogc\" xmlns:gml=\"http://www.opengis.net/gml\">" +
		    "<Or>" + 
		      "<And>" +
		        "<Intersects>" +
		          "<PropertyName>the_geom</PropertyName>" +
		            "<gml:Polygon>" +
		              "<gml:outerBoundaryIs>" +
		                "<gml:LinearRing><gml:coordinates decimal=\".\" cs=\",\" ts=\" \">-99.79800943339099,30.41833858217994 -99.79800943339099,30.71813913408305 -99.49820888148788,30.71813913408305 -99.49820888148788,30.41833858217994 -99.79800943339099,30.41833858217994</gml:coordinates>" +
		                "</gml:LinearRing>" +
		              "</gml:outerBoundaryIs>" +
			    "</gml:Polygon>" +
		        "</Intersects>" +
		        
		          "<BBOX><PropertyName>the_geom</PropertyName>" +
		            "<gml:Box><gml:coordinates decimal=\".\" cs=\",\" ts=\" \">-124.731422,24.955967 -66.969849,49.371735</gml:coordinates></gml:Box>" +
		          "</BBOX>" +
		      "</And>" +
		      "<PropertyIsEqualTo>" + 
              "<Function name=\"geometryType\"><PropertyName>the_geom</PropertyName></Function>" + 
              "<Literal>Point</Literal>" + 
            "</PropertyIsEqualTo>" + 
            "</Or>" + 
		  "</Filter>" +

		    "</wfs:Query>" + 
		"</wfs:GetFeature>";
		
StringReader reader = new StringReader( filter );
		
		InputSource requestSource = new InputSource(reader);
		
//		 instantiante parsers and content handlers
        MyHandler contentHandler = new MyHandler();
        FilterFilter filterParser = new FilterFilter(contentHandler, null);
        GMLFilterGeometry geometryFilter = new GMLFilterGeometry(filterParser);
        GMLFilterDocument documentFilter = new GMLFilterDocument(geometryFilter);

        // read in XML file and parse to content handler
    
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        ParserAdapter adapter = new ParserAdapter(parser.getParser());
        adapter.setContentHandler(documentFilter);
        adapter.parse(requestSource);
        
        assertEquals(contentHandler.filters.size(),1);
	}

	public void testWithFunction2() throws Exception {
		String filter = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+
		"<sld:StyledLayerDescriptor xmlns:sld=\"http://www.opengis.net/sld\" xmlns:ogc=\"http://www.opengis.net/ogc\" xmlns:gml=\"http://www.opengis.net/gml\" version=\"1.0.0\">"+
	    "<sld:UserLayer>"+
	        "<sld:LayerFeatureConstraints>"+
	            "<sld:FeatureTypeConstraint/>"+
	        "</sld:LayerFeatureConstraints>"+
	        "<sld:UserStyle>"+
	            "<sld:FeatureTypeStyle>"+
	                "<sld:FeatureTypeName>Feature</sld:FeatureTypeName>"+
	                "<sld:Rule>"+
	                    "<ogc:Filter>"+
	                        "<ogc:PropertyIsEqualTo>"+
	                            "<ogc:Function name=\"geometryType\">"+
	                                "<ogc:PropertyName>the_geom</ogc:PropertyName>"+
	                            "</ogc:Function>"+
	                            "<ogc:Literal>Point</ogc:Literal>"+
	                        "</ogc:PropertyIsEqualTo>"+
	                    "</ogc:Filter>"+
	                    "<sld:PointSymbolizer>"+
	                    "</sld:PointSymbolizer>"+
	                "</sld:Rule>"+
	                "<sld:Rule>"+
	                    "<ogc:Filter>"+
	                        "<ogc:PropertyIsEqualTo>"+
	                            "<ogc:Function name=\"geometryType\">"+
	                                "<ogc:PropertyName>the_geom</ogc:PropertyName>"+
	                            "</ogc:Function>"+
	                            "<ogc:Literal>MultiPoint</ogc:Literal>"+
	                        "</ogc:PropertyIsEqualTo>"+
	                    "</ogc:Filter>"+
	                    "<sld:PointSymbolizer>"+
	                    "</sld:PointSymbolizer>"+
	                "</sld:Rule>"+
	            "</sld:FeatureTypeStyle>"+
	        "</sld:UserStyle>"+
	    "</sld:UserLayer>"+
	"</sld:StyledLayerDescriptor>";
		
StringReader reader = new StringReader( filter );
		
		InputSource requestSource = new InputSource(reader);
		
//		 instantiante parsers and content handlers
        MyHandler contentHandler = new MyHandler();
        FilterFilter filterParser = new FilterFilter(contentHandler, null);
        GMLFilterGeometry geometryFilter = new GMLFilterGeometry(filterParser);
        GMLFilterDocument documentFilter = new GMLFilterDocument(geometryFilter);

//        Logger logger = Logging.getLogger("org.geotools.filter");
//        logger.setLevel(Level.ALL);
//        ConsoleHandler consoleHandler = new ConsoleHandler();
//        consoleHandler.setLevel(Level.ALL);
//		logger.addHandler(consoleHandler);
        // read in XML file and parse to content handler
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        ParserAdapter adapter = new ParserAdapter(parser.getParser());
        adapter.setContentHandler(documentFilter);
        adapter.parse(requestSource);
        
//        assertEquals(contentHandler.filters.size(),1);
	}
    
    /**
     * As for GEOT-821, this test ensures that the filter parser makes proper use
     * of the characters(...) method in ContentHandler to not truncate the content
     * of attribute names
     *
     * @throws Exception
     */
    public void testLargeFilter() throws Exception{
        final int filterCount = 100;
        String filter = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
        "<GetFeature xmlns=\"http://www.opengis.net/wfs\" xmlns:gml=\"http://www.opengis.net/gml\" xmlns:ogc=\"http://www.opengis.net/ogc\" version=\"1.0.0\" service=\"WFS\" outputFormat=\"GML2\"><Query typeName=\"topp:roadevent_pnt\"><ogc:PropertyName>roadeventid</ogc:PropertyName>" +
        "<ogc:Filter>" +
        "<ogc:Or>";
        for(int i = 0; i < filterCount; i++){
            StringBuffer attName = new StringBuffer();
            for(int repCount = 0; repCount <= i; repCount++){
                attName.append("eventtype-" + repCount + "_");
            }

            filter += "<ogc:PropertyIsEqualTo><ogc:PropertyName>" + attName + "</ogc:PropertyName>" +
            "<ogc:Literal>literal-" + i + "</ogc:Literal>" +
            "</ogc:PropertyIsEqualTo>";
        }
        filter += "</ogc:Or>" +
        "</ogc:Filter>" +
        "</Query>" +
        "</GetFeature>";
        
        StringReader reader = new StringReader( filter );
        
        InputSource requestSource = new InputSource(reader);
        
//       instantiante parsers and content handlers
        MyHandler contentHandler = new MyHandler();
        FilterFilter filterParser = new FilterFilter(contentHandler, null);
        GMLFilterGeometry geometryFilter = new GMLFilterGeometry(filterParser);
        GMLFilterDocument documentFilter = new GMLFilterDocument(geometryFilter);

        Logger logger = Logging.getLogger("org.geotools.filter");
        logger.setLevel(Level.INFO);
        ConsoleHandler consoleHandler = new ConsoleHandler();
        consoleHandler.setLevel(Level.INFO);
        logger.addHandler(consoleHandler);
        
        // read in XML file and parse to content handler
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        ParserAdapter adapter = new ParserAdapter(parser.getParser());
        adapter.setContentHandler(documentFilter);
        adapter.parse(requestSource);
        
        assertEquals(1, contentHandler.filters.size());
        Filter f = (Filter)contentHandler.filters.get(0);
        assertTrue(f instanceof LogicFilter);
        assertEquals(FilterType.LOGIC_OR, f.getFilterType());
        
        int i = 0;
        for(Iterator filters = ((LogicFilter)f).getFilterIterator(); filters.hasNext(); i++){
            CompareFilter subFitler = (CompareFilter)filters.next();
            StringBuffer attName = new StringBuffer();
            for(int repCount = 0; repCount <= i; repCount++){
                attName.append("eventtype-" + repCount + "_");
            }
            String parsedName = ((AttributeExpression)subFitler.getLeftValue()).getAttributePath();
            try{
                assertEquals("at index " + i, attName.toString(), parsedName);
            }catch(AssertionFailedError e){
                Logging.getLogger("org.geotools.filter").warning("expected " + attName + ",\n but was " + parsedName);
                throw e;
            }
            assertEquals("literal-" + i, ((LiteralExpression)subFitler.getRightValue()).getLiteral());
        }
        assertEquals(filterCount, i);
    }

    static class MyHandler extends XMLFilterImpl implements FilterHandler {
	
		public List filters = new ArrayList();
		
		public void filter(org.opengis.filter.Filter filter) {
			filters.add(filter);
		}
	}		
}
