package org.optaplanner.examples.vehiclerouting.domain.timewindowed.solver;

import org.optaplanner.core.impl.domain.variable.listener.PlanningVariableListener;
import org.optaplanner.core.impl.score.director.ScoreDirector;
import org.optaplanner.examples.vehiclerouting.domain.VrpCustomer;
import org.optaplanner.examples.vehiclerouting.domain.VrpStandstill;
import org.optaplanner.examples.vehiclerouting.domain.VrpVehicle;
import org.optaplanner.examples.vehiclerouting.domain.timewindowed.VrpTimeWindowedCustomer;

public class ArrivalTimeUpdatingVariableListener implements PlanningVariableListener<VrpTimeWindowedCustomer> {

    public void beforeEntityAdded(ScoreDirector scoreDirector, VrpTimeWindowedCustomer customer) {
        // Do nothing
    }

    public void afterEntityAdded(ScoreDirector scoreDirector, VrpTimeWindowedCustomer customer) {
        updateVehicle(scoreDirector, customer);
    }

    public void beforeVariableChanged(ScoreDirector scoreDirector, VrpTimeWindowedCustomer customer) {
        // Do nothing
    }

    public void afterVariableChanged(ScoreDirector scoreDirector, VrpTimeWindowedCustomer customer) {
        updateVehicle(scoreDirector, customer);
    }

    public void beforeEntityRemoved(ScoreDirector scoreDirector, VrpTimeWindowedCustomer customer) {
        // Do nothing
    }

    public void afterEntityRemoved(ScoreDirector scoreDirector, VrpTimeWindowedCustomer customer) {
        // Do nothing
    }

    protected void updateVehicle(ScoreDirector scoreDirector, VrpTimeWindowedCustomer sourceCustomer) {
        VrpStandstill previousStandstill = sourceCustomer.getPreviousStandstill();
        int departureTime = (previousStandstill instanceof VrpTimeWindowedCustomer)
                ? ((VrpTimeWindowedCustomer) previousStandstill).getDepartureTime() : 0;
        VrpTimeWindowedCustomer shadowCustomer = sourceCustomer;
        int arrivalTime = calculateArrivalTime(shadowCustomer, departureTime);
        while (shadowCustomer != null && shadowCustomer.getArrivalTime() != arrivalTime) {
            scoreDirector.beforeVariableChanged(shadowCustomer, "arrivalTime");
            shadowCustomer.setArrivalTime(arrivalTime);
            scoreDirector.afterVariableChanged(shadowCustomer, "arrivalTime");
            departureTime = shadowCustomer.getDepartureTime();
            shadowCustomer = shadowCustomer.getNextCustomer();
            arrivalTime = calculateArrivalTime(shadowCustomer, departureTime);
        }
    }

    private int calculateArrivalTime(VrpTimeWindowedCustomer customer, int previousDepartureTime) {
        if (customer == null) {
            return -1;
        }
        int arrivalTime = previousDepartureTime + customer.getDistanceToPreviousStandstill();
        if (arrivalTime < customer.getReadyTime()) {
            arrivalTime = customer.getReadyTime();
        }
        return arrivalTime;
    }

}

@@ -1649,19 +1649,23 @@
         
-i
+I
 nt
+eger
  departu
@@ -1883,35 +1883,39 @@
 stomer;%0A        
-i
+I
 nt
+eger
  arrivalTime = c
@@ -2007,16 +2007,17 @@
 null && 
+!
 shadowCu
@@ -2039,20 +2039,24 @@
 alTime()
- != 
+.equals(
 arrivalT
@@ -2055,24 +2055,25 @@
 arrivalTime)
+)
  %7B%0A         

fix NPE