public VehicleRoutingSolution retrieveOrPrepareTripSheetData(
      TripSheet tripSheet, boolean create) {

    List<PickupPoint> pickUpPoints = getPickupPointDetails(tripSheet);
    Shift shift = shiftRepository.get(tripSheet.getShiftId());

    Address address = new Address();
    address.setId(0L);
    address.setAddressLine("Office");
    address.setLatitude(new BigDecimal(AppConstants.OFFICE_LATITUDE));
    address.setLongitude(new BigDecimal(AppConstants.OFFICE_LONGITUDE));

    PickupPoint pickUpPoint = new PickupPoint();
    pickUpPoint.setAddress(address);
    pickUpPoint.setNumberOfEmployees(0);
    pickUpPoints.add(0, pickUpPoint);
    tripSheet.setPickUpPoints(pickUpPoints);
    tripSheet.setShift(shift);

    // sort vehicles
    if (tripSheet.getVehicles() != null) {
      Collections.sort(tripSheet.getVehicles());
    }

    tripSheet.setDistanceMatrix(addressRepository.getAddressDistanceMatrix());
    VehicleRoutingSolution solution;
    if (create) {
      solution =
          solverManager.createSolution(
              tripSheet.getDate().toString() + tripSheet.getShiftId() + tripSheet.isDrop(),
              tripSheet);
    } else {
      solution =
          solverManager.retrieveOrCreateSolution(
              tripSheet.getDate().toString() + tripSheet.getShiftId() + tripSheet.isDrop(),
              tripSheet);
    }
    return solution;
  }
 public boolean solveRoute(TripSheet tripSheet) {
   return solverManager.solve(
       tripSheet.getDate().toString() + tripSheet.getShiftId() + tripSheet.isDrop());
 }
 public boolean terminateEarly(TripSheet tripSheet) {
   boolean success =
       solverManager.terminateEarly(
           tripSheet.getDate().toString() + tripSheet.getShiftId() + tripSheet.isDrop());
   return success;
 }
@Service
public class TripSheetServiceImpl implements TripSheetService {

  private static final NumberFormat NUMBER_FORMAT = new DecimalFormat("#,##0.00");

  private TripSheetRepository tripSheetRepository;

  private ShiftRepository shiftRepository;

  private TripRouteRepository tripRouteRepository;

  private AddressRepository addressRepository;

  private VehicleRoutingSolverManager solverManager = VehicleRoutingSolverManager.getInstance();

  @Autowired
  public TripSheetServiceImpl(
      TripSheetRepository tripSheetRepository,
      ShiftRepository shiftRepository,
      TripRouteRepository tripRouteRepository,
      AddressRepository addressRepository) {
    this.tripSheetRepository = tripSheetRepository;
    this.shiftRepository = shiftRepository;
    this.tripRouteRepository = tripRouteRepository;
    this.addressRepository = addressRepository;
  }

  public boolean solveRoute(TripSheet tripSheet) {
    return solverManager.solve(
        tripSheet.getDate().toString() + tripSheet.getShiftId() + tripSheet.isDrop());
  }

  public List<PickupPoint> getPickupPointDetails(TripSheet tripSheet) {
    return tripSheetRepository.getPickUpPointDetails(tripSheet);
  }

  public boolean terminateEarly(TripSheet tripSheet) {
    boolean success =
        solverManager.terminateEarly(
            tripSheet.getDate().toString() + tripSheet.getShiftId() + tripSheet.isDrop());
    return success;
  }

  public com.abcc.trobo.domain.Vehicle findCab(TripSheet tripSheet, Long empId) {
    String tripSheetId =
        tripSheet.getDate().toString() + tripSheet.getShiftId() + (tripSheet.isDrop() ? "D" : "P");
    return tripRouteRepository.findCab(tripSheetId, empId);
  }

