Ref<Result> OneDReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
  try {
    return doDecode(image, hints);
  } catch (NotFoundException const& nfe) {
    // std::cerr << "trying harder" << std::endl;
    bool tryHarder = hints.getTryHarder();
    if (tryHarder && image->isRotateSupported()) {
      // std::cerr << "v rotate" << std::endl;
      Ref<BinaryBitmap> rotatedImage(image->rotateCounterClockwise());
      // std::cerr << "^ rotate" << std::endl;
      Ref<Result> result = doDecode(rotatedImage, hints);
      // Doesn't have java metadata stuff
      ArrayRef< Ref<ResultPoint> >& points (result->getResultPoints());
      if (points && !points->empty()) {
        int height = rotatedImage->getHeight();
        for (int i = 0; i < points->size(); i++) {
          points[i].reset(new OneDResultPoint(height - points[i]->getY() - 1, points[i]->getX()));
        }
      }
      // std::cerr << "tried harder" << std::endl;
      return result;
    } else {
      // std::cerr << "tried harder nfe" << std::endl;
      throw nfe;
    }
  }
}
void MultiFormatReader::setHints(DecodeHints hints) {
  hints_ = hints;
  readers_.clear();
  bool tryHarder = hints.getTryHarder();

  bool addOneDReader = hints.containsFormat(BarcodeFormat::UPC_E) ||
    hints.containsFormat(BarcodeFormat::UPC_A) ||
    hints.containsFormat(BarcodeFormat::UPC_E) ||
    hints.containsFormat(BarcodeFormat::EAN_13) ||
    hints.containsFormat(BarcodeFormat::EAN_8) ||
    hints.containsFormat(BarcodeFormat::CODABAR) ||
    hints.containsFormat(BarcodeFormat::CODE_39) ||
    hints.containsFormat(BarcodeFormat::CODE_93) ||
    hints.containsFormat(BarcodeFormat::CODE_128) ||
    hints.containsFormat(BarcodeFormat::ITF) ||
    hints.containsFormat(BarcodeFormat::RSS_14) ||
    hints.containsFormat(BarcodeFormat::RSS_EXPANDED);
  if (addOneDReader && !tryHarder) {
    readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints)));
  }
  if (hints.containsFormat(BarcodeFormat::QR_CODE)) {
    readers_.push_back(Ref<Reader>(new zxing::qrcode::QRCodeReader()));
  }
  if (hints.containsFormat(BarcodeFormat::DATA_MATRIX)) {
    readers_.push_back(Ref<Reader>(new zxing::datamatrix::DataMatrixReader()));
  }
  if (hints.containsFormat(BarcodeFormat::AZTEC)) {
    readers_.push_back(Ref<Reader>(new zxing::aztec::AztecReader()));
  }
    if (hints.containsFormat(BarcodeFormat::PDF_417)) {
        readers_.push_back(Ref<Reader>(new zxing::pdf417::PDF417Reader()));
    }
