package com.graphhopper.jsprit.core.algorithm.recreate;

import com.graphhopper.jsprit.core.algorithm.recreate.listener.InsertionStartsListener;
import com.graphhopper.jsprit.core.algorithm.recreate.listener.JobInsertedListener;
import com.graphhopper.jsprit.core.algorithm.ruin.listener.RuinListener;
import com.graphhopper.jsprit.core.algorithm.state.StateManager;
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager;
import com.graphhopper.jsprit.core.problem.job.Break;
import com.graphhopper.jsprit.core.problem.job.Job;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;

/**
 * Created by schroeder on 07/04/16.
 */
public class BreakScheduling implements InsertionStartsListener,JobInsertedListener, RuinListener {

    private final static Logger logger = LogManager.getLogger();

    private final StateManager stateManager;

    private final BreakInsertionCalculator breakInsertionCalculator;

    private final EventListeners eventListeners;

    private Set<VehicleRoute> modifiedRoutes = new HashSet<VehicleRoute>();

    private boolean firstRuin = true;

    public BreakScheduling(VehicleRoutingProblem vrp, StateManager stateManager, ConstraintManager constraintManager) {
        this.stateManager = stateManager;
        this.breakInsertionCalculator = new BreakInsertionCalculator(vrp.getTransportCosts(),vrp.getActivityCosts(),new LocalActivityInsertionCostsCalculator(vrp.getTransportCosts(),vrp.getActivityCosts(),stateManager),constraintManager);
        this.breakInsertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory());
        eventListeners = new EventListeners();
    }

    @Override
    public void informJobInserted(Job job2insert, VehicleRoute inRoute, double additionalCosts, double additionalTime) {
        Break aBreak = inRoute.getVehicle().getBreak();
        if(aBreak != null){
            boolean removed = inRoute.getTourActivities().removeJob(aBreak);
            if(removed){
                logger.trace("ruin: {}", aBreak.getId());
                stateManager.removed(aBreak,inRoute);
                stateManager.reCalculateStates(inRoute);
            }
            if(inRoute.getEnd().getArrTime() > aBreak.getTimeWindow().getEnd()){
                InsertionData iData = breakInsertionCalculator.getInsertionData(inRoute, aBreak, inRoute.getVehicle(), inRoute.getDepartureTime(), inRoute.getDriver(), Double.MAX_VALUE);
                if(!(iData instanceof InsertionData.NoInsertionFound)){
                    logger.trace("insert: [jobId={}]{}", aBreak.getId(), iData);
                    for(Event e : iData.getEvents()){
                        eventListeners.inform(e);
                    }
                    stateManager.informJobInserted(aBreak,inRoute,0,0);
                }
            }
        }
    }

    @Override
    public void ruinStarts(Collection<VehicleRoute> routes) {

    }

    @Override
    public void ruinEnds(Collection<VehicleRoute> routes, Collection<Job> unassignedJobs) {
//        if(firstRuin){
//            firstRuin = false;
//            modifiedRoutes.clear();
//            modifiedRoutes.addAll(routes);
//        }
        for(VehicleRoute route : routes){
            Break aBreak = route.getVehicle().getBreak();
            boolean removed = route.getTourActivities().removeJob(aBreak);
            if(removed) logger.trace("ruin: {}", aBreak.getId());
        }
        List<Break> breaks = new ArrayList<Break>();
//        if(!modifiedRoutes.isEmpty()) {
            for (Job j : unassignedJobs) {
                if (j instanceof Break) {
                    breaks.add((Break) j);
                }
            }
//        }
        for(Break b : breaks){ unassignedJobs.remove(b); }
//        modifiedRoutes.clear();
    }

    @Override
    public void removed(Job job, VehicleRoute fromRoute) {
        if(fromRoute.getVehicle().getBreak() != null) modifiedRoutes.add(fromRoute);
    }

    @Override
    public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs) {
        for(VehicleRoute route : vehicleRoutes){
            Break aBreak = route.getVehicle().getBreak();
            if(aBreak != null && !route.getTourActivities().servesJob(aBreak)){
                if(route.getEnd().getArrTime() > aBreak.getTimeWindow().getEnd()){
                    InsertionData iData = breakInsertionCalculator.getInsertionData(route, aBreak, route.getVehicle(), route.getDepartureTime(), route.getDriver(), Double.MAX_VALUE);
                    if(!(iData instanceof InsertionData.NoInsertionFound)){
                        logger.trace("insert: [jobId={}]{}", aBreak.getId(), iData);
                        for(Event e : iData.getEvents()){
                            eventListeners.inform(e);
                        }
                        stateManager.informJobInserted(aBreak,route,0,0);
                    }
                }
            }
//            if(aBreak != null){
//                boolean removed = route.getTourActivities().removeJob(aBreak);
//                if(removed){
//                    logger.trace("ruin: {}", aBreak.getId());
//                    stateManager.removed(aBreak,route);
//                    stateManager.reCalculateStates(route);
//                }
//                if(route.getEnd().getArrTime() > aBreak.getTimeWindow().getEnd()){
//                    InsertionData iData = breakInsertionCalculator.getInsertionData(route, aBreak, route.getVehicle(), route.getDepartureTime(), route.getDriver(), Double.MAX_VALUE);
//                    if(!(iData instanceof InsertionData.NoInsertionFound)){
//                        logger.trace("insert: [jobId={}]{}", aBreak.getId(), iData);
//                        for(Event e : iData.getEvents()){
//                            eventListeners.inform(e);
//                        }
//                        stateManager.informJobInserted(aBreak,route,0,0);
//                    }
//                }
//            }
        }

    }
}