  public VehicleRoutingSolution retrieveOrPrepareTripSheetData(
      TripSheet tripSheet, boolean create) {

    List<PickupPoint> pickUpPoints = getPickupPointDetails(tripSheet);
    Shift shift = shiftRepository.get(tripSheet.getShiftId());

    Address address = new Address();
    address.setId(0L);
    address.setAddressLine("Office");
    address.setLatitude(new BigDecimal(AppConstants.OFFICE_LATITUDE));
    address.setLongitude(new BigDecimal(AppConstants.OFFICE_LONGITUDE));

    PickupPoint pickUpPoint = new PickupPoint();
    pickUpPoint.setAddress(address);
    pickUpPoint.setNumberOfEmployees(0);
    pickUpPoints.add(0, pickUpPoint);
    tripSheet.setPickUpPoints(pickUpPoints);
    tripSheet.setShift(shift);

    // sort vehicles
    if (tripSheet.getVehicles() != null) {
      Collections.sort(tripSheet.getVehicles());
    }

    tripSheet.setDistanceMatrix(addressRepository.getAddressDistanceMatrix());
    VehicleRoutingSolution solution;
    if (create) {
      solution =
          solverManager.createSolution(
              tripSheet.getDate().toString() + tripSheet.getShiftId() + tripSheet.isDrop(),
              tripSheet);
    } else {
      solution =
          solverManager.retrieveOrCreateSolution(
              tripSheet.getDate().toString() + tripSheet.getShiftId() + tripSheet.isDrop(),
              tripSheet);
    }
    return solution;
  }

