/*
   * this fetches the tour from the database or network
   * if the tour db cache has timed out it will try to cache from network
   * but if that fails revert to using the version cached in the DB
   */
  private static void fetchTourHelper(final Context context, final Handler uiHandler) {
    final TourDB tourDB = TourDB.getInstance(context);

    if (tourDB.tourDetailsCached(TOUR_GUID, true)) {
      List<TourHeader> tourHeaders = tourDB.retrieveTourHeaders();
      sTour = new Tour(tourHeaders.get(0));
      tourDB.populateTourDetails(sTour);
      MobileWebApi.sendSuccessMessage(uiHandler);
      return;
    }

    // check if stale version of tour exists in db
    if (tourDB.tourDetailsCached(TOUR_GUID, false)) {
      // stale version exists, so check if stale version is recent enough
      MobileWebApi webApi = new MobileWebApi(false, false, null, context, null);
      HashMap<String, String> query = new HashMap<String, String>();
      query.put("module", "tours");
      query.put("command", "toursList");

      webApi.requestJSONArray(
          query,
          new JSONArrayResponseListener(new TourRefreshErrorListener(tourDB, uiHandler), null) {

            @Override
            public void onResponse(JSONArray array) throws ServerResponseException, JSONException {
              Long lastModified = null;
              for (int i = 0; i < array.length(); i++) {
                JSONObject tourSummary = array.getJSONObject(i);
                if (tourSummary.getString("id").equals(TOUR_GUID)) {
                  lastModified = tourSummary.getLong("last-modified") * 1000;
                }
              }

              if (lastModified != null) {
                if (lastModified < tourDB.tourDetailsLastUpdated(TOUR_GUID)) {
                  // cache is still fresh (use it, and mark it as fresh)
                  tourDB.markTourFresh(TOUR_GUID);

                  // retrieve tour from DB
                  List<TourHeader> tourHeaders = tourDB.retrieveTourHeaders();
                  sTour = new Tour(tourHeaders.get(0));
                  tourDB.populateTourDetails(sTour);
                  MobileWebApi.sendSuccessMessage(uiHandler);
                  return;
                }
              }

              // tour is not up-to-date we need to update it.
              fetchTourFromNetwork(context, uiHandler);
            }
          });

      return;
    }

    // no fresh or stale version of tour exists must retrieve it from network
    fetchTourFromNetwork(context, uiHandler);
  }
  public void fetchSuggestedUrl(Context context, String originString, final Handler uiHandler) {
    HashMap<String, String> parameters = new HashMap<String, String>();
    parameters.put("module", MODULE_NAME);
    parameters.put("q", originString);

    final MobileWebApi api = new MobileWebApi(false, true, "QR Code", context, uiHandler);
    api.requestJSONObject(
        parameters,
        new JSONObjectResponseListener(
            new MobileWebApi.DefaultErrorListener(uiHandler),
            new MobileWebApi.DefaultCancelRequestListener(uiHandler)) {

          @Override
          public void onResponse(JSONObject object) throws ServerResponseException, JSONException {

            SuggestedUrl suggest = new SuggestedUrl();
            if (object.has("success") && !object.isNull("success")) {
              boolean success = object.getBoolean("success");
              suggest.isSuccess = success;
              // success so parse rest of JSON
              suggest.type = object.getString("type");
              suggest.displayType = object.getString("displayType");
              suggest.displayName = object.getString("displayName");

              // init suggest.shareAction
              JSONObject shareObj = object.optJSONObject("share");
              if (shareObj != null) {
                suggest.shareAction = new QRAction();
                suggest.shareAction.title = shareObj.getString("title");
                suggest.shareAction.payload = shareObj.getString("data");
              }

              // init suggest.actions array
              JSONArray actionsArr = object.optJSONArray("actions");
              if (actionsArr != null) {
                int actionsArrLength = actionsArr.length();
                for (int i = 0; i < actionsArrLength; i++) {

                  JSONObject jsonAction = actionsArr.getJSONObject(i);
                  QRAction actionItem = new QRAction();
                  actionItem.title = jsonAction.optString("title");
                  actionItem.payload = jsonAction.optString("url");

                  suggest.actions.add(actionItem);
                }
              }
              MobileWebApi.sendSuccessMessage(uiHandler, suggest);
            }
            MobileWebApi.sendErrorMessage(uiHandler);
          }
        });
  }
  /** ***************************************************************** */
  public void fetchMIT150WelcomeContent(final Context context, final Handler uiHandler) {

    MobileWebApi webApi = new MobileWebApi(false, true, "MIT150", context, uiHandler);

    HashMap<String, String> query = new HashMap<String, String>();
    query.put("module", "anniversary");
    query.put("command", "welcome");

    webApi.requestJSONObject(
        query,
        new JSONObjectResponseListener(new MobileWebApi.DefaultErrorListener(uiHandler), null) {
          @Override
          public void onResponse(JSONObject object) throws ServerResponseException, JSONException {
            content = object.getString("content");
            MobileWebApi.sendSuccessMessage(uiHandler);
          }
        });
  }
    @Override
    public void onError() {
      // failed to retrieve last modified time from network, lets just assume
      // data in db is up-to-date

      // retrieve tour from DB
      List<TourHeader> tourHeaders = mTourDB.retrieveTourHeaders();
      sTour = new Tour(tourHeaders.get(0));
      mTourDB.populateTourDetails(sTour);
      MobileWebApi.sendSuccessMessage(mUIHandler);
    }
  private static void fetchTourFromNetwork(Context context, final Handler uiHandler) {
    final TourDB tourDB = TourDB.getInstance(context);

    MobileWebApi webApi = new MobileWebApi(false, true, "Tour", context, uiHandler);

    HashMap<String, String> query = new HashMap<String, String>();
    query.put("module", "tours");
    query.put("command", "tourDetails");
    query.put("tourId", TOUR_GUID);

    webApi.requestJSONObject(
        query,
        new JSONObjectResponseListener(new MobileWebApi.DefaultErrorListener(uiHandler), null) {

          @Override
          public void onResponse(JSONObject object) throws ServerResponseException, JSONException {

            // save header info
            String title = object.getString("title");
            String descriptionTop = object.getString("description-top");
            String descriptionBottom = object.getString("description-bottom");

            Tour tour = new Tour(TOUR_GUID, title, descriptionTop, descriptionBottom);

            // save footer info
            String feedbackSubject = object.getJSONObject("feedback").getString("subject");
            tour.setFeedbackSubject(feedbackSubject);

            // parse all the links for the tour footer
            JSONArray links = object.getJSONArray("links");
            for (int linkIndex = 0; linkIndex < links.length(); linkIndex++) {
              JSONObject linkJson = links.getJSONObject(linkIndex);
              tour.getFooter().addLink(linkJson.getString("title"), linkJson.getString("url"));
            }

            // parse all the sites data
            JSONArray sites = object.getJSONArray("sites");

            for (int siteIndex = 0; siteIndex < sites.length(); siteIndex++) {
              JSONObject siteJson = sites.getJSONObject(siteIndex);
              Site site =
                  tour.addSite(
                      siteJson.getString("id"),
                      siteJson.getString("title"),
                      siteJson.getString("photo-url"),
                      siteJson.getString("thumbnail156-url"),
                      optString(siteJson, "audio-url"),
                      parseLatLon(siteJson.getJSONObject("latlon")));

              if (siteJson.has("exit-directions")) {
                String destinationGuid;
                if (siteIndex + 1 < sites.length()) {
                  destinationGuid = sites.getJSONObject(siteIndex + 1).getString("id");
                } else {
                  destinationGuid = sites.getJSONObject(0).getString("id");
                }

                site.setExitDirections(
                    siteJson.getJSONObject("exit-directions").getString("title"),
                    destinationGuid,
                    siteJson.getJSONObject("exit-directions").getInt("zoom"));

                JSONObject exitDirectionsJson = siteJson.getJSONObject("exit-directions");
                populateContent(site.getExitDirections().getContent(), exitDirectionsJson);
                populatePath(site.getExitDirections().getPath(), exitDirectionsJson);

                site.getExitDirections().setPhotoUrl(optString(exitDirectionsJson, "photo-url"));
                site.getExitDirections().setAudioUrl(optString(exitDirectionsJson, "audio-url"));
              }

              populateContent(site.getContent(), siteJson);
            }

            // parse all the start locations data
            tour.setStartLocationsHeader(
                object.getJSONObject("start-locations").getString("header"));
            JSONArray startLocationsItems =
                object.getJSONObject("start-locations").getJSONArray("items");

            for (int startLocationIndex = 0;
                startLocationIndex < startLocationsItems.length();
                startLocationIndex++) {
              JSONObject startLocationJson = startLocationsItems.getJSONObject(startLocationIndex);
              tour.addStartLocation(
                  startLocationJson.getString("title"),
                  startLocationJson.getString("id"),
                  startLocationJson.getString("start-site"),
                  startLocationJson.getString("content"),
                  optString(startLocationJson, "photo-url"),
                  parseLatLon(startLocationJson.getJSONObject("latlon")));
            }

            ArrayList<Tour> tours = new ArrayList<Tour>();
            tours.add(tour);
            tourDB.saveTourHeaders(tours);
            tourDB.saveTourDetails(tour);
            sTour = tour;
            MobileWebApi.sendSuccessMessage(uiHandler);
          }
        });
  }