public static FeatureReader wrap(FeatureReader reader, final Query remainingParameters)
      throws DataStoreException {

    final Integer start = remainingParameters.getStartIndex();
    final Integer max = remainingParameters.getMaxFeatures();
    final Filter filter = remainingParameters.getFilter();
    final GenericName[] properties = remainingParameters.getPropertyNames();
    final SortBy[] sorts = remainingParameters.getSortBy();
    final double[] resampling = remainingParameters.getResolution();
    final CoordinateReferenceSystem crs = remainingParameters.getCoordinateSystemReproject();
    final Hints hints = remainingParameters.getHints();

    // we should take care of wrapping the reader in a correct order to avoid
    // unnecessary calculations. fast and reducing number wrapper should be placed first.
    // but we must not take misunderstanding assumptions neither.
    // exemple : filter is slow than startIndex and MaxFeature but must be placed before
    //          otherwise the result will be illogic.

    // wrap sort by ---------------------------------------------------------
    // This can be really expensive, and force the us to read the full iterator.
    // that may cause out of memory errors.
    if (sorts != null && sorts.length != 0) {
      reader = GenericSortByFeatureIterator.wrap(reader, sorts);
    }

    // wrap filter ----------------------------------------------------------
    // we must keep the filter first since it impacts the start index and max feature
    if (filter != null && filter != Filter.INCLUDE) {
      if (filter == Filter.EXCLUDE) {
        // filter that exclude everything, use optimzed reader
        reader = GenericEmptyFeatureIterator.createReader(reader.getFeatureType());
        // close original reader
        reader.close();
      } else {
        reader = GenericFilterFeatureIterator.wrap(reader, filter);
      }
    }

    // wrap start index -----------------------------------------------------
    if (start != null && start > 0) {
      reader = GenericStartIndexFeatureIterator.wrap(reader, start);
    }

    // wrap max -------------------------------------------------------------
    if (max != null) {
      if (max == 0) {
        // use an optimized reader
        reader = GenericEmptyFeatureIterator.createReader(reader.getFeatureType());
        // close original reader
        reader.close();
      } else {
        reader = GenericMaxFeatureIterator.wrap(reader, max);
      }
    }

    // wrap properties, remove primary keys if necessary --------------------
    final Boolean hide = (Boolean) hints.get(HintsPending.FEATURE_HIDE_ID_PROPERTY);
    final FeatureType original = reader.getFeatureType();
    FeatureType mask = original;
    if (properties != null) {
      final List<GenericName> names = new ArrayList<GenericName>();
      loop:
      for (GenericName n : properties) {
        for (GenericName dn : names) {
          if (NamesExt.match(n, dn)) continue loop;
        }
        names.add(n);
      }

      try {
        mask = FeatureTypeUtilities.createSubType(mask, names.toArray(new GenericName[0]));
      } catch (MismatchedFeatureException ex) {
        throw new DataStoreException(ex);
      }
    }
    if (hide != null && hide) {
      try {
        // remove primary key properties
        mask = FeatureTypeUtilities.excludePrimaryKeyFields(mask);
      } catch (MismatchedFeatureException ex) {
        throw new DataStoreException(ex);
      }
    }
    if (mask != original) {
      reader = GenericRetypeFeatureIterator.wrap(reader, mask, hints);
    }

    // wrap resampling ------------------------------------------------------
    if (resampling != null) {
      reader =
          GenericTransformFeatureIterator.wrap(
              reader, new GeometryScaleTransformer(resampling[0], resampling[1]), hints);
    }

    // wrap reprojection ----------------------------------------------------
    if (crs != null) {
      try {
        reader = GenericReprojectFeatureIterator.wrap(reader, crs, hints);
      } catch (FactoryException ex) {
        throw new DataStoreException(ex);
      } catch (MismatchedFeatureException ex) {
        throw new DataStoreException(ex);
      }
    }

    return reader;
  }