/**
   * Finds the lowest number (highest level) organisaiton unit level from the organisations assigned
   * to the current user.
   */
  private int getCurrentUsersLowestNumberOrgUnitLevel() {
    int level = APPROVAL_LEVEL_UNAPPROVED;

    Set<OrganisationUnit> userOrgUnits = currentUserService.getCurrentUser().getOrganisationUnits();

    for (OrganisationUnit orgUnit : userOrgUnits) {
      if (orgUnit.getLevel() < level) {
        level = orgUnit.getLevel();
      }
    }
    return level;
  }
  @Override
  public DataApprovalLevel getLowestDataApprovalLevel(
      OrganisationUnit orgUnit, DataElementCategoryOptionCombo attributeOptionCombo) {
    Set<CategoryOptionGroupSet> cogSets = null;

    if (attributeOptionCombo != null
        && attributeOptionCombo != categoryService.getDefaultDataElementCategoryOptionCombo()) {
      cogSets = new HashSet<>();

      for (DataElementCategoryOption option : attributeOptionCombo.getCategoryOptions()) {
        if (option.getGroupSets() != null) {
          cogSets.addAll(option.getGroupSets());
        }
      }
    }

    int orgUnitLevel = orgUnit.getLevel();

    List<DataApprovalLevel> approvalLevels = getDataApprovalLevelsByOrgUnitLevel(orgUnitLevel);

    for (DataApprovalLevel level : Lists.reverse(approvalLevels)) {
      if (level.getCategoryOptionGroupSet() == null) {
        if (cogSets == null) {
          return level;
        }
      } else if (cogSets != null && cogSets.contains(level.getCategoryOptionGroupSet())) {
        return level;
      }
    }

    return null;
  }
  /**
   * Get the approval level for a user for a given organisation unit. It is assumed that the user
   * has access to the organisation unit (must be checked elsewhere, it is not checked here.) If the
   * organisation unit is above all approval levels, returns null (no approval levels apply.)
   *
   * <p>If users are restricted to viewing approved data only, users may see data from lower levels
   * *only* if it is approved *below* this approval level (higher number approval level). Or, if
   * this method returns the lowest (highest number) approval level, users may see unapproved data.
   *
   * <p>If users have approve/unapprove authority (checked elsewhere, not here), the returned level
   * is the level at which users may approve/unapprove. If users have authority to approve at lower
   * levels, they may approve at levels below the returned level.
   *
   * <p>If users have accept/unaccept authority (checked elsewhere, not here), users may
   * accept/unaccept at the level just *below* this level.
   *
   * @param orgUnit organisation unit to test.
   * @param user the user.
   * @param approvalLevels app data approval levels.
   * @return approval level for user.
   */
  private DataApprovalLevel getUserApprovalLevel(
      OrganisationUnit orgUnit, User user, List<DataApprovalLevel> approvalLevels) {
    int userOrgUnitLevel = orgUnit.getLevel();

    DataApprovalLevel userLevel = null;

    for (DataApprovalLevel level : approvalLevels) {
      if (level.getOrgUnitLevel() >= userOrgUnitLevel
          && securityService.canRead(level)
          && canReadCOGS(user, level.getCategoryOptionGroupSet())) {
        userLevel = level;
        break;
      }
    }

    return userLevel;
  }
  public void write(Document document, ExportParams params) {
    I18n i18n = params.getI18n();
    I18nFormat format = params.getFormat();

    PDFUtils.printObjectFrontPage(
        document, params.getOrganisationUnits(), i18n, format, "organisation_unit_hierarchy");

    if (params.getOrganisationUnits() != null && params.getOrganisationUnits().size() > 0) {
      Collection<OrganisationUnit> hierarchy = getHierarchy();

      PdfPTable table = getPdfPTable(false, 0.100f);

      for (OrganisationUnit unit : hierarchy) {
        String indent = getIndent(unit.getLevel());

        table.addCell(getTextCell(indent + unit.getName()));
      }

      addTableToDocument(document, table);

      moveToNewPage(document);
    }
  }
  @Override
  public DataApprovalLevel getHighestDataApprovalLevel(OrganisationUnit orgUnit) {
    int orgUnitLevel = orgUnit.getLevel();

    DataApprovalLevel levelAbove = null;

    int levelAboveOrgUnitLevel = 0;

    List<DataApprovalLevel> userApprovalLevels = getUserDataApprovalLevels();

    for (DataApprovalLevel level : userApprovalLevels) {
      log.debug("Get highest data approval level: " + level.getName());

      if (level.getOrgUnitLevel() == orgUnitLevel) {
        return level; // Exact match on org unit level.
      } else if (level.getOrgUnitLevel() > levelAboveOrgUnitLevel) {
        levelAbove = level; // Must be first matching approval level for this org unit level.

        levelAboveOrgUnitLevel = level.getOrgUnitLevel();
      }
    }

    return levelAbove; // Closest ancestor above, or null if none.
  }