content: package tim.prune.function.compress.methods;
  
  import tim.prune.data.DataPoint;
  import tim.prune.data.MarkingData;
  import tim.prune.data.Track;
  import tim.prune.function.compress.CompressionMethodType;
  import tim.prune.function.compress.TrackDetails;
  
  /** Parent class of all the compression methods */
  public abstract class CompressionMethod
  {
  	private boolean _active = false;
  
  	/**
  	 * Preview the algorithm by counting the number of points deleted
  	 * @param inTrack track object
  	 * @param inDetails track details including span
  	 * @param inMarkings information about deletion flags from previous algorithms
  	 * @return number of points to be deleted by this algorithm
  	 */
  	public int preview(Track inTrack, TrackDetails inDetails, MarkingData inMarkings)
  	{
  		if (!_active) {
  			return 0;
  		}
  		TrackDetails modifiedDetails = inDetails.modifyUsingMarkings(inMarkings);
  		return compress(inTrack, modifiedDetails, inMarkings);
  	}
  
  	public void setActive(boolean inActive) {
  		_active = inActive;
  	}
  
  	public boolean isActive() {
  		return _active;
  	}
  
  	/**
  	 * Perform the compression and set the results in the given array
  	 * @param inTrack track object
  	 * @param inDetails track details including span
  	 * @param inMarkings information about deletion flags from previous algorithms
  	 * @return number of points deleted by this algorithm
  	 */
  	public abstract int compress(Track inTrack, TrackDetails inDetails, MarkingData inMarkings);
  
  	/**
  	 * @return String to save in Settings
  	 */
  	public String getTotalSettingsString() {
  		return (_active ? "x" : "o") + getSettingsString();
  	}
  
  	protected abstract String getSettingsString();
  
  	public abstract CompressionMethodType getType();
  
  	public abstract String getParam();
  
  	/** Construct a specific CompressionMethod from a string fragment */
  	public static CompressionMethod fromSettingsString(String inString)
  	{
  		if (inString == null || inString.isEmpty()) {
  			return null;
  		}
  		CompressionMethod method = fromSettingsSubstring(inString.substring(1));
  		if (method != null) {
  			method.setActive(inString.charAt(0) == 'x');
  		}
  		return method;
  	}
  
  	private static CompressionMethod fromSettingsSubstring(String inString)
  	{
  		if (inString == null || inString.length() < 3) {
  			return null;
  		}
  		if (DuplicatesMethod.recogniseString(inString)) {
  			return new DuplicatesMethod();
  		}
  		if (NearbyFactorMethod.recogniseString(inString)) {
  			return new NearbyFactorMethod(inString);
  		}
  		if (WackyPointsMethod.recogniseString(inString)) {
  			return new WackyPointsMethod(inString);
  		}
  		if (SingletonsMethod.recogniseString(inString)) {
  			return new SingletonsMethod(inString);
  		}
  		if (DouglasPeuckerMethod.recogniseString(inString)) {
  			return new DouglasPeuckerMethod(inString);
  		}
  		if (NearbyDistMethod.recogniseString(inString)) {
  			return new NearbyDistMethod(inString);
  		}
  		if (TooSlowMethod.recogniseString(inString)) {
  			return new TooSlowMethod(inString);
  		}
  		if (TooFastMethod.recogniseString(inString)) {
  			return new TooFastMethod(inString);
  		}
  		if (TooSoonMethod.recogniseString(inString)) {
  			return new TooSoonMethod(inString);
  		}
  		if (SkiLiftsMethod.recogniseString(inString)) {
  			return new SkiLiftsMethod();
  		}
  		return null;
  	}
  
  	protected static boolean recogniseString(String inString, CompressionMethodType inType) {
  		return inString != null && inString.startsWith(inType.getKey());
  	}
  
  	public static boolean isPointDeleted(int inIndex, MarkingData inMarkings) {
  		return inMarkings != null && inMarkings.isPointMarkedForDeletion(inIndex);
  	}
  
  	public static boolean isPointAtSegmentBoundary(int inIndex, TrackDetails inDetails, MarkingData inMarkings)
  	{
  		if (inDetails != null && (inDetails.isSegmentStart(inIndex) || inDetails.isSegmentEnd(inIndex))) {
  			return true;
  		}
  		// Maybe it wasn't a segment boundary before but due to the deletion flags it now is
  		for (int i=inIndex - 1; i >= 0; i--)
  		{
  			if (isPointDeleted(i, inMarkings))
  			{
  				if (inMarkings.isPointMarkedForSegmentBreak(i)) {
  					return true;
  				}
  			}
  			else if (!inDetails.isWaypoint(i))
  			{
  				// Found a track point before our point, so our point isn't the start
  				return false;
  			}
  		}
  		// Couldn't find any points
  		return true;
  	}
  
  	protected static DataPoint getNextTrackPoint(Track inTrack, int inIndex, MarkingData inMarkings)
  	{
  		int index = inIndex + 1;
  		while (index < inTrack.getNumPoints())
  		{
  			DataPoint point = inTrack.getPoint(index);
  			if (!point.isWaypoint() && !inMarkings.isPointMarkedForDeletion(index)) {
  				return point;
  			}
  			index++;
  		}
  		return null;
  	}
  }
  