ShuttleOutdoorDirectionsService.java
package com.soen390.backend.service;
import com.soen390.backend.enums.*;
import com.soen390.backend.object.*;
import org.springframework.stereotype.Service;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@Service
public class ShuttleOutdoorDirectionsService {
static final String MINUTES = " mins";
private final GoogleMapsService googleMapsService;
ShuttleOutdoorDirectionsService(GoogleMapsService googleMapsService) {
this.googleMapsService = googleMapsService;
}
public OutdoorDirectionResponse getShuttleOutdoorDirections(String origin, String destination, Campus destinationCampus) {
String loyolaCoords = String.format("%f,%f", CampusConstants.LOYOLA.getLatitude(), CampusConstants.LOYOLA.getLongitude());
String sgwCoords = String.format("%f,%f", CampusConstants.SGW.getLatitude(), CampusConstants.SGW.getLongitude());
String destShuttleCoords = (destinationCampus == Campus.LOYOLA) ? loyolaCoords : sgwCoords;
String originShuttleCoords = (destinationCampus == Campus.LOYOLA) ? sgwCoords : loyolaCoords;
OutdoorDirectionResponse walkToBus = googleMapsService.getDirections(origin, originShuttleCoords, TransportMode.WALKING);
OutdoorDirectionResponse shuttleLeg = googleMapsService.getDirections(originShuttleCoords, destShuttleCoords, TransportMode.TRANSIT);
OutdoorDirectionResponse walkToDest = googleMapsService.getDirections(destShuttleCoords, destination, TransportMode.WALKING);
int walkMins = (int) extractDouble(walkToBus.getDuration());
LocalTime arrivalAtStop = LocalTime.now(java.time.ZoneId.of("America/Montreal")).plusMinutes(walkMins);
String departureLocation = (destinationCampus == Campus.LOYOLA) ? "SGW" : "LOY";
String nextDeparture = findNextDeparture(arrivalAtStop, departureLocation);
int waitTime = 0;
String shuttleInstruction = "Take the Concordia Shuttle Bus";
if (nextDeparture != null) {
LocalTime departureTime = LocalTime.parse(nextDeparture);
waitTime = (int) java.time.Duration.between(arrivalAtStop, departureTime).toMinutes();
shuttleInstruction += " (Scheduled at " + nextDeparture + ")";
} else {
return null;
}
RouteStep manualShuttleStep = new RouteStep(
shuttleInstruction,
shuttleLeg.getDistance(),
shuttleLeg.getDuration(),
ManeuverType.SHUTTLE,
shuttleLeg.getPolyline()
);
List<RouteStep> allSteps = new ArrayList<>();
allSteps.addAll(walkToBus.getSteps());
if (waitTime > 0) {
allSteps.add(new RouteStep("Wait for shuttle", "0 km", waitTime + MINUTES, ManeuverType.STRAIGHT, ""));
}
allSteps.add(manualShuttleStep);
allSteps.addAll(walkToDest.getSteps());
String totalDistance = sumMetricStrings(walkToBus.getDistance(), shuttleLeg.getDistance(), walkToDest.getDistance(), "km");
double totalMins = extractDouble(walkToBus.getDuration()) + waitTime + extractDouble(shuttleLeg.getDuration()) + extractDouble(walkToDest.getDuration());
String totalDuration = (int) totalMins + MINUTES;
return new OutdoorDirectionResponse(totalDistance, totalDuration,
walkToBus.getPolyline() + shuttleLeg.getPolyline() + walkToDest.getPolyline(),
TransportMode.SHUTTLE, allSteps);
}
private String findNextDeparture(LocalTime arrivalTime, String location) {
DayOfWeek day = LocalDate.now().getDayOfWeek();
List<String> schedule;
// the switch case does not need break because it uses the modern arrow form (case ... ->)
switch (day) {
case DayOfWeek.FRIDAY ->
schedule = location.equals("SGW") ? ShuttleConstants.SGW_FRIDAY : ShuttleConstants.LOY_FRIDAY;
case DayOfWeek.SATURDAY, DayOfWeek.SUNDAY -> {
return null;
}
default ->
schedule = location.equals("SGW") ? ShuttleConstants.SGW_WEEKDAY : ShuttleConstants.LOY_WEEKDAY;
}
return schedule.stream()
.map(LocalTime::parse)
.filter(time -> time.isAfter(arrivalTime))
.map(time -> time.format(DateTimeFormatter.ofPattern("HH:mm")))
.findFirst()
.orElse(null);
}
private String sumMetricStrings(String s1, String s2, String s3, String unit) {
double total = extractDouble(s1) + extractDouble(s2) + extractDouble(s3);
return (unit.equals("km")) ? String.format("%.2f km", total) : (int) total + MINUTES;
}
private double extractDouble(String s) {
if (s == null) return 0;
try {
return Double.parseDouble(s.replaceAll("[^0-9.]", ""));
} catch (Exception e) {
return 0;
}
}
}