  public JsonVehicleRoutingSolution generateTripSheet(
      TripSheet tripSheet, VehicleRoutingSolution solution) {
    JsonVehicleRoutingSolution jsonSol = convertToJsonVehicleRoutingSolution(solution);
    jsonSol.setTripDate(tripSheet.getDate());
    jsonSol.setShiftId(tripSheet.getShiftId());
    if (tripSheet.isDrop()) {
      jsonSol.setTripType("D");
    } else {
      jsonSol.setTripType("P");
    }

    Map<Long, Queue<Employee>> map = new HashMap<Long, Queue<Employee>>();
    for (PickupPoint pickUpPoint : tripSheet.getPickUpPoints()) {
      List<Employee> employees =
          tripSheetRepository.getEmployeeDetails(tripSheet, pickUpPoint.getAddress());
      Queue<Employee> queue = new LinkedList<Employee>(employees);
      map.put(pickUpPoint.getAddress().getId(), queue);
    }

    // get and prepare distance matrix map
    //		Address[] addresses = new Address[tripSheet.getPickUpPoints().size()];
    //	   	 for(int i = 0; i < tripSheet.getPickUpPoints().size(); i++) {
    //	   		 Address address = new Address();
    //	   		 address.setLatitude(new
    // BigDecimal(tripSheet.getPickUpPoints().get(i).getAddress().getLatitude()+""));
    //	   		 address.setLongitude(new
    // BigDecimal(tripSheet.getPickUpPoints().get(i).getAddress().getLongitude()+""));
    //	   		 addresses[i] = address;
    //	   	 }
    //
    //	   	 GoogleMapsHelper helper = new GoogleMapsHelper();
    //	   	 DistanceMatrix matrix = helper.findDistanceMatrix(addresses, addresses);
    //	   	 Map<Long, Map<Long, DistanceMatrixElement>> distanceMatrix = new HashMap<Long, Map<Long,
    // DistanceMatrixElement>>();
    //	   	 for(int i = 0; i < tripSheet.getPickUpPoints().size(); i++) {
    //	   		Map<Long, DistanceMatrixElement> addrDistMatrix = new HashMap<Long,
    // DistanceMatrixElement>();
    //	   		for(int j = 0; j < tripSheet.getPickUpPoints().size(); j++) {
    //	   			DistanceMatrixElement dme = matrix.rows[i].elements[j];
    //	   			addrDistMatrix.put(tripSheet.getPickUpPoints().get(j).getAddress().getId(), dme);
    //	   		}
    //	   		distanceMatrix.put(tripSheet.getPickUpPoints().get(i).getAddress().getId(),
    // addrDistMatrix);
    //	   	 }

    Map<Long, com.abcc.trobo.domain.Vehicle> vehicles =
        new HashMap<Long, com.abcc.trobo.domain.Vehicle>();
    for (com.abcc.trobo.domain.Vehicle vehicle : tripSheet.getVehicles()) {
      vehicles.put(vehicle.getId(), vehicle);
    }

    Map<Long, Address> distanceMatrix = addressRepository.getAddressDistanceMatrix();

    Iterator<JsonVehicleRoute> iterator = jsonSol.getVehicleRouteList().iterator();
    while (iterator.hasNext()) {
      JsonVehicleRoute vehicleRoute = iterator.next();
      if (vehicleRoute.getCustomerList().size() == 0) {
        continue;
      }

      //			long timeInSecond = lastCustomerDistance.duration.inSeconds;
      long totalTime = 0;
      long customerId = 0L;

      for (int i = 0; i < vehicleRoute.getCustomerList().size(); i++) {
        if (distanceMatrix.get(customerId) == null
            || distanceMatrix
                    .get(customerId)
                    .getTimeToEachAddrMap()
                    .get(vehicleRoute.getCustomerList().get(i).getId())
                == null) {
          totalTime =
              distanceMatrix
                      .get(vehicleRoute.getCustomerList().get(i).getId())
                      .getTimeToEachAddrMap()
                      .get(customerId)
                  + totalTime;
        } else {
          totalTime =
              distanceMatrix
                      .get(customerId)
                      .getTimeToEachAddrMap()
                      .get(vehicleRoute.getCustomerList().get(i).getId())
                  + totalTime;
        }

        //				totalTime = distanceMatrix.get(customerId).get(customer.getId()).duration.inSeconds +
        // totalTime;
        customerId = vehicleRoute.getCustomerList().get(i).getId();

        // if time is more than 5 minutes then provide seperate cab and use next avilable cab
        long timeInSecond =
            distanceMatrix
                .get(vehicleRoute.getCustomerList().get(i).getId())
                .getTimeToEachAddrMap()
                .get(0L);
        if (totalTime - timeInSecond > 660) {
          List<JsonCustomer> sub =
              new ArrayList<JsonCustomer>(
                  vehicleRoute.getCustomerList().subList(i, vehicleRoute.getCustomerList().size()));
          boolean isReRouteSuccess = false;
          for (JsonVehicleRoute route : jsonSol.getVehicleRouteList()) {
            if (route.getCustomerList().size() == 0 && route.getCapacity() >= sub.size()) {
              isReRouteSuccess = true;
              // remove it from vehicle and add it to new vehicle
              vehicleRoute.getCustomerList().removeAll(sub);
              vehicleRoute.setDemandTotal(vehicleRoute.getCustomerList().size());

              route.getCustomerList().addAll(sub);
              route.setDemandTotal(sub.size());
              break;
            }
          }
          if (!isReRouteSuccess) {
            jsonSol.setIsNotAccurate(true);
            System.out.println("reroute is not success.");
          } else {
            System.out.println("reroute is success.");
          }
          System.out.println(timeInSecond + "-->" + totalTime);
          break;
        }

        //				calendar.add(Calendar.SECOND,
        // distanceMatrix.get(vehicleRoute.getCustomerList().get(i).getId()).getTimeToEachAddrMap().get(customerId).intValue());
        //				vehicleRoute.getCustomerList().get(i).setTime(sdf.format(calendar.getTime()));
      }
    }

    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
    for (JsonVehicleRoute vehicleRoute : jsonSol.getVehicleRouteList()) {
      //			List<Address> pickUpPoints =   new ArrayList<Address>();
      vehicleRoute.setVehicleNumber(vehicles.get(vehicleRoute.getId()).getVehicleNumber());

      if (vehicleRoute.getCustomerList().size() == 0) {
        continue;
      }

      long customerId = 0;

      Date date = null;
      try {
        if (tripSheet.isDrop()) {
          date = sdf.parse(tripSheet.getShift().getEndTime());
        } else {
          date = sdf.parse(tripSheet.getShift().getStartTime());
        }
      } catch (ParseException e) {
        e.printStackTrace();
      }

      Calendar calendar = GregorianCalendar.getInstance();
      calendar.setTime(date);

      // add or minus additional 15 mins
      if (tripSheet.isDrop()) {
        calendar.add(Calendar.SECOND, 900);
      } else {
        calendar.add(Calendar.SECOND, -900);
      }

      for (JsonCustomer customer : vehicleRoute.getCustomerList()) {
        Employee employee = map.get(customer.getId()).poll();

        List<Employee> employees = new ArrayList<Employee>();
        employees.add(employee);

        customer.setEmployees(employees);

        int seconds;
        if (distanceMatrix.get(customerId) == null
            || distanceMatrix.get(customerId).getTimeToEachAddrMap().get(customer.getId())
                == null) {
          seconds =
              distanceMatrix
                  .get(customer.getId())
                  .getTimeToEachAddrMap()
                  .get(customerId)
                  .intValue();
        } else {
          seconds =
              distanceMatrix
                  .get(customerId)
                  .getTimeToEachAddrMap()
                  .get(customer.getId())
                  .intValue();
        }

        if (tripSheet.isDrop()) {
          calendar.add(Calendar.SECOND, seconds);
        } else {
          calendar.add(Calendar.SECOND, -seconds);
        }
        customer.setTime(sdf.format(calendar.getTime()));
        customerId = customer.getId();

        //				Address address = new Address();
        //				address.setLatitude(new BigDecimal(customer.getLatitude()+ ""));
        //				address.setLongitude(new BigDecimal(customer.getLongitude() + ""));
        //				customer.setTime("??:??");
        //				pickUpPoints.add(address);
      }

      //			if(pickUpPoints.size() == 0) {
      //				continue;
      //			}

      // add escort
      JsonCustomer lastAddress =
          vehicleRoute.getCustomerList().get(vehicleRoute.getCustomerList().size() - 1);
      List<Employee> employees = lastAddress.getEmployees();
      boolean isFemaleEmp = true;
      for (Employee emp : employees) {
        if (("M").equalsIgnoreCase(emp.getSex())) {
          isFemaleEmp = false;
        }
      }

      if (isFemaleEmp && vehicleRoute.getCapacity() > vehicleRoute.getDemandTotal()) {
        Employee emp = new Employee();
        emp.setId(49L);
        emp.setName("ESCORT");
        emp.setSex("M");
        emp.setAddressId(lastAddress.getId());
        employees.add(emp);
      }

      //			Address officeAddress = new Address();
      //			officeAddress.setLatitude(new BigDecimal(AppConstants.OFFICE_LATITUDE));
      //			officeAddress.setLongitude(new BigDecimal(AppConstants.OFFICE_LONGITUDE));
      //
      //			if(tripSheet.isDrop()) {
      //				pickUpPoints.add(0,officeAddress);
      //			} else {
      //				pickUpPoints.add(1,officeAddress);
      //			}
      //
      //			GoogleMapsHelper helper = new GoogleMapsHelper();
      //
      //			Address[] wayPoints = new Address[pickUpPoints.size()];
      //
      //			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
      //			Date date = null;
      //			try {
      //				if(tripSheet.isDrop()) {
      //					date = sdf.parse(tripSheet.getShift().getEndTime());
      //				} else {
      //					date = sdf.parse(tripSheet.getShift().getStartTime());
      //				}
      //			} catch (ParseException e) {
      //				e.printStackTrace();
      //			}
      //			Calendar calendar = GregorianCalendar.getInstance(); // creates a new calendar instance
      //			calendar.setTime(date);   // assigns calendar to given date
      //			int hour = calendar.get(Calendar.HOUR_OF_DAY);
      //			int minute = calendar.get(Calendar.MINUTE);
      //
      //			Calendar calendar1 = GregorianCalendar.getInstance();
      //			calendar1.setTime(tripSheet.getDate());
      //
      //			DateTime time = new DateTime(calendar1.get(Calendar.YEAR),
      // calendar1.get(Calendar.MONTH)+1,
      //					calendar1.get(Calendar.DAY_OF_MONTH), hour, minute);
      //			DirectionsRoute route = helper.findOptimizedRoute(pickUpPoints.toArray(wayPoints), time,
      // !tripSheet.isDrop());
      //
      //			DirectionsLeg[] legs = route.legs;
      //
      //			if(!tripSheet.isDrop()) {
      //				long totalTimeInSeconds = 0;
      //				for (int i = 0; i < legs.length; i++) {
      //					totalTimeInSeconds = totalTimeInSeconds + legs[i].duration.inSeconds;
      //				}
      //				calendar.add(Calendar.SECOND, (int) -totalTimeInSeconds);
      //
      //				int count = 0;
      //				for (int i = 0; i < vehicleRoute.getCustomerList().size(); i++) {
      //					JsonCustomer customer = vehicleRoute.getCustomerList().get(i);
      //					customer.setTime(sdf.format(calendar.getTime()));
      //					long duration = legs[count++].duration.inSeconds;
      //					calendar.add(Calendar.SECOND, (int) duration);
      //				}
      //			} else {
      //				for(int i = vehicleRoute.getCustomerList().size() -1; i>=0; i--) {
      //					long duration = legs[i].duration.inSeconds;
      //					JsonCustomer customer = vehicleRoute.getCustomerList().get(i);
      //					calendar.add(Calendar.SECOND, (int) duration);
      //					customer.setTime(sdf.format(calendar.getTime()));
      //				}
      //			}
    }

    return jsonSol;
  }

