/**
   * Exposed as `POST /api/photos`.
   *
   * <p>Takes the following payload in the request body. Payload represents a Photo that should be
   * created. { "id":0, "ownerUserId":0, "ownerDisplayName":"", "ownerProfileUrl":"",
   * "ownerProfilePhoto":"", "themeId":0, "themeDisplayName":"", "numVotes":0, "voted":false, //
   * Whether or not the current user has voted on this. "created":0, "fullsizeUrl":"",
   * "thumbnailUrl":"", "voteCtaUrl":"", // URL for Vote interactive post button.
   * "photoContentUrl":"" // URL for Google crawler to hit to get info. }
   *
   * <p>Returns the following JSON response representing the created Photo. { "id":0,
   * "ownerUserId":0, "ownerDisplayName":"", "ownerProfileUrl":"", "ownerProfilePhoto":"",
   * "themeId":0, "themeDisplayName":"", "numVotes":0, "voted":false, // Whether or not the current
   * user has voted on this. "created":0, "fullsizeUrl":"", "thumbnailUrl":"", "voteCtaUrl":"", //
   * URL for Vote interactive post button. "photoContentUrl":"" // URL for Google crawler to hit to
   * get info. }
   *
   * <p>Issues the following errors along with corresponding HTTP response codes: 400: "Bad Request"
   * if the request is missing image data. 401: "Unauthorized request" (if certain parameters are
   * present in the request) 401: "Access token expired" (there is a logged in user, but he doesn't
   * have a refresh token and his access token is expiring in less than 100 seconds, get a new token
   * and retry) 500: "Error while writing app activity: " + error from client library.
   *
   * @see javax.servlet.http.HttpServlet#doPost( javax.servlet.http.HttpServletRequest,
   *     javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
    try {
      checkAuthorization(req);
      List<BlobKey> blobKeys = blobstoreService.getUploads(req).get("image");
      BlobKey imageKey = null;

      if (blobKeys != null) {
        imageKey = blobKeys.iterator().next();
      }

      if (imageKey == null) {
        sendError(resp, 400, "Missing image data.");
      }

      Long currentUserId = (Long) req.getSession().getAttribute(CURRENT_USER_SESSION_KEY);
      User author = ofy().load().type(User.class).id(currentUserId).get();
      GoogleCredential credential = this.getCredentialFromLoggedInUser(req);
      Photo photo = new Photo();
      photo.setOwnerUserId(author.getId());
      photo.setOwnerDisplayName(author.getGoogleDisplayName());
      photo.setOwnerProfilePhoto(author.getGooglePublicProfilePhotoUrl());
      photo.setOwnerProfileUrl(author.getGooglePublicProfileUrl());
      photo.setThemeId(Theme.getCurrentTheme().getId());
      photo.setThemeDisplayName(Theme.getCurrentTheme().getDisplayName());
      photo.setCreated(Calendar.getInstance().getTime());
      photo.setNumVotes(0);
      photo.setImageBlobKey(imageKey.getKeyString());
      ofy().save().entity(photo).now();
      ofy().clear();
      photo = ofy().load().type(Photo.class).id(photo.getId()).get();
      addPhotoToGooglePlusHistory(author, photo, credential);
      sendResponse(req, resp, photo);
    } catch (UserNotAuthorizedException e) {
      sendError(resp, 401, "Unauthorized request");
    } catch (MomentWritingException e) {
      sendError(resp, 500, "Error while writing app activity: " + e.getMessage());
    } catch (GoogleTokenExpirationException e) {
      sendError(resp, 401, "Access token expired");
    }
  }
  /**
   * Exposed as `GET /api/photos`.
   *
   * <p>Accepts the following request parameters.
   *
   * <p>'photoId': id of the requested photo. Will return a single Photo. 'themeId': id of a theme.
   * Will return the collection of photos for the specified theme. 'userId': id of the owner of the
   * photo. Will return the collection of photos for that user. The keyword ‘me’ can be used and
   * will be converted to the logged in user. Requires auth. 'friends': value evaluated to boolean,
   * if true will filter only photos from friends of the logged in user. Requires auth.
   *
   * <p>Returns the following JSON response representing a list of Photos.
   *
   * <p>[ { "id":0, "ownerUserId":0, "ownerDisplayName":"", "ownerProfileUrl":"",
   * "ownerProfilePhoto":"", "themeId":0, "themeDisplayName":"", "numVotes":0, "voted":false, //
   * Whether or not the current user has voted on this. "created":0, "fullsizeUrl":"",
   * "thumbnailUrl":"", "voteCtaUrl":"", // URL for Vote interactive post button.
   * "photoContentUrl":"" // URL for Google crawler to hit to get info. }, ... ]
   *
   * <p>Issues the following errors along with corresponding HTTP response codes: 401: "Unauthorized
   * request" (if certain parameters are present in the request)
   *
   * @see javax.servlet.http.HttpServlet#doGet( javax.servlet.http.HttpServletRequest,
   *     javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
    try {
      BASE_URL = getBaseUrlFromRequest(req);
      String photoId = req.getParameter("photoId");
      String themeId = req.getParameter("themeId");
      String userIdParam = req.getParameter("userId");
      Long userId;
      Long currentUserId = null;
      if (req.getSession().getAttribute(CURRENT_USER_SESSION_KEY) != null) {
        currentUserId =
            Long.parseLong(req.getSession().getAttribute(CURRENT_USER_SESSION_KEY).toString());
      }
      boolean showFriends = Boolean.parseBoolean(req.getParameter("friends"));
      Query<Photo> q = ofy().load().type(Photo.class);
      if (photoId != null) {
        // Get the photo with the given ID and return it.
        Photo photo = q.filter("id", Long.parseLong(photoId)).first().get();
        sendResponse(req, resp, photo);
      } else {
        if (userIdParam != null) {
          // If the key word me is used, retrieve the current user from the session
          // The user needs to be authenticated to use 'me'
          if (userIdParam.equals("me")) {
            checkAuthorization(req);
            userId = currentUserId;
          } else {
            userId = Long.parseLong(userIdParam);
          }
          if (showFriends) {
            checkAuthorization(req);
            // Get all photos for the user's friends.
            User user = ofy().load().type(User.class).filterKey(User.key(userId)).first().get();
            List<Long> friendIds = user.getFriendIds();
            if (friendIds.size() > 30) {
              friendIds = friendIds.subList(0, 30);
            }
            if (friendIds.size() > 0) {
              q = q.filter("ownerUserId in", friendIds);
            } else {
              List<Photo> emptyList = new ArrayList<Photo>();
              // If there are no friends for the user, return an empty list
              sendResponse(req, resp, emptyList, "photohunt#photos");
              return;
            }
          } else {
            // Get all photos for the user.
            q = q.filter("ownerUserId", userId);
          }
        }
        if (themeId != null) {
          // Limit photos to just those for the given theme.
          q = q.filter("themeId", Long.parseLong(themeId));
        }
        List<Photo> photos = q.list();

        if (currentUserId != null) {
          // Build Hash map of voted photos
          List<Vote> userVotes =
              ofy().load().type(Vote.class).filter("ownerUserId", currentUserId).list();
          HashMap<Long, Boolean> userVotesTable = new HashMap<Long, Boolean>(userVotes.size());
          Iterator<Vote> votesIterator = userVotes.iterator();
          while (votesIterator.hasNext()) {
            userVotesTable.put(votesIterator.next().getPhotoId(), true);
          }
          // Check if user voted for each photo
          Iterator<Photo> photosIterator = photos.iterator();
          while (photosIterator.hasNext()) {
            Photo current = photosIterator.next();
            current.setVoted(userVotesTable.containsKey(current.getId()));
          }
        }
        sendResponse(req, resp, photos, "photohunt#photos");
      }
    } catch (UserNotAuthorizedException e) {
      sendError(resp, 401, "Unauthorized request");
    }
  }