/**
   * Promotes user in specified ladder. If user is not member of the ladder RankingException will be
   * thrown If promoter is not null and he is member of the ladder and his rank is lower then user's
   * RankingException will be thrown too. If there is no group to promote the user to
   * RankingException would be thrown
   *
   * @param promoter null if action is performed from console or by a plugin
   * @param ladderName Ladder name
   * @throws RankingException
   */
  public PermissionGroup promote(PermissionUser promoter, String ladderName)
      throws RankingException {
    if (ladderName == null || ladderName.isEmpty()) {
      ladderName = "default";
    }

    int promoterRank = getPromoterRankAndCheck(promoter, ladderName);
    int rank = this.getRank(ladderName);

    PermissionGroup sourceGroup = this.getRankLadders().get(ladderName);
    PermissionGroup targetGroup = null;

    for (Map.Entry<Integer, PermissionGroup> entry :
        this.manager.getRankLadder(ladderName).entrySet()) {
      int groupRank = entry.getValue().getRank();
      if (groupRank >= rank) { // group have equal or lower than current rank
        continue;
      }

      if (groupRank <= promoterRank) { // group have higher rank than promoter
        continue;
      }

      if (targetGroup != null
          && groupRank <= targetGroup.getRank()) { // group have higher rank than target group
        continue;
      }

      targetGroup = entry.getValue();
    }

    if (targetGroup == null) {
      throw new RankingException("User are not promoteable", this, promoter);
    }

    this.swapGroups(sourceGroup, targetGroup);

    this.callEvent(PermissionEntityEvent.Action.RANK_CHANGED);

    return targetGroup;
  }