  @Override
  public void saveTripSheet(JsonVehicleRoutingSolution tripSheet) {
    List<TripRoute> tripRoutes = TripSheetDataMapper.mapTripSheetToRoute(tripSheet);
    tripRouteRepository.save(tripRoutes);
  }

  @Override
  public boolean isTripSheetExist(TripSheet tripSheet) {
    List<TripRoute> tripRoutes =
        tripRouteRepository.get(
            tripSheet.getDate() + "" + tripSheet.getShiftId() + (tripSheet.isDrop() ? "D" : "P"));
    if (tripRoutes == null || tripRoutes.size() == 0) {
      return false;
    } else {
      return true;
    }
  }

  @Override
  public JsonVehicleRoutingSolution getTripSheet(TripSheet tripSheet) {
    List<TripRoute> tripRoutes =
        tripRouteRepository.get(
            tripSheet.getDate() + "" + tripSheet.getShiftId() + (tripSheet.isDrop() ? "D" : "P"));
    return TripSheetDataMapper.mapRoutesToTripSheet(tripRoutes, tripSheet);
  }

  @Override
  public List<TripRoute> getTripSheetDataForExport(TripSheet tripSheet) {
    List<TripRoute> tripRoutes =
        tripRouteRepository.get(
            tripSheet.getDate() + "" + tripSheet.getShiftId() + (tripSheet.isDrop() ? "D" : "P"));
    return tripRoutes;
  }

