content: package tim.prune.function.sew;
  
  import tim.prune.data.Coordinate;
  import tim.prune.data.DataPoint;
  
  /**
   * Class to represent one end of a segment, including the
   * coordinates and the other end of the segment
   */
  public class SegmentEnd implements Comparable<SegmentEnd>
  {
  	private SegmentEnd _otherEnd = null;
  	private final Coordinate _longitude;
  	private final Coordinate _latitude;
  	private int        _pointIndex = 0;
  	private boolean    _active = true;
  
  
  	/**
  	 * Constructor
  	 * @param inPoint data point
  	 * @param inIndex point index within track
  	 */
  	public SegmentEnd(DataPoint inPoint, int inIndex)
  	{
  		_longitude = inPoint.getLongitude();
  		_latitude  = inPoint.getLatitude();
  		_pointIndex = inIndex;
  		_active    = true;
  	}
  
  	/**
  	 * @param inOther other end of the segment
  	 */
  	public void setOtherEnd(SegmentEnd inOther) {
  		_otherEnd = inOther;
  	}
  
  	/**
  	 * @return other end
  	 */
  	public SegmentEnd getOtherEnd() {
  		return _otherEnd;
  	}
  
  	/**
  	 * @return true if this is the start of the segment
  	 */
  	public boolean isStart() {
  		return _otherEnd == null || _otherEnd._pointIndex > _pointIndex;
  	}
  
  	/** @return point index */
  	public int getPointIndex() {
  		return _pointIndex;
  	}
  
  	/** @return point index of other end */
  	public int getOtherPointIndex() {
  		return _otherEnd == null ? _pointIndex : _otherEnd._pointIndex;
  	}
  
  	/** @return get the earlier of the two point indices */
  	public int getEarlierIndex() {
  		return isStart() ? _pointIndex : _otherEnd._pointIndex;
  	}
  
  	/** @return get the later of the two point indices */
  	public int getLaterIndex() {
  		return isStart() ? _otherEnd._pointIndex : _pointIndex;
  	}
  
  	/**
  	 * @return earlier end of this segment
  	 */
  	public SegmentEnd getEarlierEnd() {
  		return isStart() ? this : _otherEnd;
  	}
  
  	/**
  	 * @return later end of this segment
  	 */
  	public SegmentEnd getLaterEnd() {
  		return isStart() ? _otherEnd : this;
  	}
  
  	/**
  	 * Reverse this segment, by swapping the point indices of the start and end
  	 * isStart() will thereby also be reversed for both ends
  	 */
  	public void reverseSegment()
  	{
  		if (_otherEnd != null)
  		{
  			int pointIndex = _pointIndex;
  			_pointIndex = _otherEnd._pointIndex;
  			_otherEnd._pointIndex = pointIndex;
  		}
  	}
  
  	/**
  	 * @return true if this node is still active
  	 */
  	public boolean isActive() {
  		return _active;
  	}
  
  	/**
  	 * Deactivate this node, don't use it any more (it's already been merged)
  	 */
  	public void deactivate() {
  		_active = false;
  	}
  
  	/**
  	 * @param inOther other segment end
  	 * @return true if the coordinates are identical
  	 */
  	public boolean atSamePointAs(SegmentEnd inOther)
  	{
  		return inOther != null && _latitude.equals(inOther._latitude)
  				&& _longitude.equals(inOther._longitude);
  	}
  
  	/**
  	 * Compare two objects for sorting
  	 */
  	public int compareTo(SegmentEnd inOther)
  	{
  		if (inOther == null) {
  			return -1;
  		}
  		// First, sort by latitude
  		if (!_latitude.equals(inOther._latitude)) {
  			return (_latitude.getDouble() < inOther._latitude.getDouble() ? -1 : 1);
  		}
  		// Latitudes same, so sort by longitude
  		if (!_longitude.equals(inOther._longitude)) {
  			return (_longitude.getDouble() < inOther._longitude.getDouble() ? -1 : 1);
  		}
  		// Points are identical so just sort by index
  		return _pointIndex - inOther._pointIndex;
  	}
  
  	/** Compare two objects for equality */
  	public boolean equals(Object inOther)
  	{
  		if (!(inOther instanceof SegmentEnd)) {
  			return false;
  		}
  		SegmentEnd otherEnd = (SegmentEnd) inOther;
  		return _latitude.equals(otherEnd._latitude)
  				&& _longitude.equals(otherEnd._longitude)
  				&& _pointIndex == otherEnd._pointIndex;
  	}
  
  	/**
  	 * Adjust the point index as a result of a cut/move operation on the track
  	 * @param inSegmentStart index of start of segment to be moved
  	 * @param inSegmentEnd index of end of segment to be moved
  	 * @param inMoveTo index of point before which the segment should be moved
  	 */
  	public void adjustPointIndex(int inSegmentStart, int inSegmentEnd, int inMoveTo)
  	{
  		final int segmentSize = inSegmentEnd - inSegmentStart + 1; // number of points moved
  		final boolean forwardsMove = inMoveTo > inSegmentEnd;
  		// Min and max indices of affected points (apart from segment to be moved)
  		final int minIndex = forwardsMove ? inSegmentEnd + 1 : inMoveTo;
  		final int maxIndex = forwardsMove ? inMoveTo - 1 : inSegmentStart - 1;
  		if (_pointIndex >= minIndex && _pointIndex <= maxIndex)
  		{
  			if (forwardsMove) {
  				_pointIndex -= segmentSize; // segment moved forwards, point indices reduced
  			}
  			else {
  				_pointIndex += segmentSize; // segment moved backwards, point indices shifted forwards
  			}
  		}
  		else if (_pointIndex == inSegmentStart)
  		{
  			if (forwardsMove) {
  				_pointIndex = inMoveTo - segmentSize;
  			}
  			else
  			{
  				// Point index moves to moveTo
  				_pointIndex = inMoveTo;
  			}
  		}
  		else if (_pointIndex == inSegmentEnd)
  		{
  			if (forwardsMove) {
  				_pointIndex = inMoveTo - 1;
  			}
  			else
  			{
  				// Point index moves to moveTo
  				_pointIndex = inMoveTo + inSegmentEnd - inSegmentStart;
  			}
  		}
  	}
  }
  