package org.matsim.amodeus.dvrp.activity;

import java.util.Map;

import org.matsim.api.core.v01.Id;
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;
import org.matsim.contrib.dvrp.optimizer.Request;
import org.matsim.contrib.dvrp.passenger.PassengerEngine;
import org.matsim.contrib.dvrp.passenger.PassengerPickupActivity;
import org.matsim.contrib.dvrp.passenger.PassengerRequest;
import org.matsim.contrib.dynagent.DynAgent;
import org.matsim.contrib.dynagent.FirstLastSimStepDynActivity;
import org.matsim.core.mobsim.framework.MobsimPassengerAgent;

/** In theory, we could use MultiPassengerPickupActivity from DVRP here. However,
 * that class only either waits until all passengers have entered or until a
 * predefined end time. Here, entering actually takes a certain amount of time
 * *per passenger*. Need to check whether we can move this into DVRP. */
public class AmodeusPickupActivity extends FirstLastSimStepDynActivity implements PassengerPickupActivity {
    public static final String ACTIVITY_TYPE = "pickup";

    private final PassengerEngine passengerEngine;
    private final DynAgent driver;

    private final Map<Id<Request>, PassengerRequest> requests;
    private final double durationPerPassenger;

    private final double expectedEndTime;
    private double passengerEndTime = Double.NEGATIVE_INFINITY;

    private int insidePassengers = 0;

    public AmodeusPickupActivity(PassengerEngine passengerEngine, DynAgent driver, DvrpVehicle vehicle, Map<Id<Request>, PassengerRequest> requests, double expectedEndTime,
            double durationPerPassenger) {
        super(ACTIVITY_TYPE);

        this.expectedEndTime = expectedEndTime;
        this.durationPerPassenger = durationPerPassenger;

        this.passengerEngine = passengerEngine;
        this.driver = driver;

        this.requests = requests;

        if (requests.size() > vehicle.getCapacity()) {
            throw new IllegalStateException("Number of requests exceeds number of seats");
        }
    }

    @Override
    protected void beforeFirstStep(double now) {
        for (PassengerRequest request : requests.values()) {
            tryPerformPickup(request, now);
        }
    }

    private boolean tryPerformPickup(PassengerRequest request, double now) {
        if (passengerEngine.pickUpPassenger(this, driver, request, now)) {
            insidePassengers++;
            passengerEndTime = Math.max(passengerEndTime, now) + durationPerPassenger;
            return true;
        }

        return false;
    }

    @Override
    public boolean isLastStep(double now) {
        return now >= expectedEndTime && now >= passengerEndTime && insidePassengers == requests.size();
    }

    private PassengerRequest getRequestForPassenger(MobsimPassengerAgent passenger) {
        for (PassengerRequest request : requests.values()) {
            if (passenger.getId().equals(request.getPassengerId())) {
                return request;
            }
        }

        return null;
    }

    @Override
    public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) {
        PassengerRequest request = getRequestForPassenger(passenger);

        if (request == null) {
            throw new IllegalArgumentException("I am waiting for different passengers!");
        }

        if (!tryPerformPickup(request, now)) {
            throw new IllegalStateException();
        }
    }
}