  public JsonVehicleRoutingSolution convertToJsonVehicleRoutingSolution(
      VehicleRoutingSolution solution) {
    JsonVehicleRoutingSolution jsonSolution = new JsonVehicleRoutingSolution();
    jsonSolution.setName(solution.getName());
    List<JsonCustomer> jsonCustomerList =
        new ArrayList<JsonCustomer>(solution.getCustomerList().size());
    for (Customer customer : solution.getCustomerList()) {
      Location customerLocation = customer.getLocation();
      jsonCustomerList.add(
          new JsonCustomer(
              customerLocation.getId(),
              customerLocation.getName(),
              customerLocation.getLatitude(),
              customerLocation.getLongitude(),
              customer.getDemand()));
    }
    jsonSolution.setCustomerList(jsonCustomerList);
    List<JsonVehicleRoute> jsonVehicleRouteList =
        new ArrayList<JsonVehicleRoute>(solution.getVehicleList().size());
    TangoColorFactory tangoColorFactory = new TangoColorFactory();
    for (Vehicle vehicle : solution.getVehicleList()) {
      JsonVehicleRoute jsonVehicleRoute = new JsonVehicleRoute();
      Location depotLocation = vehicle.getDepot().getLocation();
      jsonVehicleRoute.setId(vehicle.getId());
      jsonVehicleRoute.setDepotLocationName(depotLocation.getName());
      jsonVehicleRoute.setDepotLatitude(depotLocation.getLatitude());
      jsonVehicleRoute.setDepotLongitude(depotLocation.getLongitude());
      jsonVehicleRoute.setCapacity(vehicle.getCapacity());
      Color color = tangoColorFactory.pickColor(vehicle);
      jsonVehicleRoute.setHexColor(
          String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()));
      Customer customer = vehicle.getNextCustomer();
      int demandTotal = 0;
      List<JsonCustomer> jsonVehicleCustomerList = new ArrayList<JsonCustomer>();
      while (customer != null) {
        Location customerLocation = customer.getLocation();
        demandTotal += customer.getDemand();
        jsonVehicleCustomerList.add(
            new JsonCustomer(
                customerLocation.getId(),
                customerLocation.getName(),
                customerLocation.getLatitude(),
                customerLocation.getLongitude(),
                customer.getDemand()));
        customer = customer.getNextCustomer();
      }
      jsonVehicleRoute.setDemandTotal(demandTotal);
      jsonVehicleRoute.setCustomerList(jsonVehicleCustomerList);
      jsonVehicleRouteList.add(jsonVehicleRoute);
    }
    jsonSolution.setVehicleRouteList(jsonVehicleRouteList);
    HardSoftLongScore score = solution.getScore();
    jsonSolution.setFeasible(score != null && score.isFeasible());
    jsonSolution.setDistance(solution.getDistanceString(NUMBER_FORMAT));
    return jsonSolution;
  }
}