/*
      if (formats.contains(BarcodeFormat.PDF_417)) {
         readers.add(new PDF417Reader());
      }
      if (formats.contains(BarcodeFormat.MAXICODE)) {
         readers.add(new MaxiCodeReader());
      }
*/
  if (addOneDReader && tryHarder) {
    readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints)));
  }
  if (readers_.size() == 0) {
    if (!tryHarder) {
      readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints)));
    }
    readers_.push_back(Ref<Reader>(new zxing::qrcode::QRCodeReader()));
    readers_.push_back(Ref<Reader>(new zxing::datamatrix::DataMatrixReader()));
    readers_.push_back(Ref<Reader>(new zxing::aztec::AztecReader()));
    readers_.push_back(Ref<Reader>(new zxing::pdf417::PDF417Reader()));
      
    // readers.add(new PDF417Reader());
    // readers.add(new MaxiCodeReader());

    if (tryHarder) {
      readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints)));
    }
  }
}
Ref<Result> OneDReader::doDecode(Ref<BinaryBitmap> image, DecodeHints hints) {
  int width = image->getWidth();
  int height = image->getHeight();
  Ref<BitArray> row(new BitArray(width));

  int middle = height >> 1;
  bool tryHarder = hints.getTryHarder();
  int rowStep = std::max(1, height >> (tryHarder ? 8 : 5));
  using namespace std;
  // cerr << "rS " << rowStep << " " << height << " " << tryHarder << endl;
  int maxLines;
  if (tryHarder) {
    maxLines = height; // Look at the whole image, not just the center
  } else {
    maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image
  }

  for (int x = 0; x < maxLines; x++) {

    // Scanning from the middle out. Determine which row we're looking at next:
    int rowStepsAboveOrBelow = (x + 1) >> 1;
    bool isAbove = (x & 0x01) == 0; // i.e. is x even?
    int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);
    if (false) {
      std::cerr << "rN "
                << rowNumber << " "
                << height << " "
                << middle << " "
                << rowStep << " "
                << isAbove << " "
                << rowStepsAboveOrBelow
                << std::endl;
    }
    if (rowNumber < 0 || rowNumber >= height) {
      // Oops, if we run off the top or bottom, stop
      break;
    }

    // Estimate black point for this row and load it:
    try {
      row = image->getBlackRow(rowNumber, row);
    } catch (NotFoundException const& ignored) {
      (void)ignored;
      continue;
    }

    // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to
    // handle decoding upside down barcodes.
    for (int attempt = 0; attempt < 2; attempt++) {
      if (attempt == 1) {
        row->reverse(); // reverse the row and continue
      }

      // Java hints stuff missing

      try {
        // Look for a barcode
        // std::cerr << "rn " << rowNumber << " " << typeid(*this).name() << std::endl;
        Ref<Result> result = decodeRow(rowNumber, row);
        // We found our barcode
        if (attempt == 1) {
          // But it was upside down, so note that
          // result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(180));
          // And remember to flip the result points horizontally.
          ArrayRef< Ref<ResultPoint> > points(result->getResultPoints());
          if (points) {
            points[0] = Ref<ResultPoint>(new OneDResultPoint(width - points[0]->getX() - 1,
                                                             points[0]->getY()));
            points[1] = Ref<ResultPoint>(new OneDResultPoint(width - points[1]->getX() - 1,
                                                             points[1]->getY()));
            
          }
        }
        return result;
      } catch (ReaderException const& re) {
        (void)re;
        continue;
      }
    }
  }
  throw NotFoundException();
}
Ref<FinderPatternInfo> FinderPatternFinder::find(DecodeHints const &hints) {
    bool tryHarder = hints.getTryHarder();

    size_t maxI = image_->getHeight();
    size_t maxJ = image_->getWidth();


    // We are looking for black/white/black/white/black modules in
    // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far

    // As this is used often, we use an integer array instead of vector
    int stateCount[5];
    bool done = false;


    // Let's assume that the maximum version QR Code we support takes up 1/4
    // the height of the image, and then account for the center being 3
    // modules in size. This gives the smallest number of pixels the center
    // could be, so skip this often. When trying harder, look for all
    // QR versions regardless of how dense they are.
    int iSkip = (3 * maxI) / (4 * MAX_MODULES);
    if (iSkip < MIN_SKIP || tryHarder) {
        iSkip = MIN_SKIP;
    }

    // This is slightly faster than using the Ref. Efficiency is important here
    BitMatrix &matrix = *image_;

    for (size_t i = iSkip - 1; i < maxI && !done; i += iSkip) {
        // Get a row of black/white values

        stateCount[0] = 0;
        stateCount[1] = 0;
        stateCount[2] = 0;
        stateCount[3] = 0;
        stateCount[4] = 0;
        int currentState = 0;
        for (size_t j = 0; j < maxJ; j++) {
            if (matrix.get(j, i)) {
                // Black pixel
                if ((currentState & 1) == 1) { // Counting white pixels
                    currentState++;
                }
                stateCount[currentState]++;
            } else { // White pixel
                if ((currentState & 1) == 0) { // Counting black pixels
                    if (currentState == 4) { // A winner?
                        if (foundPatternCross(stateCount)) { // Yes
                            bool confirmed = handlePossibleCenter(stateCount, i, j);
                            if (confirmed) {
                                // Start examining every other line. Checking each line turned out to be too
                                // expensive and didn't improve performance.
                                iSkip = 2;
                                if (hasSkipped_) {
                                    done = haveMultiplyConfirmedCenters();
                                } else {
                                    int rowSkip = findRowSkip();
                                    if (rowSkip > stateCount[2]) {
                                        // Skip rows between row of lower confirmed center
                                        // and top of presumed third confirmed center
                                        // but back up a bit to get a full chance of detecting
                                        // it, entire width of center of finder pattern

                                        // Skip by rowSkip, but back off by stateCount[2] (size
                                        // of last center of pattern we saw) to be conservative,
                                        // and also back off by iSkip which is about to be
                                        // re-added
                                        i += rowSkip - stateCount[2] - iSkip;
                                        j = maxJ - 1;
                                    }
                                }
                            } else {
                                stateCount[0] = stateCount[2];
                                stateCount[1] = stateCount[3];
                                stateCount[2] = stateCount[4];
                                stateCount[3] = 1;
                                stateCount[4] = 0;
                                currentState = 3;
                                continue;
                            }
                            // Clear state to start looking again
                            currentState = 0;
                            stateCount[0] = 0;
                            stateCount[1] = 0;
                            stateCount[2] = 0;
                            stateCount[3] = 0;
                            stateCount[4] = 0;
                        } else { // No, shift counts back by two
                            stateCount[0] = stateCount[2];
                            stateCount[1] = stateCount[3];
                            stateCount[2] = stateCount[4];
                            stateCount[3] = 1;
                            stateCount[4] = 0;
                            currentState = 3;
                        }
                    } else {
                        stateCount[++currentState]++;
                    }
                } else { // Counting white pixels
                    stateCount[currentState]++;
                }
            }
        }
        if (foundPatternCross(stateCount)) {
            bool confirmed = handlePossibleCenter(stateCount, i, maxJ);
            if (confirmed) {
                iSkip = stateCount[0];
                if (hasSkipped_) {
                    // Found a third one
                    done = haveMultiplyConfirmedCenters();
                }
            }
        }
    }

    vector<Ref<FinderPattern> > patternInfo = selectBestPatterns();
    patternInfo = orderBestPatterns(patternInfo);

    Ref<FinderPatternInfo> result(new FinderPatternInfo(patternInfo));
    return result;
}
vector<Ref<FinderPatternInfo> > MultiFinderPatternFinder::findMulti(DecodeHints const& hints){
  bool tryHarder = hints.getTryHarder();
  Ref<BitMatrix> image = image_; // Protected member
  int maxI = image->getHeight();
  int maxJ = image->getWidth();
  // We are looking for black/white/black/white/black modules in
  // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far

  // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
  // image, and then account for the center being 3 modules in size. This gives the smallest
  // number of pixels the center could be, so skip this often. When trying harder, look for all
  // QR versions regardless of how dense they are.
  int iSkip = (int) (maxI / (MAX_MODULES * 4.0f) * 3);
  if (iSkip < MIN_SKIP || tryHarder) {
    iSkip = MIN_SKIP;
  }

  int stateCount[5];
  for (int i = iSkip - 1; i < maxI; i += iSkip) {
    // Get a row of black/white values
    stateCount[0] = 0;
    stateCount[1] = 0;
    stateCount[2] = 0;
    stateCount[3] = 0;
    stateCount[4] = 0;
    int currentState = 0;
    for (int j = 0; j < maxJ; j++) {
      if (image->get(j, i)) {
        // Black pixel
        if ((currentState & 1) == 1) { // Counting white pixels
          currentState++;
        }
        stateCount[currentState]++;
      } else { // White pixel
        if ((currentState & 1) == 0) { // Counting black pixels
          if (currentState == 4) { // A winner?
            if (foundPatternCross(stateCount) && handlePossibleCenter(stateCount, i, j)) { // Yes
              // Clear state to start looking again
              currentState = 0;
              stateCount[0] = 0;
              stateCount[1] = 0;
              stateCount[2] = 0;
              stateCount[3] = 0;
              stateCount[4] = 0;
            } else { // No, shift counts back by two
              stateCount[0] = stateCount[2];
              stateCount[1] = stateCount[3];
              stateCount[2] = stateCount[4];
              stateCount[3] = 1;
              stateCount[4] = 0;
              currentState = 3;
            }
          } else {
            stateCount[++currentState]++;
          }
        } else { // Counting white pixels
          stateCount[currentState]++;
        }
      }
    } // for j=...

    if (foundPatternCross(stateCount)) {
      handlePossibleCenter(stateCount, i, maxJ);
    } // end if foundPatternCross
  } // for i=iSkip-1 ...
  vector<vector<Ref<FinderPattern> > > patternInfo = selectBestPatterns();
  vector<Ref<FinderPatternInfo> > result;
  for (unsigned int i = 0; i < patternInfo.size(); i++) {
    vector<Ref<FinderPattern> > pattern = patternInfo[i];
    pattern = FinderPatternFinder::orderBestPatterns(pattern);
    result.push_back(Ref<FinderPatternInfo>(new FinderPatternInfo(pattern)));
  }
  return result;
}