File indexing completed on 2025-02-02 05:02:34

0001 /*
0002     SPDX-FileCopyrightText: 2019-2023 Volker Krause <vkrause@kde.org>
0003     SPDX-License-Identifier: LGPL-2.0-or-later
0004 */
0005 
0006 #include "publictransportmatcher.h"
0007 #include "logging.h"
0008 #include "publictransport.h"
0009 #include "reservationhelper.h"
0010 
0011 #include <KItinerary/LocationUtil>
0012 #include <KItinerary/Reservation>
0013 #include <KItinerary/SortUtil>
0014 
0015 #include <KPublicTransport/Journey>
0016 #include <KPublicTransport/Stopover>
0017 
0018 bool PublicTransportMatcher::isSameMode(const QVariant &res, KPublicTransport::Line::Mode mode)
0019 {
0020     using namespace KPublicTransport;
0021 
0022     if (KItinerary::JsonLd::isA<KItinerary::TrainReservation>(res)) {
0023         return PublicTransport::isTrainMode(mode);
0024     } else if (KItinerary::JsonLd::isA<KItinerary::BusReservation>(res)) {
0025         return PublicTransport::isBusMode(mode);
0026     } else if (KItinerary::JsonLd::isA<KItinerary::FlightReservation>(res)) {
0027         return mode == Line::Air;
0028     } else if (KItinerary::JsonLd::isA<KItinerary::BoatReservation>(res)) {
0029         return mode == Line::Boat || mode == Line::Ferry;
0030     } else if (res.isValid()) {
0031         qCWarning(Log) << "unexpected reservation type!?" << res;
0032     }
0033 
0034     return false;
0035 }
0036 
0037 bool PublicTransportMatcher::isSameMode(const QVariant &res, const KPublicTransport::JourneySection &section)
0038 {
0039     return isSameMode(res, section.route().line().mode());
0040 }
0041 
0042 bool PublicTransportMatcher::isSameRoute(const KPublicTransport::Route &lhs, const QString &trainName, const QString &trainNumber)
0043 {
0044     KPublicTransport::Line rhs;
0045     rhs.setModeString(trainName);
0046     rhs.setName(trainNumber);
0047     if (KPublicTransport::Line::isSame(lhs.line(), rhs)) {
0048         return true;
0049     }
0050     if (lhs.name().size() >= 3) {
0051         rhs.setName(QString(trainName + QLatin1Char(' ') + trainNumber).trimmed());
0052         auto lhsLine = lhs.line();
0053         lhsLine.setModeString(QString());
0054         lhsLine.setName(lhs.name());
0055         return KPublicTransport::Line::isSame(lhsLine, rhs);
0056     }
0057     return false;
0058 }
0059 
0060 bool PublicTransportMatcher::isDepartureForReservation(const QVariant &res, const KPublicTransport::Stopover &dep)
0061 {
0062     const auto lineData = ReservationHelper::lineNameAndNumber(res);
0063     return PublicTransportMatcher::isSameMode(res, dep.route().line().mode())
0064         && KItinerary::SortUtil::startDateTime(res) == dep.scheduledDepartureTime()
0065         && PublicTransportMatcher::isSameRoute(dep.route(), lineData.first, lineData.second);
0066 }
0067 
0068 bool PublicTransportMatcher::isArrivalForReservation(const QVariant &res, const KPublicTransport::Stopover &arr)
0069 {
0070     const auto lineData = ReservationHelper::lineNameAndNumber(res);
0071     return PublicTransportMatcher::isSameMode(res, arr.route().line().mode())
0072         && KItinerary::SortUtil::endDateTime(res) == arr.scheduledArrivalTime()
0073         && PublicTransportMatcher::isSameRoute(arr.route(), lineData.first, lineData.second);
0074 }
0075 
0076 bool PublicTransportMatcher::isJourneyForReservation(const QVariant &res, const KPublicTransport::JourneySection &journey)
0077 {
0078     const auto lineData = ReservationHelper::lineNameAndNumber(res);
0079     return PublicTransportMatcher::isSameMode(res, journey.route().line().mode())
0080         && KItinerary::SortUtil::startDateTime(res) == journey.scheduledDepartureTime()
0081         && PublicTransportMatcher::isSameRoute(journey.route(), lineData.first, lineData.second);
0082 }
0083 
0084 /* Compare times without assuming times without a timezone are in the current time zone
0085  * (they might be local to the destination instead).
0086  */
0087 static bool isSameDateTime(const QDateTime &lhs, const QDateTime &rhs)
0088 {
0089     if (lhs == rhs) {
0090         return true;
0091     }
0092     if (lhs.timeSpec() == Qt::LocalTime && rhs.timeSpec() != Qt::LocalTime) {
0093         QDateTime dt(rhs);
0094         dt.setTimeSpec(Qt::LocalTime);
0095         return lhs == dt;
0096     }
0097     if (lhs.timeSpec() != Qt::LocalTime && rhs.timeSpec() == Qt::LocalTime) {
0098         QDateTime dt(lhs);
0099         dt.setTimeSpec(Qt::LocalTime);
0100         return dt == rhs;
0101     }
0102     return false;
0103 }
0104 
0105 static bool isSameDeparture(const QVariant &depLoc, const QVariant &res, const QDateTime &depTime, const KPublicTransport::Stopover &stop)
0106 {
0107     return isSameDateTime(depTime, stop.scheduledDepartureTime())
0108         && KPublicTransport::Location::isSame(PublicTransport::locationFromPlace(depLoc, res), stop.stopPoint());
0109 }
0110 
0111 static bool isSameArrival(const QVariant &arrLoc, const QVariant &res, const QDateTime &arrTime, const KPublicTransport::Stopover &stop)
0112 {
0113     return isSameDateTime(arrTime, stop.scheduledArrivalTime())
0114         && KPublicTransport::Location::isSame(PublicTransport::locationFromPlace(arrLoc, res), stop.stopPoint());
0115 }
0116 
0117 KPublicTransport::JourneySection PublicTransportMatcher::subJourneyForReservation(const QVariant &res, const KPublicTransport::JourneySection &journey)
0118 {
0119     const auto lineData = ReservationHelper::lineNameAndNumber(res);
0120     if (!PublicTransportMatcher::isSameMode(res, journey.route().line().mode()) ||
0121         !PublicTransportMatcher::isSameRoute(journey.route(), lineData.first, lineData.second)) {
0122         return {};
0123     }
0124 
0125     KPublicTransport::JourneySection result(journey);
0126     auto stopovers = result.takeIntermediateStops();
0127 
0128     auto it = stopovers.begin();
0129     if (!isSameDeparture(KItinerary::LocationUtil::departureLocation(res), res, KItinerary::SortUtil::startDateTime(res), journey.departure())) {
0130         for (;it != stopovers.end(); ++it) {
0131             if (isSameDeparture(KItinerary::LocationUtil::departureLocation(res), res, KItinerary::SortUtil::startDateTime(res), *it)) {
0132                 result.setDeparture(*it);
0133                 break;
0134             }
0135         }
0136     }
0137     if (it == stopovers.end()) {
0138         return {};
0139     }
0140     ++it;
0141     auto it2 = it;
0142     for (;it2 != stopovers.end(); ++it2) {
0143         if (isSameArrival(KItinerary::LocationUtil::arrivalLocation(res), res, KItinerary::SortUtil::endDateTime(res), *it2)) {
0144             result.setArrival(*it2);
0145             break;
0146         }
0147     }
0148     if (it2 == stopovers.end() && !isSameArrival(KItinerary::LocationUtil::arrivalLocation(res), res, KItinerary::SortUtil::endDateTime(res), journey.arrival())) {
0149         return {};
0150     }
0151 
0152     result.setIntermediateStops({it, it2});
0153     return result;
0154 }