package tim.prune.function;
import java.util.Arrays;
import javax.swing.JOptionPane;
import tim.prune.App;
import tim.prune.I18nManager;
import tim.prune.data.Checker;
import tim.prune.data.DataPoint;
import tim.prune.data.Track;
import tim.prune.data.sort.SortMode;
import tim.prune.data.sort.WaypointComparer;
import tim.prune.undo.UndoRearrangeWaypoints;
/**
* Class to provide the function for rearranging waypoints
*/
public class RearrangeWaypointsFunction extends RearrangeFunction
{
/**
* Constructor
* @param inApp app object
*/
public RearrangeWaypointsFunction(App inApp)
{
super(inApp, true);
}
/** Get the name key */
public String getNameKey() {
return "function.rearrangewaypoints";
}
/** Get whether sorting by time is allowed or not */
protected boolean isSortByTimeAllowed() {
return Checker.haveWaypointsGotTimestamps(_app.getTrackInfo().getTrack());
}
/** Get the description key */
public String getDescriptionKey() {
return "dialog.rearrangewaypoints.desc";
}
/** Sort by name key */
protected String getSortNameKey() {
return "sortbyname";
}
/**
* Perform the rearrange and sort according to the radio buttons
*/
protected void finish()
{
Track track = _app.getTrackInfo().getTrack();
// Figure out what is required from the radio buttons
Rearrange rearrangeOption = getRearrangeOption();
SortMode sortOption = getSortMode();
UndoRearrangeWaypoints undo = new UndoRearrangeWaypoints(track);
boolean success = false;
if (rearrangeOption == Rearrange.TO_START || rearrangeOption == Rearrange.TO_END)
{
// Collect the waypoints to the start or end of the track
success = collectWaypoints(rearrangeOption, sortOption);
}
else
{
// Interleave the waypoints into track order
success = track.interleaveWaypoints();
}
if (success)
{
_app.getTrackInfo().getSelection().clearAll(); // clear selected point and range
_app.completeFunction(undo, I18nManager.getText("confirm.rearrangewaypoints"));
}
else
{
JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.rearrange.noop"),
I18nManager.getText("error.function.noop.title"), JOptionPane.WARNING_MESSAGE);
}
}
/**
* Do the collection and sorting of the waypoints
* @param inRearrangeOption beginning or end
* @param inSortOption optional sort criterion
* @return true on success
*/
private boolean collectWaypoints(Rearrange inRearrangeOption, SortMode inSortOption)
{
// Check for mixed data, numbers of waypoints & nons
int numWaypoints = 0, numNonWaypoints = 0;
boolean wayAfterNon = false, nonAfterWay = false;
Track track = _app.getTrackInfo().getTrack();
final int numPoints = track.getNumPoints();
DataPoint[] waypoints = new DataPoint[numPoints];
DataPoint[] nonWaypoints = new DataPoint[numPoints];
DataPoint point = null;
for (int i=0; i<numPoints; i++)
{
point = track.getPoint(i);
if (point.isWaypoint())
{
waypoints[numWaypoints] = point;
numWaypoints++;
wayAfterNon |= (numNonWaypoints > 0);
}
else
{
nonWaypoints[numNonWaypoints] = point;
numNonWaypoints++;
nonAfterWay |= (numWaypoints > 0);
}
}
// Exit if the data is already in the specified order
final boolean wpsToStart = (inRearrangeOption == Rearrange.TO_START);
final boolean doSort = (inSortOption != SortMode.DONT_SORT);
if (numWaypoints == 0 || numNonWaypoints == 0
|| (wpsToStart && !wayAfterNon && nonAfterWay && !doSort)
|| (!wpsToStart && wayAfterNon && !nonAfterWay && !doSort)
|| inRearrangeOption == Rearrange.TO_NEAREST)
{
return false;
}
// Note: it could still be that the rearrange and sort has no effect, but we don't know yet
// Make a copy of the waypoints array first so we can compare it with after the sort
DataPoint[] origWaypoints = new DataPoint[numPoints];
System.arraycopy(waypoints, 0, origWaypoints, 0, numPoints);
if (doSort && numWaypoints > 1)
{
// Sort the waypoints array
WaypointComparer comparer = new WaypointComparer(inSortOption);
Arrays.sort(waypoints, comparer);
final boolean sortDidNothing = areArraysSame(origWaypoints, waypoints);
if (sortDidNothing && (numNonWaypoints == 0
|| (wpsToStart && !wayAfterNon && nonAfterWay)
|| (!wpsToStart && wayAfterNon && !nonAfterWay)))
{
return false;
}
}
// Copy the arrays into an array in the specified order
DataPoint[] neworder = new DataPoint[numPoints];
if (wpsToStart)
{
System.arraycopy(waypoints, 0, neworder, 0, numWaypoints);
System.arraycopy(nonWaypoints, 0, neworder, numWaypoints, numNonWaypoints);
}
else
{
System.arraycopy(nonWaypoints, 0, neworder, 0, numNonWaypoints);
System.arraycopy(waypoints, 0, neworder, numNonWaypoints, numWaypoints);
}
// Give track the new point order
return track.replaceContents(neworder);
}
/**
* Compare two arrays of DataPoints and see if they're identical or not
* @param inOriginal original array of points
* @param inSorted array of points after sorting
* @return true if the two arrays have the same points in the same order
*/
private static boolean areArraysSame(DataPoint[] inOriginal, DataPoint[] inSorted)
{
if (inOriginal == null && inSorted == null) return true; // both null
if (inOriginal == null || inSorted == null) return false; // only one of them null
if (inOriginal.length != inSorted.length) return false;
// Loop over all points
for (int i=0; i<inOriginal.length; i++)
{
DataPoint origPoint = inOriginal[i];
DataPoint sortedPoint = inSorted[i];
if ((origPoint != null || sortedPoint != null)
&& (origPoint != sortedPoint))
{
return false; // points different
}
}
// Must be all the same
return true;
}
}