class ParseUser extends ParseObject {

  Task<Void> signUpAsync(Task<Void> toAwait) {
    final ParseUser user = getCurrentUser(); //TODO (grantland): convert to async
    synchronized (mutex) {
      final String sessionToken = user != null ? user.getSessionToken() : null;
      if (ParseTextUtils.isEmpty(getUsername())) {
        return Task.forError(new IllegalArgumentException("Username cannot be missing or blank"));
      }

      if (ParseTextUtils.isEmpty(getPassword())) {
        return Task.forError(new IllegalArgumentException("Password cannot be missing or blank"));
      }

      if (getObjectId() != null) {
        // For anonymous users, there may be an objectId. Setting the
        // userName will have removed the anonymous link and set the
        // value in the authData object to JSONObject.NULL, so we can
        // just treat it like a save operation.
        Map<String, Map<String, String>> authData = getAuthData();
        if (authData.containsKey(ParseAnonymousUtils.AUTH_TYPE)
            && authData.get(ParseAnonymousUtils.AUTH_TYPE) == null) {
          return saveAsync(sessionToken, toAwait);
        }

        // Otherwise, throw.
        return Task.forError(
            new IllegalArgumentException("Cannot sign up a user that has already signed up."));
      }

      // If the operationSetQueue is has operation sets in it, then a save or signUp is in progress.
      // If there is a signUp or save already in progress, don't allow another one to start.
      if (operationSetQueue.size() > 1) {
        return Task.forError(
            new IllegalArgumentException("Cannot sign up a user that is already signing up."));
      }

      // If the current user is an anonymous user, merge this object's data into the anonymous user
      // and save.
      if (user != null && ParseAnonymousUtils.isLinked(user)) {
        // this doesn't have any outstanding saves, so we can safely merge its operations into the
        // current user.

        if (this == user) {
          return Task.forError(
              new IllegalArgumentException("Attempt to merge currentUser with itself."));
        }

        boolean isLazy = user.isLazy();
        final String oldUsername = user.getUsername();
        final String oldPassword = user.getPassword();
        final Map<String, String> anonymousData = user.getAuthData(ParseAnonymousUtils.AUTH_TYPE);

        user.copyChangesFrom(this);
        user.setUsername(getUsername());
        user.setPassword(getPassword());
        revert();

        return user.saveAsync(sessionToken, isLazy, toAwait).continueWithTask(new Continuation<Void, Task<Void>>() {
          @Override
          public Task<Void> then(Task<Void> task) throws Exception {
            if (task.isCancelled() || task.isFaulted()) { // Error
              synchronized (user.mutex) {
                if (oldUsername != null) {
                  user.setUsername(oldUsername);
                } else {
                  user.revert(KEY_USERNAME);
                }
                if (oldPassword != null) {
                  user.setPassword(oldPassword);
                } else {
                  user.revert(KEY_PASSWORD);
                }
                user.restoreAnonymity(anonymousData);
              }
              return task;
            } else { // Success
              user.revert(KEY_PASSWORD);
              revert(KEY_PASSWORD);
            }

            mergeFromObject(user);
            return saveCurrentUserAsync(ParseUser.this);
          }
        });
      }

      final ParseOperationSet operations = startSave();

      return toAwait.onSuccessTask(new Continuation<Void, Task<Void>>() {
        @Override
        public Task<Void> then(Task<Void> task) throws Exception {
          return getUserController().signUpAsync(
              getState(), operations, sessionToken
          ).continueWithTask(new Continuation<ParseUser.State, Task<Void>>() {
            @Override
            public Task<Void> then(final Task<ParseUser.State> signUpTask) throws Exception {
              ParseUser.State result = signUpTask.getResult();
              return handleSaveResultAsync(result,
                  operations).continueWithTask(new Continuation<Void, Task<Void>>() {
                @Override
                public Task<Void> then(Task<Void> task) throws Exception {
                  if (!signUpTask.isCancelled() && !signUpTask.isFaulted()) {
                    return saveCurrentUserAsync(ParseUser.this);
                  }
                  return signUpTask.makeVoid();
                }
              });
            }
          });
        }
      });
    }
  }

  public  ParseUser();

}