package edu.mit.mitmobile2.shuttles;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.util.Log;
import edu.mit.mitmobile2.MobileWebApi;
import edu.mit.mitmobile2.R;
import edu.mit.mitmobile2.maps.MITMapView;
import edu.mit.mitmobile2.objs.MapItem;
import edu.mit.mitmobile2.objs.MapPoint;
import edu.mit.mitmobile2.objs.RouteItem;
import edu.mit.mitmobile2.objs.RouteMapItem;
import edu.mit.mitmobile2.objs.StopMapItem;
import edu.mit.mitmobile2.objs.VehicleMapItem;
import edu.mit.mitmobile2.objs.RouteItem.Loc;
import edu.mit.mitmobile2.objs.RouteItem.Stops;
import edu.mit.mitmobile2.objs.RouteItem.Vehicle;
public class ShuttleModel {
public static final String KEY_STOP_TITLE = "stop_title";
public static final String KEY_STOP_ID = "stop_id";
public static final String KEY_ROUTE_ID = "route_id";
public static final String KEY_TIME = "time";
static final String BASE_PATH = "/shuttles";
static private long lastRouteFetchTime = -1;
static private long ROUTE_CACHE_TIMEOUT = 20 * 60 * 1000; // 20 minutes
static private ArrayList<RouteItem> routes = null;
static final int ALERT_EXPIRE_TIME = 30 * 60 * 1000; // 30 minutes
static private HashMap<String, List<Stops>> stops = new HashMap<String, List<Stops>>();
public static boolean routesLoaded() {
return (routes != null && routes.size() > 0);
}
private static List<RouteItem> getRoutes() {
if(routes != null) {
return routes;
} else {
return Collections.<RouteItem>emptyList();
}
}
public static List<RouteItem> getRoutes(boolean isSafeRide) {
ArrayList<RouteItem> routes = new ArrayList<RouteItem>();
for (RouteItem aRouteItem : getRoutes()) {
if (aRouteItem.isSafeRide == isSafeRide) {
routes.add(aRouteItem);
}
}
return routes;
}
public static List<RouteItem> getSortedRoutes() {
// reorder the routes
// so that day time shuttles proceed night time saferides
ArrayList<RouteItem> reorderedRoutes = new ArrayList<RouteItem>();
reorderedRoutes.addAll(getRoutes(false));
reorderedRoutes.addAll(getRoutes(true));
return reorderedRoutes;
}
public static RouteItem getRoute(String routeId) {
for(RouteItem routeItem : getRoutes()) {
if(routeItem.route_id.equals(routeId)) {
return routeItem;
}
}
return null;
}
public static RouteItem getUpdatedRoute(RouteItem routeItem) {
return getRoute(routeItem.route_id);
}
public static List<Stops> getStops(String stopId) {
return stops.get(stopId);
}
public static Stops getStops(String stopId, String routeId) {
for(Stops stops : getStops(stopId)) {
if(stops.route_id.equals(routeId)) {
return stops;
}
}
return null;
}
public static int getStopPosition(List<Stops> stops, String stopId) {
for(int position = 0; position < stops.size(); position++) {
Stops stop = stops.get(position);
if(stop.id.equals(stopId)) {
return position;
}
}
return -1;
}
public static void addRoute(RouteItem routeItem) {
if(routes == null) {
routes = new ArrayList<RouteItem>();
}
for(int index=0; index < routes.size(); index++) {
if(routes.get(index).route_id.equals(routeItem.route_id)) {
routes.set(index, routeItem);
return;
}
}
routes.add(routeItem);
}
/*
* This function only updates the route data, if the route currently
* exists in the routes list
*/
private static void updateRouteItem(RouteItem routeItem) {
if(routes != null) {
for(int index=0; index < routes.size(); index++) {
if(routes.get(index).route_id.equals(routeItem.route_id)) {
routes.set(index, routeItem);
return;
}
}
}
}
public static void fetchRoutes(Context context, final Handler uiHandler, boolean forceRefresh) {
if(!forceRefresh &&
(System.currentTimeMillis() - lastRouteFetchTime) < ROUTE_CACHE_TIMEOUT ) {
MobileWebApi.sendSuccessMessage(uiHandler);
return;
}
HashMap<String, String> routesParameters = new HashMap<String, String>();
routesParameters.put("command", "routes");
routesParameters.put("compact", "true");
MobileWebApi webApi = new MobileWebApi(false, true, "Shuttle Routes", context, uiHandler);
webApi.requestJSONArray(BASE_PATH, routesParameters,
new MobileWebApi.JSONArrayResponseListener(new MobileWebApi.DefaultErrorListener(uiHandler), null) {
@Override
public void onResponse(JSONArray array) throws JSONException {
routes = new ArrayList<RouteItem>();
routes.addAll(RoutesParser.routesParser(array));
lastRouteFetchTime = System.currentTimeMillis();
MobileWebApi.sendSuccessMessage(uiHandler);
}
}
);
}
public static void fetchRouteDetails(Context context, RouteItem routeItem, final Handler uiHandler) {
// determine if any predictions for route details data exists
// if some predictions exists do a silent request (i.e. dont show error or loading messages);
boolean silent = true;
/*
RouteItem cachedRouteItem = getRoute(routeItem.route_id);
boolean silent = false;
if(cachedRouteItem != null && !cachedRouteItem.stops.isEmpty()) {
if(cachedRouteItem.stops.get(0).predictions.isEmpty()) {
silent = true;
}
}
*/
HashMap<String, String> routeInfoParameters = new HashMap<String, String>();
routeInfoParameters.put("command", "routeInfo");
routeInfoParameters.put("full", "true");
routeInfoParameters.put("id", routeItem.route_id);
MobileWebApi webApi = new MobileWebApi(!silent, !silent, "Shuttle Route", context, uiHandler);
webApi.requestJSONObject(BASE_PATH, routeInfoParameters,
new MobileWebApi.JSONObjectResponseListener(new MobileWebApi.DefaultErrorListener(uiHandler), null) {
@Override
public void onResponse(JSONObject object) throws JSONException {
RouteItem routeItem = RoutesParser.parseJSONRouteObject(object);
updateRouteItem(routeItem);
MobileWebApi.sendSuccessMessage(uiHandler,routeItem);
}
}
);
}
public static void fetchStopDetails(final String stopId, final Handler handler) {
HashMap<String, String> stopInfoParameters = new HashMap<String, String>();
stopInfoParameters.put("command", "stopInfo");
stopInfoParameters.put("id", stopId);
MobileWebApi webApi = new MobileWebApi();
webApi.requestJSONObject(stopInfoParameters, new MobileWebApi.JSONObjectResponseListener(null, null) {
@Override
public void onResponse(JSONObject object) throws JSONException {
stops.put(stopId, RoutesParser.parseJSONStopsArray(object));
MobileWebApi.sendSuccessMessage(handler);
}
});
}
/****************************************************/
public static HashMap<String, HashMap<String, Long>> getAlerts(SharedPreferences pref) {
HashMap<String, HashMap<String, Long>> alertIdx = new HashMap<String, HashMap<String, Long>>();
//SharedPreferences pref = ctx.getSharedPreferences(Global.PREFS,Context.MODE_WORLD_READABLE|Context.MODE_WORLD_READABLE);
String stop_id = pref.getString(ShuttleModel.KEY_STOP_ID, null);
String routes = pref.getString(ShuttleModel.KEY_ROUTE_ID, null);
String times = pref.getString(ShuttleModel.KEY_TIME, null);
Log.d("ShuttleModel", "shuttle-alerts: get-> stop_id: " + stop_id);
Log.d("ShuttleModel", "shuttle-alerts: get-> route_id: " + routes);
Log.d("ShuttleModel", "shuttle-alerts: get-> times: " + times);
if ((stop_id!=null)&&(routes!=null)&&(times!=null)) {
String[] stop_alarms = stop_id.split(",");
String[] routes_alarms = routes.split(",");
String[] times_alarms = times.split(",");
if (stop_alarms.length<1) {
return null;
}
if (stop_alarms.length!=routes_alarms.length) {
Log.e("ShuttleModel", "shuttle-alerts: bad length 1");
return null;
}
if (stop_alarms.length!=times_alarms.length) {
Log.e("ShuttleModel", "shuttle-alerts: bad length 2");
return null;
}
Long time;
HashMap<String,Long> route_times;
for (int x=0; x<stop_alarms.length; x++) {
String s = stop_alarms[x];
if ("".equals(s)) {
Log.d("ShuttleModel", "shuttle-alerts: error?");
continue;
}
route_times = alertIdx.get(s);
if (route_times==null) {
route_times = new HashMap<String,Long>();
}
if ("".equals(times_alarms[x])) continue;
time = Long.valueOf(times_alarms[x]);
route_times.put(routes_alarms[x], time);
alertIdx.put(s, route_times);
}
}
return alertIdx;
}
/**
* @param alertIdx **************************************************/
//public static void saveAlerts(Context ctx, HashMap<String, HashMap<String, Long>> alertIdx ) {
public static void saveAlerts(SharedPreferences pref, HashMap<String, HashMap<String, Long>> alertIdx ) {
//SharedPreferences pref = ctx.getSharedPreferences(Global.PREFS,Context.MODE_PRIVATE);
//SharedPreferences pref = ctx.getSharedPreferences(Global.PREFS,Context.MODE_WORLD_READABLE|Context.MODE_WORLD_READABLE);
SharedPreferences.Editor editor = pref.edit();
String concat1 = "";
String concat2 = "";
String concat3 = "";
HashMap<String,Long> route_times;
for (String stop : alertIdx.keySet()) {
route_times = alertIdx.get(stop);
for (String r : route_times.keySet()) {
Long t = route_times.get(r);
concat1 += stop + ",";
concat2 += r + ",";
concat3 += t + ",";
}
}
editor.putString(ShuttleModel.KEY_STOP_ID, concat1);
editor.putString(ShuttleModel.KEY_ROUTE_ID, concat2);
editor.putString(ShuttleModel.KEY_TIME, concat3);
Log.d("ShuttleModel", "shuttle-alerts: set-> stop_id: " + concat1);
Log.d("ShuttleModel", "shuttle-alerts: set-> route_id: " + concat2);
Log.d("ShuttleModel", "shuttle-alerts: set-> times: " + concat3);
boolean success = editor.commit();
if (!success) {
Log.e("ShuttleModel", "shuttle-alerts: failed shuttle commit");
}
}
public static HashMap<String,ArrayList<? extends MapItem>> buildShuttleItems(RouteItem updatedRouteItem) {
HashMap<String,ArrayList<? extends MapItem>> layers = new HashMap<String,ArrayList<? extends MapItem>>();
ArrayList<RouteMapItem> routeItems = new ArrayList<RouteMapItem>();
ArrayList<VehicleMapItem> vehicleItems = new ArrayList<VehicleMapItem>();
ArrayList<StopMapItem> stopItems = new ArrayList<StopMapItem>();
RouteMapItem route = new RouteMapItem();
// create a polygon for route
// create shuttle pin + callout for each stop
// create shuttle location pin for each vehicle location
route.setGeometryType(MapItem.TYPE_POLYGON);
// loop through all the stops
for (int s = 0; s < updatedRouteItem.stops.size(); s++) {
Stops stop = updatedRouteItem.stops.get(s);
MapPoint mapPoint = new MapPoint();
mapPoint.lat_wgs84 = Double.valueOf(stop.lat);
mapPoint.long_wgs84 = Double.valueOf(stop.lon);
// for each stop, add the paths to the route polygon
for (int p = 0; p < stop.path.size(); p++) {
Loc loc = (Loc) stop.path.get(p);
// get a map point for the stop
MapPoint pathPoint = new MapPoint();
pathPoint.lat_wgs84 = Double.valueOf(loc.lat);
pathPoint.long_wgs84 = Double.valueOf(loc.lon);
// add the map point to the route polygon
route.getMapPoints().add(pathPoint);
}
// Create a map item to show an icon at the stop
StopMapItem stopItem = new StopMapItem();
// add itemData
stopItem.getItemData().put("alertSet", stop.alertSet);
stopItem.getItemData().put("direction", stop.direction);
stopItem.getItemData().put("gps", stop.gps);
stopItem.getItemData().put("id", stop.id);
stopItem.getItemData().put("lat", stop.lat);
stopItem.getItemData().put("lon", stop.lon);
stopItem.getItemData().put("next", stop.next);
stopItem.getItemData().put("now", stop.now);
stopItem.getItemData().put("route_id", updatedRouteItem.route_id);
stopItem.getItemData().put("title", stop.title);
stopItem.getItemData().put("upcoming", stop.upcoming);
stopItem.setGeometryType(MapItem.TYPE_POINT);
if (updatedRouteItem.isRunning) {
if (stop.upcoming) {
stopItem.symbol = R.drawable.map_pin_shuttle_stop_complete_next;
} else {
stopItem.symbol = R.drawable.map_pin_shuttle_stop_complete;
}
} else {
stopItem.symbol = R.drawable.map_pin_shuttle_stop_complete;
}
stopItem.getItemData().put("displayName", stop.title);
stopItem.getMapPoints().add(mapPoint);
stopItems.add(stopItem);
}
// add route
routeItems.add(route);
layers.put(ShuttlesMapActivity.SHUTTLE_ROUTE_LAYER, routeItems);
// add stops
layers.put(ShuttlesMapActivity.SHUTTLE_STOPS_LAYER, stopItems);
// add vehicle locations
for (int v = 0; v < updatedRouteItem.vehicleLocations.size(); v++) {
Vehicle vehicle = updatedRouteItem.vehicleLocations.get(v);
VehicleMapItem vehicleItem = new VehicleMapItem();
vehicleItem.setGeometryType(MapItem.TYPE_POINT);
vehicleItem.setSymbol(VehicleMapItem
.getShuttleMarkerForHeading(vehicle.heading));
vehicleItem.getMapPoints().add(
new MapPoint(vehicle.lat, vehicle.lon));
vehicleItem.getItemData().put("heading", vehicle.heading);
vehicleItem.getItemData().put("lat", vehicle.lat);
vehicleItem.getItemData().put("lon", vehicle.lon);
vehicleItems.add(vehicleItem);
}
layers.put(MITMapView.DEFAULT_GRAPHICS_LAYER, vehicleItems);
return layers;
}
}