package org.opentripplanner.analyst.scenario;

import com.beust.jcommander.internal.Lists;
import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import org.onebusaway.gtfs.model.Stop;
import org.onebusaway.gtfs.model.StopTime;
import org.opentripplanner.model.StopPattern;
import org.opentripplanner.routing.edgetype.TripPattern;
import org.opentripplanner.routing.trippattern.Deduplicator;
import org.opentripplanner.routing.trippattern.FrequencyEntry;
import org.opentripplanner.routing.trippattern.TripTimes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * Skip stops and associated dwell times.
 *
 * Skipped stops are no longer served by the matched trips, and and dwell time at a skipped stop is removed from the schedule.
 * If stops are skipped at the start of a trip, the start of the trip is simply removed; the remaining times are not shifted.
 */
public class SkipStop extends TripPatternFilter {
    private static final Logger LOG = LoggerFactory.getLogger(SkipStop.class);

    /** Stops to skip. Note that setting this to null as a wildcard is not supported, obviously */
    public Collection<String> stopId;

    @Override
    public String getType() {
        return "skip-stop";
    }

    @Override
    public Collection<TripPattern> apply(TripPattern original) {
        if (!couldMatch(original))
            return Arrays.asList(original);

        // figure out which stops we skip
        TIntList skippedStops = new TIntArrayList();
        // retained stops
        // we use stop times to carry a little additional information, e.g. pickup/dropoff type.
        List<StopTime> stopTimes = Lists.newArrayList();

        {
            int i = 0;
            for (Stop stop : original.getStops()) {
                if (stopId.contains(stop.getId().getId()))
                    skippedStops.add(i);
                else {
                    // make a fake stop time
                    StopTime stopTime = new StopTime();
                    stopTime.setStop(stop);
                    stopTime.setPickupType(original.stopPattern.pickups[i]);
                    stopTime.setDropOffType(original.stopPattern.dropoffs[i]);
                    stopTimes.add(stopTime);
                }

                i++;
            }
        }

        if (skippedStops.isEmpty()) {
            LOG.warn("No stops found to skip on matched trip pattern {}", original);
            return Arrays.asList(original);
        }

        if (original.getStops().size() - skippedStops.size() < 2) {
            // TODO best way to handle this case?
            LOG.warn("Trip with skipped stops would have less that two stops for TripPattern {}, not skipping stops", original);
            return Arrays.asList(original);
        }

        // make the new stop pattern
        StopPattern sp = new StopPattern(stopTimes);
        TripPattern modified = new TripPattern(original.route, sp);

        // Any trips that are not matched keep the original trip pattern, so put them here.
        TripPattern originalClone = new TripPattern(original.route, original.stopPattern);

        // keep track of what we have to return
        boolean anyTripsMatched = false;
        boolean allTripsMatched = true;

        for (TripTimes tt : original.scheduledTimetable.tripTimes) {
            if (!matches(tt.trip)) {
                // this trip should not be modified
                allTripsMatched = false;
                originalClone.scheduledTimetable.addTripTimes(tt);
            }
            else {
                // This trip should be modified
                anyTripsMatched = true;
                modified.scheduledTimetable.addTripTimes(omitStops(tt, skippedStops.toArray()));
            }
        }

        for (FrequencyEntry fe : original.scheduledTimetable.frequencyEntries) {
            if (!matches(fe.tripTimes.trip)) {
                allTripsMatched = false;
                originalClone.scheduledTimetable.addFrequencyEntry(fe);
            }
            else {
                anyTripsMatched = true;
                TripTimes newtt = omitStops(fe.tripTimes, skippedStops.toArray());
                FrequencyEntry newfe = new FrequencyEntry(fe.startTime, fe.endTime, fe.headway, fe.exactTimes, newtt);
                modified.scheduledTimetable.addFrequencyEntry(newfe);
            }
        }

        if (!anyTripsMatched)
            return Arrays.asList(original);

        List<TripPattern> ret = Lists.newArrayList();

        ret.add(modified);

        if (!allTripsMatched)
            ret.add(originalClone);

        return ret;
    }

    public TripTimes omitStops (TripTimes tt, int... stopsToSkip) {
        TIntSet skipped = new TIntHashSet(stopsToSkip);

        List<StopTime> newSts = Lists.newArrayList();

        int cumulativeTime = -1;
        for (int i = 0; i < tt.getNumStops(); i++) {
            int hopTime = i != 0 ? tt.getArrivalTime(i) - tt.getDepartureTime(i - 1) : 0;
            int dwellTime = tt.getDepartureTime(i) - tt.getArrivalTime(i);

            // handle the first stop(s) being skipped
            if (cumulativeTime != -1)
                // note that we include hopTime before the check if the stop is included but dwell time after,
                // the assumption being that there is no dwell at a skipped stop.
                cumulativeTime += hopTime;

            if (skipped.contains(i))
                continue;

            // this stop is still part of the trip
            // if this stop is now the first stop, get the time
            if (cumulativeTime == -1)
                cumulativeTime = tt.getArrivalTime(i);

            StopTime stopTime = new StopTime();
            stopTime.setArrivalTime(cumulativeTime);
            cumulativeTime += dwellTime;
            stopTime.setDepartureTime(cumulativeTime);

            stopTime.setStopSequence(tt.getStopSequence(i));
            stopTime.setTimepoint(tt.isTimepoint(i) ? 1 : 0);
            newSts.add(stopTime);
        }

        TripTimes newtt = new TripTimes(tt.trip, newSts, new Deduplicator());
        newtt.serviceCode = tt.serviceCode;
        return newtt;
    }
}

@@ -55,33 +55,29 @@
 com.
-beust.jcommander.internal
+google.common.collect
 .Lis

remove incorrect import.