/**
   * Provides the next record from the underlying iterator after applying filter strings generated
   * by the set of filters in use by the iterator.
   */
  @Override
  public VariantContext next() {
    final VariantContext ctx = this.iterator.next();
    final Set<String> filterStrings = new HashSet<String>();

    // Collect variant level filters
    for (final VariantFilter filter : this.filters) {
      final String val = filter.filter(ctx);
      if (val != null) filterStrings.add(val);
    }

    // Collect genotype level filters in a Map of Sample -> List<filter string>
    final ListMap<String, String> gtFilterStrings = new ListMap<String, String>();
    final Set<String> variantSamples = new HashSet<String>();
    for (final Genotype gt : ctx.getGenotypes()) {
      if (gt.isCalled() && !gt.isHomRef()) variantSamples.add(gt.getSampleName());

      for (final GenotypeFilter filter : gtFilters) {
        final String filterString = filter.filter(ctx, gt);
        if (filterString != null) gtFilterStrings.add(gt.getSampleName(), filterString);
      }
    }

    // If all genotypes are filtered apply a site level filter
    if (gtFilterStrings.keySet().containsAll(variantSamples)) {
      filterStrings.add(ALL_GTS_FILTERED);
    }

    // Make a builder and set the site level filter appropriately
    final VariantContextBuilder builder = new VariantContextBuilder(ctx);
    if (filterStrings.isEmpty()) {
      builder.passFilters();
    } else {
      builder.filters(filterStrings);
    }

    // Apply filters to the necessary genotypes
    builder.noGenotypes();
    final List<Genotype> newGenotypes = new ArrayList<Genotype>(ctx.getNSamples());
    for (final Genotype gt : ctx.getGenotypes()) {
      final GenotypeBuilder gtBuilder = new GenotypeBuilder(gt);
      final List<String> filtersLocal = gtFilterStrings.get(gt.getSampleName());

      if (filtersLocal == null || filtersLocal.isEmpty()) {
        gtBuilder.filter(PASS_FILTER);
      } else {
        gtBuilder.filters(filtersLocal);
      }
      newGenotypes.add(gtBuilder.make());
    }
    builder.genotypes(newGenotypes);

    return builder.make();
  }
  @Override
  public ListVector convert(Iterable<Genotype> genotypes) {

    NamedBuilder fullBuilder = ListVector.newNamedBuilder();

    // add a named element in the build for each sample
    for (Genotype gt : genotypes) {
      NamedBuilder itemBuilder = ListVector.newNamedBuilder();

      // add id
      itemBuilder.add("ID", gt.getSampleName());

      if (header == null) {
        // only add genotype
        itemBuilder.add("GT", getGT(gt));
      } else {

        itemBuilder.add("GT", getGT(gt));
        Collection<VCFFormatHeaderLine> formatLines = header.getFormatHeaderLines();
        for (VCFFormatHeaderLine hl : formatLines) {
          String key = hl.getID();
          VCFHeaderLineType type = hl.getType();

          if (key.equals("GT")) {
            continue;
          }

          Object val = gt.getAnyAttribute(key);
          switch (type) {
            case String:
              {
                if (val instanceof List<?>) {
                  List<String> items = (List<String>) val;
                  StringArrayVector vec = new StringArrayVector(items);
                  itemBuilder.add(key, vec);
                } else if (val instanceof String[]) {
                  String[] items = (String[]) val;
                  StringArrayVector vec = new StringArrayVector(items);
                  itemBuilder.add(key, vec);
                } else if (val instanceof String) {
                  String items = (String) val;
                  StringArrayVector vec = new StringArrayVector(items);
                  itemBuilder.add(key, vec);
                }

                break;
              }
            case Float:
              {
                if (val instanceof List<?>) {
                  List<Double> items = (List<Double>) val;
                  DoubleArrayVector vec = new DoubleArrayVector(items);
                  itemBuilder.add(key, vec);
                } else if (val instanceof Double[]) {
                  double[] items = ArrayUtils.toPrimitive((Double[]) val);
                  DoubleArrayVector vec = new DoubleArrayVector(items);
                  itemBuilder.add(key, vec);
                } else if (val instanceof Double) {
                  Double items = (Double) val;
                  DoubleArrayVector vec = new DoubleArrayVector(items);
                  itemBuilder.add(key, vec);
                } else if (val instanceof String) {
                  Double items = Double.parseDouble((String) val);
                  DoubleArrayVector vec = new DoubleArrayVector(items);
                  itemBuilder.add(key, vec);
                }

                break;
              }
            case Integer:
              {
                if (val instanceof List<?>) {
                  List<Integer> items = (List<Integer>) val;
                  int[] arr = new int[items.size()];
                  for (int i = 0; i < items.size(); i++) {
                    arr[i] = items.get(i);
                  }
                  IntArrayVector vec = new IntArrayVector(arr);
                  itemBuilder.add(key, vec);
                } else if (val instanceof Integer[]) {
                  int[] items = ArrayUtils.toPrimitive((Integer[]) val);
                  IntArrayVector vec = new IntArrayVector(items);
                  itemBuilder.add(key, vec);
                } else if (val instanceof Integer) {
                  Integer items = (Integer) val;
                  IntArrayVector vec = new IntArrayVector(items);
                  itemBuilder.add(key, vec);
                } else if (val instanceof String) {
                  Integer items = Integer.parseInt((String) val);
                  IntArrayVector vec = new IntArrayVector(items);
                  itemBuilder.add(key, vec);
                }

                break;
              }

            case Character:
              {
                char value = (char) gt.getAnyAttribute(key);
                itemBuilder.add(key, new String(new char[] {value}));

                break;
              }
            default:
              throw new IllegalStateException(type + " is not supported");
          }
        }
      }

      fullBuilder.add(gt.getSampleName(), itemBuilder.build());
    }

    return fullBuilder.build();
  }