package tim.prune.data;

/**
 * Class to hold the extents of a track, in 2d and in 3d,
 * and to calculate a square area with a default border
 */
public class TrackExtents
{
	/** Track object */
	private final Track _track;
	/** X and Y ranges */
	private final DoubleRange _xRange;
	private final DoubleRange _yRange;

	/** Border multiplier */
	private static final double BORDER_MULTIPLIER = 1.1; // 10% border

	/**
	 * Constructor
	 * @param inTrack track object to take extents from
	 */
	public TrackExtents(Track inTrack)
	{
		_track  = inTrack;
		_xRange = inTrack.getXRange().copy();
		_yRange = inTrack.getYRange().copy();
	}


	/**
	 * Make the x and y ranges square with a default border around
	 */
	public void applySquareBorder()
	{
		// Find the middle of the x and y
		final double midXvalue = _xRange.getMidValue();
		final double midYvalue = _yRange.getMidValue();
		// Find x and y range, take maximum
		double xyRange = Math.max(_xRange.getRange(), _yRange.getRange()) * BORDER_MULTIPLIER;
		if (getHorizontalDistanceMetres() < 10.0)
		{
			// all the points are near enough on the same spot, expand scale to avoid dividing by zero
			xyRange = 0.1;
		}

		// Apply these new min and max to the ranges
		_xRange.addValue(midXvalue - xyRange / 2.0);
		_xRange.addValue(midXvalue + xyRange / 2.0);
		_yRange.addValue(midYvalue - xyRange / 2.0);
		_yRange.addValue(midYvalue + xyRange / 2.0);
	}

	/** @return x range */
	public DoubleRange getXRange() {
		return _xRange;
	}

	/** @return y range */
	public DoubleRange getYRange() {
		return _yRange;
	}

	/** @return altitude range */
	public DoubleRange getAltitudeRange()
	{
		final int numPoints = _track.getNumPoints();
		DoubleRange altRange = new DoubleRange();
		for (int i=0; i<numPoints; i++)
		{
			DataPoint p = _track.getPoint(i);
			if (p != null && p.hasAltitude()) {
				altRange.addValue(p.getAltitude().getMetricValue());
			}
		}
		return altRange;
	}

	/**
	 * @return the greater of the N/S and E/W extent of the track, in metres (including border)
	 */
	public double getHorizontalDistanceMetres()
	{
		DoubleRange lonRange = _track.getLonRange();
		DoubleRange latRange = _track.getLatRange();

		// Find horizontal and vertical extents of enclosing rectangle
		DataPoint southPoint = new DataPoint(Latitude.make(latRange.getMinimum()),
			Longitude.make(lonRange.getMidValue()));
		DataPoint northPoint = new DataPoint(Latitude.make(latRange.getMaximum()),
			Longitude.make(lonRange.getMidValue()));
		double nsDist = Distance.convertRadiansToDistance(
			DataPoint.calculateRadiansBetween(northPoint, southPoint), UnitSetLibrary.UNITS_METRES); // both in m
		// Same again for bottom and top, take maximum
		DataPoint westPoint = new DataPoint(Latitude.make(latRange.getMidValue()),
			Longitude.make(lonRange.getMinimum()));
		DataPoint eastPoint = new DataPoint(Latitude.make(latRange.getMidValue()),
			Longitude.make(lonRange.getMaximum()));
		double ewDist = Distance.convertRadiansToDistance(
			DataPoint.calculateRadiansBetween(westPoint, eastPoint), UnitSetLibrary.UNITS_METRES); // both in m
		final double horizDistance = Math.max(nsDist, ewDist) * BORDER_MULTIPLIER;

		return horizDistance;
	}
}
