package de.setsoftware.reviewtool.tourrestructuring.onestop;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import de.setsoftware.reviewtool.base.Util;
import de.setsoftware.reviewtool.model.changestructure.IReviewElement;
import de.setsoftware.reviewtool.model.changestructure.Stop;
import de.setsoftware.reviewtool.model.changestructure.Tour;

/**
 * A helper class for restructuring the tours, containing the information like a tour,
 * but being mutable instead.
 */
class MutableTour implements IReviewElement {

    /**
     * Helper class containing data about a stop and its position in a (mutable) tour.
     */
    private static class StopInTour {

        private final MutableTour tour;
        private final int index;

        public StopInTour(MutableTour tour, int stopIndex) {
            this.tour = tour;
            this.index = stopIndex;
        }

        public void merge(Stop s) {
            this.tour.mergeStop(this.index, s);
        }

    }

    private final Set<String> descriptionParts;
    private final List<Stop> stops;
    private final boolean isVisible;

    public MutableTour(Tour t) {
        this.descriptionParts = new LinkedHashSet<>();
        this.descriptionParts.add(t.getDescription());
        this.stops = new ArrayList<>(t.getStops());
        this.isVisible = t.isVisible();
    }

    public static List<Tour> toTours(List<MutableTour> mutableTours) {
        final List<Tour> ret = new ArrayList<>();
        for (final MutableTour t : mutableTours) {
            ret.add(new Tour(
                    Util.implode(t.descriptionParts, " + "),
                    t.stops,
                    t.isVisible()));
        }
        return ret;
    }

    public boolean canBeResolvedCompletely(List<MutableTour> mutableTours, int excludedIndex) {
        for (final Stop s : this.stops) {
            if (!this.canBeMerged(s, mutableTours, excludedIndex)) {
                return false;
            }
        }
        return true;
    }

    private boolean canBeMerged(Stop s, List<MutableTour> mutableTours, int excludedIndex) {
        return this.getStopToMergeWith(s, mutableTours, excludedIndex) != null;
    }

    public boolean resolve(List<MutableTour> mutableTours, int excludedIndex) {
        boolean didSomething = false;
        final Iterator<Stop> iter = this.stops.iterator();
        while (iter.hasNext()) {
            final Stop s = iter.next();
            final StopInTour toMergeWith = this.getStopToMergeWith(s, mutableTours, excludedIndex);
            if (toMergeWith != null) {
                toMergeWith.merge(s);
                toMergeWith.tour.descriptionParts.addAll(this.descriptionParts);
                iter.remove();
                didSomething = true;
            }
        }
        return didSomething;
    }

    private StopInTour getStopToMergeWith(Stop s, List<MutableTour> mutableTours, int excludedIndex) {
        for (int tourIndex = excludedIndex + 1; tourIndex < mutableTours.size(); tourIndex++) {
            final StopInTour mergeWith = mutableTours.get(tourIndex).getStopToMergeWith(s);
            if (mergeWith != null) {
                return mergeWith;
            }
        }
        for (int tourIndex = excludedIndex - 1; tourIndex >= 0; tourIndex--) {
            final StopInTour mergeWith = mutableTours.get(tourIndex).getStopToMergeWith(s);
            if (mergeWith != null) {
                return mergeWith;
            }
        }
        return null;
    }

    private StopInTour getStopToMergeWith(Stop s) {
        for (int stopIndex = 0; stopIndex < this.stops.size(); stopIndex++) {
            if (this.stops.get(stopIndex).canBeMergedWith(s)) {
                return new StopInTour(this, stopIndex);
            }
        }
        return null;
    }

    public void mergeStop(int index, Stop s) {
        this.stops.set(index, this.stops.get(index).merge(s));
    }

    public boolean isEmpty() {
        return this.stops.isEmpty();
    }

    @Override
    public boolean isVisible() {
        return this.isVisible;
    }
}

@@ -2984,32 +2984,82 @@
 xcludedIndex) %7B%0A
+        // try to merge with stops of later tours%0A
         for (int
@@ -3130,32 +3130,32 @@
  tourIndex++) %7B%0A
-
             fina
@@ -3317,32 +3317,379 @@
     %7D%0A        %7D%0A
+        // try to merge with stops in this tour%0A        %7B%0A            final StopInTour mergeWith = mutableTours.get(excludedIndex).getStopToMergeWith(s);%0A            if (mergeWith != null) %7B%0A                return mergeWith;%0A            %7D%0A        %7D%0A        // try to merge with stops of earlier tours (only necessary for resolving complete tours)%0A
         for (int
@@ -4108,20 +4108,34 @@
         
-if (
+final Stop stop = 
 this.sto
@@ -4151,16 +4151,57 @@
 opIndex)
+;%0A            if (!s.equals(stop) && stop
 .canBeMe

fix merging of stops