@@ -1242,47 +1242,8 @@
 );%0A%0A
-    private boolean firstRuin = true;%0A%0A
     
@@ -1354,24 +1354,24 @@
 tManager) %7B%0A
+
         this
@@ -3149,161 +3149,8 @@
 ) %7B%0A
-//        if(firstRuin)%7B%0A//            firstRuin = false;%0A//            modifiedRoutes.clear();%0A//            modifiedRoutes.addAll(routes);%0A//        %7D%0A
     
@@ -3453,54 +3453,8 @@
 ();%0A
-//        if(!modifiedRoutes.isEmpty()) %7B%0A    
     
@@ -3500,20 +3500,16 @@
         
-    
 if (j in
@@ -3522,28 +3522,24 @@
 of Break) %7B%0A
-    
             
@@ -3565,34 +3565,16 @@
 ak) j);%0A
-                %7D%0A
         
@@ -3571,34 +3571,32 @@
 ;%0A            %7D%0A
-//
         %7D%0A      
@@ -3581,32 +3581,32 @@
     %7D%0A        %7D%0A
+
         for(Brea
@@ -3652,42 +3652,8 @@
 ; %7D%0A
-//        modifiedRoutes.clear();%0A
     
@@ -4830,1072 +4830,8 @@
   %7D%0A
-//            if(aBreak != null)%7B%0A//                boolean removed = route.getTourActivities().removeJob(aBreak);%0A//                if(removed)%7B%0A//                    logger.trace(%22ruin: %7B%7D%22, aBreak.getId());%0A//                    stateManager.removed(aBreak,route);%0A//                    stateManager.reCalculateStates(route);%0A//                %7D%0A//                if(route.getEnd().getArrTime() %3E aBreak.getTimeWindow().getEnd())%7B%0A//                    InsertionData iData = breakInsertionCalculator.getInsertionData(route, aBreak, route.getVehicle(), route.getDepartureTime(), route.getDriver(), Double.MAX_VALUE);%0A//                    if(!(iData instanceof InsertionData.NoInsertionFound))%7B%0A//                        logger.trace(%22insert: %5BjobId=%7B%7D%5D%7B%7D%22, aBreak.getId(), iData);%0A//                        for(Event e : iData.getEvents())%7B%0A//                            eventListeners.inform(e);%0A//                        %7D%0A//                        stateManager.informJobInserted(aBreak,route,0,0);%0A//                    %7D%0A//                %7D%0A//            %7D%0A
     

remove redundant stuff