package tim.prune.function;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JOptionPane;

import tim.prune.App;
import tim.prune.I18nManager;
import tim.prune.cmd.PointReference;
import tim.prune.cmd.RearrangePointsCmd;
import tim.prune.data.Checker;
import tim.prune.data.DataPoint;
import tim.prune.data.SortMode;
import tim.prune.data.Track;


/**
 * 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()
	{
		// Figure out what is required from the radio buttons
		Rearrange rearrangeOption = getRearrangeOption();
		SortMode sortOption = getSortMode();

		final List<PointReference> result;
		if (rearrangeOption == Rearrange.TO_START || rearrangeOption == Rearrange.TO_END)
		{
			// Collect the waypoints to the start or end of the track
			result = collectWaypoints(rearrangeOption, sortOption);
		}
		else
		{
			// Interleave the waypoints into track order
			result = interleaveWaypoints();
		}

		if (result == null || isResultANop(result)) {
			JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.rearrange.noop"),
					I18nManager.getText("error.function.noop.title"), JOptionPane.WARNING_MESSAGE);
		}
		else
		{
			RearrangePointsCmd command = RearrangePointsCmd.from(result);
			command.setDescription(getName());
			command.setConfirmText(I18nManager.getText("confirm.rearrangewaypoints"));
			_app.execute(command);
		}
	}


	/**
	 * Do the collection and sorting of the waypoints
	 * @param inRearrangeOption beginning or end
	 * @param inSortOption optional sort criterion
	 * @return list of point references
	 */
	private List<PointReference> collectWaypoints(Rearrange inRearrangeOption, SortMode inSortOption)
	{
		Track track = _app.getTrackInfo().getTrack();
		final int numPoints = track.getNumPoints();
		ArrayList<PointReference> waypoints = new ArrayList<>();
		ArrayList<PointReference> nonWaypoints = new ArrayList<>();
		boolean wayAfterNon = false, nonAfterWay = false;
		for (int i=0; i<numPoints; i++)
		{
			DataPoint point = track.getPoint(i);
			PointReference pointReference = new PointReference(i,
					inSortOption == SortMode.SORTBY_NAME ? point.getWaypointName() : null,
					inSortOption == SortMode.SORTBY_TIME ? point.getTimestamp() : null);
			if (point.isWaypoint())
			{
				waypoints.add(pointReference);
				wayAfterNon |= !nonWaypoints.isEmpty();
			}
			else
			{
				nonWaypoints.add(pointReference);
				nonAfterWay |= !waypoints.isEmpty();
			}
		}

		// 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 (waypoints.isEmpty()
			|| (wpsToStart && !wayAfterNon && nonAfterWay && !doSort)
			|| (!wpsToStart && wayAfterNon && !nonAfterWay && !doSort)
			|| inRearrangeOption == Rearrange.TO_NEAREST)
		{
			return null;
		}
		// Note: it could still be that the rearrange and sort has no effect, but we don't know yet

		if (doSort) {
			Collections.sort(waypoints);
		}

		// Combine the two lists into a single one
		List<PointReference> result = new ArrayList<>();
		if (wpsToStart)
		{
			result.addAll(waypoints);
			result.addAll(nonWaypoints);
		}
		else
		{
			result.addAll(nonWaypoints);
			result.addAll(waypoints);
		}
		return result;
	}

	/**
	 * Interleave all waypoints by each nearest track point
	 * @return list of point references
	 */
	private List<PointReference> interleaveWaypoints()
	{
		Track track = _app.getTrackInfo().getTrack();
		final int numPoints = track.getNumPoints();
		ArrayList<PointReference> result = new ArrayList<>();
		for (int i=0; i<numPoints; i++)
		{
			DataPoint point = track.getPoint(i);
			if (point.isWaypoint()) {
				result.add(new PointReference(i, track.getNearestTrackPointIndex(i)));
			}
			else {
				result.add(new PointReference(i, i));
		}
		}
		Collections.sort(result);
		return result;
	}
}
