/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2004-2006, Geotools Project Managment Committee (PMC)
 *    (C) 2004 TOPP - www.openplans.org
 *
 *    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.validation.spatial;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.geotools.feature.Feature;
import org.geotools.feature.FeatureType;
import org.geotools.validation.DefaultFeatureValidation;
import org.geotools.validation.ValidationResults;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;


/**
* Ensure the defaultGeometry does not overlap (only works for LineString).
*
* <p>
* Tests to see if a LineString overlaps itself. It does this by breaking up
* the LineString into two point segments then intersects them all. If a
* segment has both of its points on another segment, then they overlap. This
* is not true in all cases and this method has to be rewritten. If a segment
* spans two segments, this method will say that they do not overlap when
* clearly they do.
* </p>
*
* @author bowens, Refractions Research, Inc.
* @author $Author: jive $ (last modification)
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/validation/src/main/java/org/geotools/validation/spatial/LineNoSelfOverlappingValidation.java $
* @version $Id: LineNoSelfOverlappingValidation.java 27862 2007-11-12 19:51:19Z desruisseaux $
*/
public class LineNoSelfOverlappingValidation extends DefaultFeatureValidation {
   /** The logger for the validation module. */
   private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger(
           "org.geotools.validation");

   /**
    * LineNoSelfOverlappingFeatureValidation constructor.
    *
    * <p>
    * Description
    * </p>
    */
   public LineNoSelfOverlappingValidation() {
   		System.out.println("***************** LineNoSelfOverlappingValidation *************");
   }

   /**
    * Override getPriority.
    *
    * <p>
    * Sets the priority level of this validation.
    * </p>
    *
    * @return A made up priority for this validation.
    *
    * @see org.geotools.validation.Validation#getPriority()
    */
   public int getPriority() {
       return PRIORITY_COMPLEX;
   }


   /**
    * Tests to see if a LineString overlaps itself.
    *
    * <p>
    * It does this by breaking up the LineString into two point segments then
    * intersects them all. If a segment has both of its points on another
    * segment, then they overlap. This is not true in all cases and this
    * method has to be rewritten. If a segment spans two segments, this
    * method will say that they do not overlap when clearly they do.
    * </p>
    *
    * @param feature The Feature to be validated
    * @param type The FeatureTypeInfo of the feature
    * @param results The storage for error messages.
    *
    * @return True if the feature does not overlap itself.
    *
    * @see org.geotools.validation.FeatureValidation#validate(org.geotools.feature.Feature,
    *      org.geotools.feature.FeatureTypeInfo,
    *      org.geotools.validation.ValidationResults)
    */
   public boolean validate(Feature feature, FeatureType type,
       ValidationResults results) {
       //BUG: refer to comments above.
       LOGGER.setLevel(Level.ALL);
  
       LineString line = null;
       try {
           line = getDefaultLineString( feature );
       }
       catch( ClassCastException unLine ){            
           results.error(feature,"Geometry is required to be a LineString");
           System.out.println( feature.getID()+"  name: "+getName() );
           System.out.println( feature.getID()+"   ref: "+getTypeRef() );
           System.out.println( feature.getID()+"   ref: "+getTypeRefs() );            
       }
       if (line == null) {
           // Ignore null geometry (user can check with nullZero )
           return true;
       }
       if (line.getNumPoints() < 2) {
           results.warning(feature,"LineString contains too few points");            
           return false;
       }
       GeometryFactory gf = new GeometryFactory();

       int numPoints = line.getNumPoints();

       // break up the LineString into line segments
       LineString[] segments = new LineString[numPoints - 1];

       for (int i = 0; i < (numPoints - 1); i++) {
           Coordinate[] coords = new Coordinate[] {
                   line.getCoordinateN(i), line.getCoordinateN(i + 1)
               };
           segments[i] = gf.createLineString(coords);
       }
       
       // overlap all of the line segments with each other
       for (int i = 0; i < segments.length; i++) // for each line segment
       {
           for (int j = 0; j < segments.length; j++) // test with every other line segment
           {
           		if ((i != j)) // if they aren't the same segment
           		{
					if (segments[i].relate(segments[j],"1********")) // changed to relate - bowens
					{
	                    results.error(feature, "LineString overlapped itself.");
	                    return false;
	                }
           		}
           	}
       }
       
       //LOGGER.log(Level.FINEST, getName() + "(" + feature.getID() + ") passed");
       return true;
   }
     /*
    * touchesSegment
    * iterates all segments and returns true as soon as the point is found to intersect with the linestring
    * */
   private boolean touchesSegment(LineString[] segments, Point p1) {
       for(int i = 0; i < segments.length; i++ ) {
           //using intersects will handle a point that is not  an end point on the line segment
           if(p1.intersects(segments[i])) {
               return true;
           }
       }
       return false;
   }
}

