public void onComplete() {
        // summarize read data
        if (metrics.TOTAL_READS > 0) {
          metrics.PCT_PF_READS = (double) metrics.PF_READS / (double) metrics.TOTAL_READS;
          metrics.PCT_ADAPTER = this.adapterReads / (double) metrics.PF_READS;
          metrics.MEAN_READ_LENGTH = readLengthHistogram.getMean();

          // Calculate BAD_CYCLES
          metrics.BAD_CYCLES = 0;
          for (final Histogram<Integer>.Bin cycleBin : badCycleHistogram.values()) {
            final double badCyclePercentage = cycleBin.getValue() / metrics.TOTAL_READS;
            if (badCyclePercentage >= .8) {
              metrics.BAD_CYCLES++;
            }
          }

          if (doRefMetrics) {
            if (metrics.PF_READS > 0)
              metrics.PCT_PF_READS_ALIGNED =
                  (double) metrics.PF_READS_ALIGNED / (double) metrics.PF_READS;
            if (metrics.PF_READS_ALIGNED > 0)
              metrics.PCT_READS_ALIGNED_IN_PAIRS =
                  (double) metrics.READS_ALIGNED_IN_PAIRS / (double) metrics.PF_READS_ALIGNED;
            if (metrics.PF_READS_ALIGNED > 0)
              metrics.STRAND_BALANCE = numPositiveStrand / (double) metrics.PF_READS_ALIGNED;
            if (this.chimerasDenominator > 0)
              metrics.PCT_CHIMERAS = this.chimeras / (double) this.chimerasDenominator;

            if (nonBisulfiteAlignedBases > 0)
              metrics.PF_MISMATCH_RATE =
                  mismatchHistogram.getSum() / (double) nonBisulfiteAlignedBases;
            metrics.PF_HQ_MEDIAN_MISMATCHES = hqMismatchHistogram.getMedian();
            if (hqNonBisulfiteAlignedBases > 0)
              metrics.PF_HQ_ERROR_RATE =
                  hqMismatchHistogram.getSum() / (double) hqNonBisulfiteAlignedBases;
            if (metrics.PF_ALIGNED_BASES > 0)
              metrics.PF_INDEL_RATE = this.indels / (double) metrics.PF_ALIGNED_BASES;
          }
        }
      }
      private void collectReadData(final SAMRecord record, final ReferenceSequence ref) {
        metrics.TOTAL_READS++;
        readLengthHistogram.increment(record.getReadBases().length);

        if (!record.getReadFailsVendorQualityCheckFlag()) {
          metrics.PF_READS++;
          if (isNoiseRead(record)) metrics.PF_NOISE_READS++;

          if (record.getReadUnmappedFlag()) {
            // If the read is unmapped see if it's adapter sequence
            final byte[] readBases = record.getReadBases();
            if (!(record instanceof BAMRecord)) StringUtil.toUpperCase(readBases);

            if (isAdapterSequence(readBases)) {
              this.adapterReads++;
            }
          } else if (doRefMetrics) {
            metrics.PF_READS_ALIGNED++;
            if (!record.getReadNegativeStrandFlag()) numPositiveStrand++;

            if (record.getReadPairedFlag() && !record.getMateUnmappedFlag()) {
              metrics.READS_ALIGNED_IN_PAIRS++;

              // Check that both ends have mapq > minimum
              final Integer mateMq = record.getIntegerAttribute("MQ");
              if (mateMq == null
                  || mateMq >= MAPPING_QUALITY_THRESOLD
                      && record.getMappingQuality() >= MAPPING_QUALITY_THRESOLD) {
                ++this.chimerasDenominator;

                // With both reads mapped we can see if this pair is chimeric
                if (Math.abs(record.getInferredInsertSize()) > maxInsertSize
                    || !record.getReferenceIndex().equals(record.getMateReferenceIndex())) {
                  ++this.chimeras;
                }
              }
            }
          }
        }
      }
      private void collectQualityData(final SAMRecord record, final ReferenceSequence reference) {
        // If the read isnt an aligned PF read then look at the read for no-calls
        if (record.getReadUnmappedFlag()
            || record.getReadFailsVendorQualityCheckFlag()
            || !doRefMetrics) {
          final byte[] readBases = record.getReadBases();
          for (int i = 0; i < readBases.length; i++) {
            if (SequenceUtil.isNoCall(readBases[i])) {
              badCycleHistogram.increment(
                  CoordMath.getCycle(record.getReadNegativeStrandFlag(), readBases.length, i));
            }
          }
        } else if (!record.getReadFailsVendorQualityCheckFlag()) {
          final boolean highQualityMapping = isHighQualityMapping(record);
          if (highQualityMapping) metrics.PF_HQ_ALIGNED_READS++;

          final byte[] readBases = record.getReadBases();
          final byte[] refBases = reference.getBases();
          final byte[] qualities = record.getBaseQualities();
          final int refLength = refBases.length;
          long mismatchCount = 0;
          long hqMismatchCount = 0;

          for (final AlignmentBlock alignmentBlock : record.getAlignmentBlocks()) {
            final int readIndex = alignmentBlock.getReadStart() - 1;
            final int refIndex = alignmentBlock.getReferenceStart() - 1;
            final int length = alignmentBlock.getLength();

            for (int i = 0; i < length && refIndex + i < refLength; ++i) {
              final int readBaseIndex = readIndex + i;
              boolean mismatch =
                  !SequenceUtil.basesEqual(readBases[readBaseIndex], refBases[refIndex + i]);
              boolean bisulfiteBase = false;
              if (mismatch && isBisulfiteSequenced) {
                if ((record.getReadNegativeStrandFlag()
                        && (refBases[refIndex + i] == 'G' || refBases[refIndex + i] == 'g')
                        && (readBases[readBaseIndex] == 'A' || readBases[readBaseIndex] == 'a'))
                    || ((!record.getReadNegativeStrandFlag())
                            && (refBases[refIndex + i] == 'C' || refBases[refIndex + i] == 'c')
                            && (readBases[readBaseIndex] == 'T')
                        || readBases[readBaseIndex] == 't')) {

                  bisulfiteBase = true;
                  mismatch = false;
                }
              }

              if (mismatch) mismatchCount++;

              metrics.PF_ALIGNED_BASES++;
              if (!bisulfiteBase) nonBisulfiteAlignedBases++;

              if (highQualityMapping) {
                metrics.PF_HQ_ALIGNED_BASES++;
                if (!bisulfiteBase) hqNonBisulfiteAlignedBases++;
                if (qualities[readBaseIndex] >= BASE_QUALITY_THRESHOLD)
                  metrics.PF_HQ_ALIGNED_Q20_BASES++;
                if (mismatch) hqMismatchCount++;
              }

              if (mismatch || SequenceUtil.isNoCall(readBases[readBaseIndex])) {
                badCycleHistogram.increment(
                    CoordMath.getCycle(record.getReadNegativeStrandFlag(), readBases.length, i));
              }
            }
          }

          mismatchHistogram.increment(mismatchCount);
          hqMismatchHistogram.increment(hqMismatchCount);

          // Add any insertions and/or deletions to the global count
          for (final CigarElement elem : record.getCigar().getCigarElements()) {
            final CigarOperator op = elem.getOperator();
            if (op == CigarOperator.INSERTION || op == CigarOperator.DELETION) ++this.indels;
          }
        }
      }