package tim.prune.data;
import tim.prune.DataSubscriber;
import tim.prune.UpdateMessageBroker;
/**
* Class to represent a selected portion of a Track
* and its properties
*/
public class Selection
{
private Track _track = null;
private int _currentPoint = -1;
private boolean _valid = false;
private int _prevNumPoints = 0;
private int _startIndex = -1, _endIndex = -1;
private int _currentPhotoIndex = -1;
private int _currentAudioIndex = -1;
private AltitudeRange _altitudeRange = null;
private long _movingMilliseconds = 0L;
private double _angMovingDistance = -1.0;
/**
* Constructor
* @param inTrack track object
*/
public Selection(Track inTrack)
{
_track = inTrack;
}
/**
* Mark selection invalid so it will be recalculated
*/
public void markInvalid()
{
_valid = false;
}
/**
* @return the current point index
*/
public int getCurrentPointIndex()
{
return _currentPoint;
}
/**
* @return true if range is selected
*/
public boolean hasRangeSelected()
{
return _startIndex >= 0 && _endIndex > _startIndex;
}
/**
* Recalculate all selection details
*/
private void recalculate()
{
final int numPoints = _track.getNumPoints();
// Recheck if the number of points has changed
if (numPoints != _prevNumPoints)
{
_prevNumPoints = numPoints;
check();
}
if (numPoints > 0 && hasRangeSelected())
{
_altitudeRange = new AltitudeRange();
Altitude altitude = null;
Timestamp time = null, previousTime = null;
DataPoint lastPoint = null, currPoint = null;
_angMovingDistance = 0.0;
_movingMilliseconds = 0L;
// Loop over points in selection
for (int i=_startIndex; i<=_endIndex; i++)
{
currPoint = _track.getPoint(i);
altitude = currPoint.getAltitude();
// Ignore waypoints in altitude calculations
if (!currPoint.isWaypoint() && altitude.isValid())
{
if (currPoint.getSegmentStart()) {
_altitudeRange.ignoreValue(altitude);
}
else {
_altitudeRange.addValue(altitude);
}
}
// Compare timestamps within the segments
time = currPoint.getTimestamp();
if (time.isValid())
{
// add moving time
if (!currPoint.getSegmentStart() && previousTime != null && time.isAfter(previousTime)) {
_movingMilliseconds += time.getMillisecondsSince(previousTime);
}
previousTime = time;
}
// Calculate distances, again ignoring waypoints
if (!currPoint.isWaypoint())
{
if (lastPoint != null)
{
double radians = DataPoint.calculateRadiansBetween(lastPoint, currPoint);
if (!currPoint.getSegmentStart()) {
_angMovingDistance += radians;
}
}
lastPoint = currPoint;
}
}
}
_valid = true;
}
/**
* @return start index
*/
public int getStart()
{
if (!_valid) recalculate();
return _startIndex;
}
/**
* @return end index
*/
public int getEnd()
{
if (!_valid) recalculate();
return _endIndex;
}
/**
* @return altitude range
*/
public AltitudeRange getAltitudeRange()
{
if (!_valid) recalculate();
return _altitudeRange;
}
/**
* @return number of seconds spanned by segments within selection
*/
public long getMovingSeconds()
{
if (!_valid) recalculate();
return _movingMilliseconds / 1000L;
}
/**
* @return moving distance of Selection in current units
*/
public double getMovingDistance()
{
return Distance.convertRadiansToDistance(_angMovingDistance);
}
/**
* Clear selected point, range, photo and audio
*/
public void clearAll()
{
_currentPoint = -1;
selectRange(-1, -1);
_currentPhotoIndex = -1;
_currentAudioIndex = -1;
check();
}
/**
* Select range from start to end
* @param inStartIndex index of start of range
* @param inEndIndex index of end of range
*/
public void selectRange(int inStartIndex, int inEndIndex)
{
_startIndex = inStartIndex;
_endIndex = inEndIndex;
markInvalid();
check();
}
/**
* Select the range from the current point
*/
public void selectRangeStart()
{
selectRangeStart(_currentPoint);
}
/**
* Set the index for the start of the range selection
* @param inStartIndex start index
*/
private void selectRangeStart(int inStartIndex)
{
if (inStartIndex < 0)
{
_startIndex = _endIndex = -1;
}
else
{
_startIndex = inStartIndex;
// Move end of selection to max if necessary
if (_endIndex <= _startIndex)
{
_endIndex = _track.getNumPoints() - 1;
}
}
markInvalid();
UpdateMessageBroker.informSubscribers();
}
/**
* Select the range up to the current point
*/
public void selectRangeEnd()
{
selectRangeEnd(_currentPoint);
}
/**
* Set the index for the end of the range selection
* @param inEndIndex end index
*/
public void selectRangeEnd(int inEndIndex)
{
if (inEndIndex < 0)
{
_startIndex = _endIndex = -1;
}
else
{
_endIndex = inEndIndex;
// Move start of selection to min if necessary
if (_startIndex > _endIndex || _startIndex < 0) {
_startIndex = 0;
}
}
markInvalid();
UpdateMessageBroker.informSubscribers();
}
/**
* Modify the selection given that the selected range has been deleted
*/
public void modifyRangeDeleted()
{
// Modify current point, if any
if (_currentPoint > _endIndex)
{
_currentPoint -= (_endIndex - _startIndex);
}
else if (_currentPoint > _startIndex)
{
_currentPoint = _startIndex;
}
// Clear selected range
_startIndex = _endIndex = -1;
// Check for consistency and fire update
check();
}
/**
* Modify the selection when a point is deleted
*/
public void modifyPointDeleted()
{
// current point index doesn't change, just gets checked
// range needs to get altered if deleted point is inside or before
if (hasRangeSelected() && _currentPoint <= _endIndex)
{
_endIndex--;
if (_currentPoint < _startIndex)
_startIndex--;
markInvalid();
}
check();
}
/**
* Select the specified photo and point
* @param inPointIndex index of selected point
* @param inPhotoIndex index of selected photo in PhotoList
* @param inAudioIndex index of selected audio item
*/
public void selectPointPhotoAudio(int inPointIndex, int inPhotoIndex, int inAudioIndex)
{
_currentPoint = inPointIndex;
_currentPhotoIndex = inPhotoIndex;
_currentAudioIndex = inAudioIndex;
check();
}
/**
* @return currently selected photo index
*/
public int getCurrentPhotoIndex()
{
return _currentPhotoIndex;
}
/**
* @return currently selected audio index
*/
public int getCurrentAudioIndex()
{
return _currentAudioIndex;
}
/**
* Check that the selection still makes sense
* and fire update message to listeners
*/
private void check()
{
if (_track != null && _track.getNumPoints() > 0)
{
int maxIndex = _track.getNumPoints() - 1;
if (_currentPoint > maxIndex)
{
_currentPoint = maxIndex;
}
if (_endIndex > maxIndex)
{
_endIndex = maxIndex;
}
if (_startIndex > maxIndex)
{
_startIndex = maxIndex;
}
}
else
{
// track is empty, clear selections
_currentPoint = _startIndex = _endIndex = -1;
}
UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
}
}