/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onebusaway.presentation.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.onebusaway.exceptions.NoSuchStopServiceException;
import org.onebusaway.presentation.client.RoutePresenter;
import org.onebusaway.presentation.services.DefaultSearchLocationService;
import org.onebusaway.transit_data.model.AgencyBean;
import org.onebusaway.transit_data.model.ArrivalAndDepartureBean;
import org.onebusaway.transit_data.model.ArrivalsAndDeparturesQueryBean;
import org.onebusaway.transit_data.model.RouteBean;
import org.onebusaway.transit_data.model.StopBean;
import org.onebusaway.transit_data.model.StopsWithArrivalsAndDeparturesBean;
import org.onebusaway.transit_data.model.trips.TripBean;
import org.onebusaway.transit_data.services.TransitDataService;
import org.onebusaway.users.client.model.UserBean;
import org.onebusaway.users.services.CurrentUserService;
import org.onebusaway.utility.text.NaturalStringOrder;
import org.onebusaway.utility.text.StringLibrary;
import org.springframework.beans.factory.annotation.Autowired;
public class ArrivalsAndDeparturesModel {
private static final OrderConstraint SORT_BY_TIME = new SortByTime();
private static final OrderConstraint SORT_BY_DEST = new SortByDestination();
private static final OrderConstraint SORT_BY_ROUTE = new SortByRoute();
private TransitDataService _transitDataService;
private CurrentUserService _currentUserService;
private DefaultSearchLocationService _defaultSearchLocationService;
private List<String> _stopIds;
private Set<String> _routeFilter = new HashSet<String>();
private OrderConstraint _order = SORT_BY_TIME;
private TimeZone _timeZone;
private ArrivalsAndDeparturesQueryBean _query = new ArrivalsAndDeparturesQueryBean();
protected StopsWithArrivalsAndDeparturesBean _result;
private List<AgencyBean> _agencies;
/**
* True if we filtered the results (ex. only include particular set of routes)
*/
private boolean _filtered = false;
protected UserBean _user;
private boolean _onlyNext = false;
private boolean _showArrivals = false;
private int _refresh = 60;
@Autowired
public void setTransitDataService(TransitDataService transitDataService) {
_transitDataService = transitDataService;
}
@Autowired
public void setCurrentUserService(CurrentUserService currentUserService) {
_currentUserService = currentUserService;
}
@Autowired
public void setDefaultSearchLocationService(
DefaultSearchLocationService defaultSearchLocationService) {
_defaultSearchLocationService = defaultSearchLocationService;
}
public void setStopIds(List<String> stopIds) {
_stopIds = stopIds;
}
public List<String> getStopIds() {
return _stopIds;
}
public void setRouteFilter(Set<String> routeFilter) {
_routeFilter = routeFilter;
}
public Set<String> getRouteFilter() {
return _routeFilter;
}
public boolean setOrderFromString(String order) {
if ("route".equals(order))
_order = SORT_BY_ROUTE;
else if ("dest".equals(order))
_order = SORT_BY_DEST;
else if ("time".equals(order))
_order = SORT_BY_TIME;
else
return false;
return true;
}
public void setTargetTime(Date time) {
_query.setTime(time.getTime());
}
public void setMinutesBefore(int minutesBefore) {
_query.setMinutesBefore(minutesBefore);
}
public void setMinutesAfter(int minutesAfter) {
_query.setMinutesAfter(minutesAfter);
}
public void setFrequencyMinutesBefore(int frequencyMinutesBefore) {
_query.setFrequencyMinutesBefore(frequencyMinutesBefore);
}
public void setFrequencyMinutesAfter(int frequencyMinutesAfter) {
_query.setFrequencyMinutesAfter(frequencyMinutesAfter);
}
public void setOnlyNext(boolean onlyNext) {
_onlyNext = onlyNext;
}
public void setShowArrivals(boolean showArrivals) {
_showArrivals = showArrivals;
}
public void setRefresh(int refresh) {
_refresh = refresh;
}
public int getRefresh() {
return _refresh;
}
public boolean isMissingData() {
return _stopIds == null || _stopIds.isEmpty();
}
public void process() {
_result = _transitDataService.getStopsWithArrivalsAndDepartures(_stopIds,
_query);
checkForEmptyResult();
filterResults();
orderResults();
_agencies = AgencyPresenter.getAgenciesForArrivalAndDepartures(_result.getArrivalsAndDepartures());
_timeZone = TimeZone.getTimeZone(_result.getTimeZone());
if (_timeZone == null)
_timeZone = TimeZone.getDefault();
updateCurrentUser();
}
public TimeZone getTimeZone() {
return _timeZone;
}
public StopsWithArrivalsAndDeparturesBean getResult() {
return _result;
}
public void setResult(StopsWithArrivalsAndDeparturesBean result) {
_result = result;
}
public boolean isFiltered() {
return _filtered;
}
public List<AgencyBean> getAgencies() {
return _agencies;
}
/****
* Private Methods
****/
private void checkForEmptyResult() {
if (_result == null) {
if (_stopIds.size() == 1)
throw new NoSuchStopServiceException(_stopIds.get(0));
else
throw new NoSuchStopServiceException(_stopIds.toString());
}
}
private void filterResults() {
applyRouteFilter();
applyArrivalsVsDeparturesFilter();
applyOnlyNextFilter();
}
private void applyRouteFilter() {
if (_routeFilter == null || _routeFilter.isEmpty())
return;
List<ArrivalAndDepartureBean> filtered = new ArrayList<ArrivalAndDepartureBean>();
for (ArrivalAndDepartureBean bean : _result.getArrivalsAndDepartures()) {
if (_routeFilter.contains(bean.getTrip().getRoute().getId()))
filtered.add(bean);
else
_filtered = true;
}
_result.setArrivalsAndDepartures(filtered);
}
private void applyArrivalsVsDeparturesFilter() {
List<ArrivalAndDepartureBean> filtered = new ArrayList<ArrivalAndDepartureBean>();
for (ArrivalAndDepartureBean bean : _result.getArrivalsAndDepartures()) {
if ((_showArrivals && bean.isArrivalEnabled())
|| (!_showArrivals && bean.isDepartureEnabled()))
filtered.add(bean);
}
_result.setArrivalsAndDepartures(filtered);
}
private void applyOnlyNextFilter() {
if (!_onlyNext)
return;
List<ArrivalAndDepartureBean> current = _result.getArrivalsAndDepartures();
Collections.sort(current, SORT_BY_TIME);
Map<String, ArrivalAndDepartureBean> keepers = new HashMap<String, ArrivalAndDepartureBean>();
for (ArrivalAndDepartureBean bean : current) {
String key = getRouteKeyForArrivalAndDeparture(bean);
if (!keepers.containsKey(key))
keepers.put(key, bean);
}
List<ArrivalAndDepartureBean> filtered = new ArrayList<ArrivalAndDepartureBean>(
keepers.values());
OrderConstraint c = _order == null ? SORT_BY_TIME : _order;
Collections.sort(filtered, c);
_result.setArrivalsAndDepartures(filtered);
}
private String getRouteKeyForArrivalAndDeparture(ArrivalAndDepartureBean bean) {
String name = bean.getRouteShortName();
if (name != null)
return name;
TripBean trip = bean.getTrip();
name = trip.getRouteShortName();
if (name != null)
return name;
RouteBean route = trip.getRoute();
name = route.getShortName();
if (name != null)
return name;
return route.getId();
}
private void orderResults() {
if (_order != null)
Collections.sort(_result.getArrivalsAndDepartures(), _order);
}
private void updateCurrentUser() {
// Save the last selected stop id
_currentUserService.setLastSelectedStopIds(_stopIds);
_user = _currentUserService.getCurrentUser();
if (_user == null || !_user.hasDefaultLocation()) {
List<StopBean> stops = _result.getStops();
StopBean stop = stops.get(0);
_defaultSearchLocationService.setDefaultLocationForCurrentUser(
stop.getName(), stop.getLat(), stop.getLon());
}
}
/****
*
****/
private interface OrderConstraint extends Comparator<ArrivalAndDepartureBean> {
// public void addTargetParams(Map<String, String> params);
}
private static class SortByRoute implements OrderConstraint {
public int compare(ArrivalAndDepartureBean o1, ArrivalAndDepartureBean o2) {
String a = RoutePresenter.getNameForRoute(o1.getTrip().getRoute());
String b = RoutePresenter.getNameForRoute(o2.getTrip().getRoute());
if (a.equals(b))
return SORT_BY_TIME.compare(o1, o2);
return NaturalStringOrder.compareNaturalIgnoreCaseAscii(a, b);
}
}
private static class SortByTime implements OrderConstraint {
public int compare(ArrivalAndDepartureBean o1, ArrivalAndDepartureBean o2) {
long a = o1.computeBestDepartureTime();
long b = o2.computeBestDepartureTime();
return a == b ? 0 : (a < b ? -1 : 1);
}
}
private static class SortByDestination implements OrderConstraint {
public int compare(ArrivalAndDepartureBean o1, ArrivalAndDepartureBean o2) {
String a = StringLibrary.getBestName(o1.getTripHeadsign(),
o1.getTrip().getTripHeadsign(), "");
String b = StringLibrary.getBestName(o2.getTripHeadsign(),
o2.getTrip().getTripHeadsign(), "");
int i = a.compareTo(b);
if (i == 0)
return SORT_BY_TIME.compare(o1, o2);
return i;
}
}
}