package tim.prune.correlate;

import tim.prune.data.DataPoint;
import tim.prune.data.MediaObject;

/**
 * Class to hold a pair of points used to hold the result of correlation
 */
public class PointMediaPair
{
	private final MediaObject _media;
	private DataPoint _pointBefore = null;
	private DataPoint _pointAfter = null;
	private long _secondsBefore = 1L;
	private long _secondsAfter = -1L;


	/**
	 * Constructor
	 * @param inMedia media object
	 */
	public PointMediaPair(MediaObject inMedia) {
		_media = inMedia;
	}

	/**
	 * Add a point to the pair
	 * @param inPoint data point
	 * @param inSeconds number of seconds time difference, positive means point later
	 */
	public void addPoint(DataPoint inPoint, long inSeconds)
	{
		// Check if point is closest point before
		if (inSeconds <= 0)
		{
			// point stamp is before media stamp
			if (inSeconds > _secondsBefore || _secondsBefore > 0L)
			{
				// point stamp is nearer to media
				_pointBefore = inPoint;
				_secondsBefore = inSeconds;
			}
		}
		// Check if point is closest point after
		if (inSeconds >= 0)
		{
			// point stamp is after media stamp
			if (inSeconds < _secondsAfter || _secondsAfter < 0L)
			{
				// point stamp is nearer to media
				_pointAfter = inPoint;
				_secondsAfter = inSeconds;
			}
		}
	}


	/**
	 * @return Media object
	 */
	public MediaObject getMedia() {
		return _media;
	}

	/**
	 * @return the closest point before the media
	 */
	public DataPoint getPointBefore() {
		return _pointBefore;
	}

	/**
	 * @return number of seconds between media and subsequent point
	 */
	public long getSecondsBefore() {
		return _secondsBefore;
	}

	/**
	 * @return the closest point after the media
	 */
	public DataPoint getPointAfter() {
		return _pointAfter;
	}

	/**
	 * @return number of seconds between previous point and media
	 */
	public long getSecondsAfter() {
		return _secondsAfter;
	}

	/**
	 * @return true if both points found
	 */
	public boolean isValid() {
		return getPointBefore() != null && getPointAfter() != null;
	}

	/**
	 * @return the fraction of the distance along the interpolated line
	 */
	public double getFraction()
	{
		if (_secondsAfter == 0L) return 0.0;
		return (-_secondsBefore * 1.0 / (-_secondsBefore + _secondsAfter));
	}

	/**
	 * @return the number of seconds to the nearest point
	 */
	public long getMinSeconds() {
		return Math.min(_secondsAfter, -_secondsBefore);
	}

	/**
	 * @return angle from media to nearest point in radians
	 */
	public double getMinRadians()
	{
		double totalRadians = DataPoint.calculateRadiansBetween(_pointBefore, _pointAfter);
		double frac = getFraction();
		return totalRadians * Math.min(frac, 1-frac);
	}
}
