// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
// Original code is licensed as follows:
/*
 * Copyright 2013 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "xfa/src/fxbarcode/barcode.h"
#include "xfa/src/fxbarcode/BC_DecoderResult.h"
#include "xfa/src/fxbarcode/BC_ResultPoint.h"
#include "xfa/src/fxbarcode/common/BC_CommonBitMatrix.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417Codeword.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417Common.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417BarcodeValue.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417BarcodeMetadata.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417BoundingBox.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417DetectionResultColumn.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417DetectionResultRowIndicatorColumn.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417DetectionResult.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417DecodedBitStreamParser.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417CodewordDecoder.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417DecodedBitStreamParser.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417ECModulusPoly.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417ECModulusGF.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417ECErrorCorrection.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417DecodedBitStreamParser.h"
#include "xfa/src/fxbarcode/pdf417/BC_PDF417ScanningDecoder.h"
int32_t CBC_PDF417ScanningDecoder::CODEWORD_SKEW_SIZE = 2;
int32_t CBC_PDF417ScanningDecoder::MAX_ERRORS = 3;
int32_t CBC_PDF417ScanningDecoder::MAX_EC_CODEWORDS = 512;
CBC_PDF417ECErrorCorrection* CBC_PDF417ScanningDecoder::errorCorrection = NULL;
CBC_PDF417ScanningDecoder::CBC_PDF417ScanningDecoder() {}
CBC_PDF417ScanningDecoder::~CBC_PDF417ScanningDecoder() {}
void CBC_PDF417ScanningDecoder::Initialize() {
  errorCorrection = new CBC_PDF417ECErrorCorrection;
}
void CBC_PDF417ScanningDecoder::Finalize() {
  delete errorCorrection;
}
CBC_CommonDecoderResult* CBC_PDF417ScanningDecoder::decode(
    CBC_CommonBitMatrix* image,
    CBC_ResultPoint* imageTopLeft,
    CBC_ResultPoint* imageBottomLeft,
    CBC_ResultPoint* imageTopRight,
    CBC_ResultPoint* imageBottomRight,
    int32_t minCodewordWidth,
    int32_t maxCodewordWidth,
    int32_t& e) {
  CBC_BoundingBox* boundingBox = new CBC_BoundingBox(
      image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight, e);
  BC_EXCEPTION_CHECK_ReturnValue(e, NULL);
  CBC_DetectionResultRowIndicatorColumn* leftRowIndicatorColumn = NULL;
  CBC_DetectionResultRowIndicatorColumn* rightRowIndicatorColumn = NULL;
  CBC_DetectionResult* detectionResult = NULL;
  for (int32_t i = 0; i < 2; i++) {
    if (imageTopLeft != NULL) {
      leftRowIndicatorColumn =
          getRowIndicatorColumn(image, boundingBox, *imageTopLeft, TRUE,
                                minCodewordWidth, maxCodewordWidth);
    }
    if (imageTopRight != NULL) {
      rightRowIndicatorColumn =
          getRowIndicatorColumn(image, boundingBox, *imageTopRight, FALSE,
                                minCodewordWidth, maxCodewordWidth);
    }
    detectionResult = merge(leftRowIndicatorColumn, rightRowIndicatorColumn, e);
    if (e != BCExceptionNO) {
      e = BCExceptiontNotFoundInstance;
      delete leftRowIndicatorColumn;
      delete rightRowIndicatorColumn;
      delete boundingBox;
      return NULL;
    }
    if (i == 0 && (detectionResult->getBoundingBox()->getMinY() <
                       boundingBox->getMinY() ||
                   detectionResult->getBoundingBox()->getMaxY() >
                       boundingBox->getMaxY())) {
      delete boundingBox;
      boundingBox = detectionResult->getBoundingBox();
    } else {
      detectionResult->setBoundingBox(boundingBox);
      break;
    }
  }
  int32_t maxBarcodeColumn = detectionResult->getBarcodeColumnCount() + 1;
  detectionResult->setDetectionResultColumn(0, leftRowIndicatorColumn);
  detectionResult->setDetectionResultColumn(maxBarcodeColumn,
                                            rightRowIndicatorColumn);
  FX_BOOL leftToRight = leftRowIndicatorColumn != NULL;
  for (int32_t barcodeColumnCount = 1; barcodeColumnCount <= maxBarcodeColumn;
       barcodeColumnCount++) {
    int32_t barcodeColumn = leftToRight ? barcodeColumnCount
                                        : maxBarcodeColumn - barcodeColumnCount;
    if (detectionResult->getDetectionResultColumn(barcodeColumn) != NULL) {
      continue;
    }
    CBC_DetectionResultColumn* detectionResultColumn = NULL;
    if (barcodeColumn == 0 || barcodeColumn == maxBarcodeColumn) {
      detectionResultColumn = new CBC_DetectionResultRowIndicatorColumn(
          boundingBox, barcodeColumn == 0);
    } else {
      detectionResultColumn = new CBC_DetectionResultColumn(boundingBox);
    }
    detectionResult->setDetectionResultColumn(barcodeColumn,
                                              detectionResultColumn);
    int32_t startColumn = -1;
    int32_t previousStartColumn = startColumn;
    for (int32_t imageRow = boundingBox->getMinY();
         imageRow <= boundingBox->getMaxY(); imageRow++) {
      startColumn =
          getStartColumn(detectionResult, barcodeColumn, imageRow, leftToRight);
      if (startColumn < 0 || startColumn > boundingBox->getMaxX()) {
        if (previousStartColumn == -1) {
          continue;
        }
        startColumn = previousStartColumn;
      }
      CBC_Codeword* codeword = detectCodeword(
          image, boundingBox->getMinX(), boundingBox->getMaxX(), leftToRight,
          startColumn, imageRow, minCodewordWidth, maxCodewordWidth);
      if (codeword != NULL) {
        detectionResultColumn->setCodeword(imageRow, codeword);
        previousStartColumn = startColumn;
        minCodewordWidth = minCodewordWidth < codeword->getWidth()
                               ? minCodewordWidth
                               : codeword->getWidth();
        maxCodewordWidth = maxCodewordWidth > codeword->getWidth()
                               ? maxCodewordWidth
                               : codeword->getWidth();
      }
    }
  }
  CBC_CommonDecoderResult* decoderresult =
      createDecoderResult(detectionResult, e);
  if (e != BCExceptionNO) {
    delete detectionResult;
    return NULL;
  }
  return decoderresult;
}
CFX_ByteString CBC_PDF417ScanningDecoder::toString(
    CFX_PtrArray* barcodeMatrix) {
  CFX_ByteString result;
  for (int32_t row = 0; row < barcodeMatrix->GetSize(); row++) {
    result += row;
    int32_t l = 0;
    for (; l < ((CFX_PtrArray*)barcodeMatrix->GetAt(row))->GetSize(); l++) {
      CBC_BarcodeValue* barcodeValue =
          (CBC_BarcodeValue*)((CFX_PtrArray*)barcodeMatrix->GetAt(row))
              ->GetAt(l);
      if (barcodeValue->getValue()->GetSize() == 0) {
        result += "";
      } else {
        result += barcodeValue->getValue()->GetAt(0);
        result +=
            barcodeValue->getConfidence(barcodeValue->getValue()->GetAt(0));
      }
    }
  }
  return result;
}
CBC_DetectionResult* CBC_PDF417ScanningDecoder::merge(
    CBC_DetectionResultRowIndicatorColumn* leftRowIndicatorColumn,
    CBC_DetectionResultRowIndicatorColumn* rightRowIndicatorColumn,
    int32_t& e) {
  if (leftRowIndicatorColumn == NULL && rightRowIndicatorColumn == NULL) {
    e = BCExceptionIllegalArgument;
    return NULL;
  }
  CBC_BarcodeMetadata* barcodeMetadata =
      getBarcodeMetadata(leftRowIndicatorColumn, rightRowIndicatorColumn);
  if (barcodeMetadata == NULL) {
    e = BCExceptionCannotMetadata;
    return NULL;
  }
  CBC_BoundingBox* leftboundingBox =
      adjustBoundingBox(leftRowIndicatorColumn, e);
  if (e != BCExceptionNO) {
    delete barcodeMetadata;
    return NULL;
  }
  CBC_BoundingBox* rightboundingBox =
      adjustBoundingBox(rightRowIndicatorColumn, e);
  if (e != BCExceptionNO) {
    delete barcodeMetadata;
    return NULL;
  }
  CBC_BoundingBox* boundingBox =
      CBC_BoundingBox::merge(leftboundingBox, rightboundingBox, e);
  if (e != BCExceptionNO) {
    delete barcodeMetadata;
    return NULL;
  }
  CBC_DetectionResult* detectionresult =
      new CBC_DetectionResult(barcodeMetadata, boundingBox);
  return detectionresult;
}
CBC_BoundingBox* CBC_PDF417ScanningDecoder::adjustBoundingBox(
    CBC_DetectionResultRowIndicatorColumn* rowIndicatorColumn,
    int32_t& e) {
  if (rowIndicatorColumn == NULL) {
    return NULL;
  }
  CFX_Int32Array* rowHeights = rowIndicatorColumn->getRowHeights(e);
  BC_EXCEPTION_CHECK_ReturnValue(e, NULL);
  int32_t maxRowHeight = getMax(*rowHeights);
  int32_t missingStartRows = 0;
  for (int32_t i = 0; i < rowHeights->GetSize(); i++) {
    int32_t rowHeight = rowHeights->GetAt(i);
    missingStartRows += maxRowHeight - rowHeight;
    if (rowHeight > 0) {
      break;
    }
  }
  CFX_PtrArray* codewords = rowIndicatorColumn->getCodewords();
  for (int32_t row = 0; missingStartRows > 0 && codewords->GetAt(row) == NULL;
       row++) {
    missingStartRows--;
  }
  int32_t missingEndRows = 0;
  for (int32_t row1 = rowHeights->GetSize() - 1; row1 >= 0; row1--) {
    missingEndRows += maxRowHeight - rowHeights->GetAt(row1);
    if (rowHeights->GetAt(row1) > 0) {
      break;
    }
  }
  for (int32_t row2 = codewords->GetSize() - 1;
       missingEndRows > 0 && codewords->GetAt(row2) == NULL; row2--) {
    missingEndRows--;
  }
  CBC_BoundingBox* boundingBox =
      rowIndicatorColumn->getBoundingBox()->addMissingRows(
          missingStartRows, missingEndRows, rowIndicatorColumn->isLeft(), e);
  BC_EXCEPTION_CHECK_ReturnValue(e, NULL);
  return boundingBox;
}
int32_t CBC_PDF417ScanningDecoder::getMax(CFX_Int32Array& values) {
  int32_t maxValue = -1;
  for (int32_t i = 0; i < values.GetSize(); i++) {
    int32_t value = values.GetAt(i);
    maxValue = maxValue > value ? maxValue : value;
  }
  return maxValue;
}
CBC_BarcodeMetadata* CBC_PDF417ScanningDecoder::getBarcodeMetadata(
    CBC_DetectionResultRowIndicatorColumn* leftRowIndicatorColumn,
    CBC_DetectionResultRowIndicatorColumn* rightRowIndicatorColumn) {
  CBC_BarcodeMetadata* leftBarcodeMetadata = NULL;
  CBC_BarcodeMetadata* rightBarcodeMetadata = NULL;
  if (leftRowIndicatorColumn == NULL ||
      (leftBarcodeMetadata = leftRowIndicatorColumn->getBarcodeMetadata()) ==
          NULL) {
    return rightRowIndicatorColumn == NULL
               ? NULL
               : rightRowIndicatorColumn->getBarcodeMetadata();
  }
  if (rightRowIndicatorColumn == NULL ||
      (rightBarcodeMetadata = rightRowIndicatorColumn->getBarcodeMetadata()) ==
          NULL) {
    return leftRowIndicatorColumn == NULL
               ? NULL
               : leftRowIndicatorColumn->getBarcodeMetadata();
  }
  if (leftBarcodeMetadata->getColumnCount() !=
          rightBarcodeMetadata->getColumnCount() &&
      leftBarcodeMetadata->getErrorCorrectionLevel() !=
          rightBarcodeMetadata->getErrorCorrectionLevel() &&
      leftBarcodeMetadata->getRowCount() !=
          rightBarcodeMetadata->getRowCount()) {
    delete leftBarcodeMetadata;
    delete rightBarcodeMetadata;
    return NULL;
  }
  delete rightBarcodeMetadata;
  return leftBarcodeMetadata;
}
CBC_DetectionResultRowIndicatorColumn*
CBC_PDF417ScanningDecoder::getRowIndicatorColumn(CBC_CommonBitMatrix* image,
                                                 CBC_BoundingBox* boundingBox,
                                                 CBC_ResultPoint startPoint,
                                                 FX_BOOL leftToRight,
                                                 int32_t minCodewordWidth,
                                                 int32_t maxCodewordWidth) {
  CBC_DetectionResultRowIndicatorColumn* rowIndicatorColumn =
      new CBC_DetectionResultRowIndicatorColumn(boundingBox, leftToRight);
  for (int32_t i = 0; i < 2; i++) {
    int32_t increment = i == 0 ? 1 : -1;
    int32_t startColumn = (int32_t)startPoint.GetX();
    for (int32_t imageRow = (int32_t)startPoint.GetY();
         imageRow <= boundingBox->getMaxY() &&
         imageRow >= boundingBox->getMinY();
         imageRow += increment) {
      CBC_Codeword* codeword =
          detectCodeword(image, 0, image->GetWidth(), leftToRight, startColumn,
                         imageRow, minCodewordWidth, maxCodewordWidth);
      if (codeword != NULL) {
        rowIndicatorColumn->setCodeword(imageRow, codeword);
        if (leftToRight) {
          startColumn = codeword->getStartX();
        } else {
          startColumn = codeword->getEndX();
        }
      }
    }
  }
  return rowIndicatorColumn;
}
void CBC_PDF417ScanningDecoder::adjustCodewordCount(
    CBC_DetectionResult* detectionResult,
    CFX_PtrArray* barcodeMatrix,
    int32_t& e) {
  CFX_Int32Array* numberOfCodewords =
      ((CBC_BarcodeValue*)((CFX_PtrArray*)barcodeMatrix->GetAt(0))->GetAt(1))
          ->getValue();
  int32_t calculatedNumberOfCodewords =
      detectionResult->getBarcodeColumnCount() *
          detectionResult->getBarcodeRowCount() -
      getNumberOfECCodeWords(detectionResult->getBarcodeECLevel());
  if (numberOfCodewords->GetSize() == 0) {
    if (calculatedNumberOfCodewords < 1 ||
        calculatedNumberOfCodewords >
            CBC_PDF417Common::MAX_CODEWORDS_IN_BARCODE) {
      e = BCExceptiontNotFoundInstance;
      delete numberOfCodewords;
      BC_EXCEPTION_CHECK_ReturnVoid(e);
    }
    ((CBC_BarcodeValue*)((CFX_PtrArray*)barcodeMatrix->GetAt(0))->GetAt(1))
        ->setValue(calculatedNumberOfCodewords);
  } else if (numberOfCodewords->GetAt(0) != calculatedNumberOfCodewords) {
    ((CBC_BarcodeValue*)((CFX_PtrArray*)barcodeMatrix->GetAt(0))->GetAt(1))
        ->setValue(calculatedNumberOfCodewords);
  }
  delete numberOfCodewords;
}
CBC_CommonDecoderResult* CBC_PDF417ScanningDecoder::createDecoderResult(
    CBC_DetectionResult* detectionResult,
    int32_t& e) {
  CFX_PtrArray* barcodeMatrix = createBarcodeMatrix(detectionResult);
  adjustCodewordCount(detectionResult, barcodeMatrix, e);
  if (e != BCExceptionNO) {
    for (int32_t i = 0; i < barcodeMatrix->GetSize(); i++) {
      CFX_PtrArray* temp = (CFX_PtrArray*)barcodeMatrix->GetAt(i);
      for (int32_t j = 0; j < temp->GetSize(); j++) {
        delete (CBC_BarcodeValue*)temp->GetAt(j);
      }
      temp->RemoveAll();
      delete temp;
    }
    barcodeMatrix->RemoveAll();
    delete barcodeMatrix;
    return NULL;
  }
  CFX_Int32Array erasures;
  CFX_Int32Array codewords;
  codewords.SetSize(detectionResult->getBarcodeRowCount() *
                    detectionResult->getBarcodeColumnCount());
  CFX_PtrArray ambiguousIndexValuesList;
  CFX_Int32Array ambiguousIndexesList;
  for (int32_t row = 0; row < detectionResult->getBarcodeRowCount(); row++) {
    for (int32_t l = 0; l < detectionResult->getBarcodeColumnCount(); l++) {
      CFX_Int32Array* values =
          ((CBC_BarcodeValue*)((CFX_PtrArray*)barcodeMatrix->GetAt(row))
               ->GetAt(l + 1))
              ->getValue();
      int32_t codewordIndex =
          row * detectionResult->getBarcodeColumnCount() + l;
      if (values->GetSize() == 0) {
        erasures.Add(codewordIndex);
      } else if (values->GetSize() == 1) {
        codewords[codewordIndex] = values->GetAt(0);
      } else {
        ambiguousIndexesList.Add(codewordIndex);
        ambiguousIndexValuesList.Add(values);
      }
    }
  }
  CFX_PtrArray ambiguousIndexValues;
  ambiguousIndexValues.SetSize(ambiguousIndexValuesList.GetSize());
  for (int32_t i = 0; i < ambiguousIndexValues.GetSize(); i++) {
    ambiguousIndexValues.SetAt(i, ambiguousIndexValuesList.GetAt(i));
  }
  for (int32_t l = 0; l < barcodeMatrix->GetSize(); l++) {
    CFX_PtrArray* temp = (CFX_PtrArray*)barcodeMatrix->GetAt(l);
    for (int32_t j = 0; j < temp->GetSize(); j++) {
      delete (CBC_BarcodeValue*)temp->GetAt(j);
    }
    temp->RemoveAll();
    delete temp;
  }
  barcodeMatrix->RemoveAll();
  delete barcodeMatrix;
  CBC_CommonDecoderResult* decoderResult =
      createDecoderResultFromAmbiguousValues(
          detectionResult->getBarcodeECLevel(), codewords, erasures,
          ambiguousIndexesList, ambiguousIndexValues, e);
  BC_EXCEPTION_CHECK_ReturnValue(e, NULL);
  return decoderResult;
}
CBC_CommonDecoderResult*
CBC_PDF417ScanningDecoder::createDecoderResultFromAmbiguousValues(
    int32_t ecLevel,
    CFX_Int32Array& codewords,
    CFX_Int32Array& erasureArray,
    CFX_Int32Array& ambiguousIndexes,
    CFX_PtrArray& ambiguousIndexValues,
    int32_t& e) {
  CFX_Int32Array ambiguousIndexCount;
  ambiguousIndexCount.SetSize(ambiguousIndexes.GetSize());
  int32_t tries = 100;
  while (tries-- > 0) {
    for (int32_t l = 0; l < ambiguousIndexCount.GetSize(); l++) {
      codewords[ambiguousIndexes[l]] =
          ((CFX_Int32Array*)ambiguousIndexValues.GetAt(l))
              ->GetAt(ambiguousIndexCount[l]);
    }
    CBC_CommonDecoderResult* decoderResult =
        decodeCodewords(codewords, ecLevel, erasureArray, e);
    if (e != BCExceptionNO) {
      e = BCExceptionNO;
      continue;
    } else {
      return decoderResult;
    }
    if (ambiguousIndexCount.GetSize() == 0) {
      e = BCExceptionChecksumInstance;
      return NULL;
    }
    for (int32_t i = 0; i < ambiguousIndexCount.GetSize(); i++) {
      if (ambiguousIndexCount[i] <
          ((CFX_Int32Array*)(ambiguousIndexValues.GetAt(i)))->GetSize() - 1) {
        ambiguousIndexCount[i]++;
        break;
      } else {
        ambiguousIndexCount[i] = 0;
        if (i == ambiguousIndexCount.GetSize() - 1) {
          e = BCExceptionChecksumInstance;
          return NULL;
        }
      }
    }
  }
  e = BCExceptionChecksumInstance;
  return NULL;
}
CFX_PtrArray* CBC_PDF417ScanningDecoder::createBarcodeMatrix(
    CBC_DetectionResult* detectionResult) {
  CFX_PtrArray* barcodeMatrix = new CFX_PtrArray;
  barcodeMatrix->SetSize(detectionResult->getBarcodeRowCount());
  CFX_PtrArray* temp = NULL;
  int32_t colume = 0;
  for (int32_t row = 0; row < barcodeMatrix->GetSize(); row++) {
    temp = new CFX_PtrArray;
    temp->SetSize(detectionResult->getBarcodeColumnCount() + 2);
    for (colume = 0; colume < detectionResult->getBarcodeColumnCount() + 2;
         colume++) {
      temp->SetAt(colume, new CBC_BarcodeValue());
    }
    barcodeMatrix->SetAt(row, temp);
  }
  colume = -1;
  for (int32_t i = 0;
       i < detectionResult->getDetectionResultColumns().GetSize(); i++) {
    CBC_DetectionResultColumn* detectionResultColumn =
        (CBC_DetectionResultColumn*)detectionResult->getDetectionResultColumns()
            .GetAt(i);
    colume++;
    if (detectionResultColumn == NULL) {
      continue;
    }
    CFX_PtrArray* temp = detectionResultColumn->getCodewords();
    for (int32_t l = 0; l < temp->GetSize(); l++) {
      CBC_Codeword* codeword = (CBC_Codeword*)temp->GetAt(l);
      if (codeword == NULL || codeword->getRowNumber() == -1) {
        continue;
      }
      ((CBC_BarcodeValue*)((CFX_PtrArray*)barcodeMatrix->GetAt(
                               codeword->getRowNumber()))
           ->GetAt(colume))
          ->setValue(codeword->getValue());
    }
  }
  return barcodeMatrix;
}
FX_BOOL CBC_PDF417ScanningDecoder::isValidBarcodeColumn(
    CBC_DetectionResult* detectionResult,
    int32_t barcodeColumn) {
  return barcodeColumn >= 0 &&
         barcodeColumn <= detectionResult->getBarcodeColumnCount() + 1;
}
int32_t CBC_PDF417ScanningDecoder::getStartColumn(
    CBC_DetectionResult* detectionResult,
    int32_t barcodeColumn,
    int32_t imageRow,
    FX_BOOL leftToRight) {
  int32_t offset = leftToRight ? 1 : -1;
  CBC_Codeword* codeword = NULL;
  if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) {
    codeword = detectionResult->getDetectionResultColumn(barcodeColumn - offset)
                   ->getCodeword(imageRow);
  }
  if (codeword != NULL) {
    return leftToRight ? codeword->getEndX() : codeword->getStartX();
  }
  codeword = detectionResult->getDetectionResultColumn(barcodeColumn)
                 ->getCodewordNearby(imageRow);
  if (codeword != NULL) {
    return leftToRight ? codeword->getStartX() : codeword->getEndX();
  }
  if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) {
    codeword = detectionResult->getDetectionResultColumn(barcodeColumn - offset)
                   ->getCodewordNearby(imageRow);
  }
  if (codeword != NULL) {
    return leftToRight ? codeword->getEndX() : codeword->getStartX();
  }
  int32_t skippedColumns = 0;
  while (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) {
    barcodeColumn -= offset;
    for (int32_t i = 0;
         i < detectionResult->getDetectionResultColumn(barcodeColumn)
                 ->getCodewords()
                 ->GetSize();
         i++) {
      CBC_Codeword* previousRowCodeword =
          (CBC_Codeword*)detectionResult->getDetectionResultColumn(
                                            barcodeColumn)
              ->getCodewords()
              ->GetAt(i);
      if (previousRowCodeword != NULL) {
        return (leftToRight ? previousRowCodeword->getEndX()
                            : previousRowCodeword->getStartX()) +
               offset * skippedColumns * (previousRowCodeword->getEndX() -
                                          previousRowCodeword->getStartX());
      }
    }
    skippedColumns++;
  }
  return leftToRight ? detectionResult->getBoundingBox()->getMinX()
                     : detectionResult->getBoundingBox()->getMaxX();
}
CBC_Codeword* CBC_PDF417ScanningDecoder::detectCodeword(
    CBC_CommonBitMatrix* image,
    int32_t minColumn,
    int32_t maxColumn,
    FX_BOOL leftToRight,
    int32_t startColumn,
    int32_t imageRow,
    int32_t minCodewordWidth,
    int32_t maxCodewordWidth) {
  startColumn = adjustCodewordStartColumn(image, minColumn, maxColumn,
                                          leftToRight, startColumn, imageRow);
  CFX_Int32Array* moduleBitCount = getModuleBitCount(
      image, minColumn, maxColumn, leftToRight, startColumn, imageRow);
  if (moduleBitCount == NULL) {
    return NULL;
  }
  int32_t endColumn;
  int32_t codewordBitCount = CBC_PDF417Common::getBitCountSum(*moduleBitCount);
  if (leftToRight) {
    endColumn = startColumn + codewordBitCount;
  } else {
    for (int32_t i = 0; i<moduleBitCount->GetSize()>> 1; i++) {
      int32_t tmpCount = moduleBitCount->GetAt(i);
      moduleBitCount->SetAt(
          i, moduleBitCount->GetAt(moduleBitCount->GetSize() - 1 - i));
      moduleBitCount->SetAt(moduleBitCount->GetSize() - 1 - i, tmpCount);
    }
    endColumn = startColumn;
    startColumn = endColumn - codewordBitCount;
  }
  int32_t decodedValue =
      CBC_PDF417CodewordDecoder::getDecodedValue(*moduleBitCount);
  int32_t codeword = CBC_PDF417Common::getCodeword(decodedValue);
  delete moduleBitCount;
  if (codeword == -1) {
    return NULL;
  }
  return new CBC_Codeword(startColumn, endColumn,
                          getCodewordBucketNumber(decodedValue), codeword);
}
CFX_Int32Array* CBC_PDF417ScanningDecoder::getModuleBitCount(
    CBC_CommonBitMatrix* image,
    int32_t minColumn,
    int32_t maxColumn,
    FX_BOOL leftToRight,
    int32_t startColumn,
    int32_t imageRow) {
  int32_t imageColumn = startColumn;
  CFX_Int32Array* moduleBitCount = new CFX_Int32Array;
  moduleBitCount->SetSize(8);
  int32_t moduleNumber = 0;
  int32_t increment = leftToRight ? 1 : -1;
  FX_BOOL previousPixelValue = leftToRight;
  while (((leftToRight && imageColumn < maxColumn) ||
          (!leftToRight && imageColumn >= minColumn)) &&
         moduleNumber < moduleBitCount->GetSize()) {
    if (image->Get(imageColumn, imageRow) == previousPixelValue) {
      moduleBitCount->SetAt(moduleNumber,
                            moduleBitCount->GetAt(moduleNumber) + 1);
      imageColumn += increment;
    } else {
      moduleNumber++;
      previousPixelValue = !previousPixelValue;
    }
  }
  if (moduleNumber == moduleBitCount->GetSize() ||
      (((leftToRight && imageColumn == maxColumn) ||
        (!leftToRight && imageColumn == minColumn)) &&
       moduleNumber == moduleBitCount->GetSize() - 1)) {
    return moduleBitCount;
  }
  delete moduleBitCount;
  return NULL;
}
int32_t CBC_PDF417ScanningDecoder::getNumberOfECCodeWords(
    int32_t barcodeECLevel) {
  return 2 << barcodeECLevel;
}
int32_t CBC_PDF417ScanningDecoder::adjustCodewordStartColumn(
    CBC_CommonBitMatrix* image,
    int32_t minColumn,
    int32_t maxColumn,
    FX_BOOL leftToRight,
    int32_t codewordStartColumn,
    int32_t imageRow) {
  int32_t correctedStartColumn = codewordStartColumn;
  int32_t increment = leftToRight ? -1 : 1;
  for (int32_t i = 0; i < 2; i++) {
    while (((leftToRight && correctedStartColumn >= minColumn) ||
            (!leftToRight && correctedStartColumn < maxColumn)) &&
           leftToRight == image->Get(correctedStartColumn, imageRow)) {
      if (abs(codewordStartColumn - correctedStartColumn) >
          CODEWORD_SKEW_SIZE) {
        return codewordStartColumn;
      }
      correctedStartColumn += increment;
    }
    increment = -increment;
    leftToRight = !leftToRight;
  }
  return correctedStartColumn;
}
FX_BOOL CBC_PDF417ScanningDecoder::checkCodewordSkew(int32_t codewordSize,
                                                     int32_t minCodewordWidth,
                                                     int32_t maxCodewordWidth) {
  return minCodewordWidth - CODEWORD_SKEW_SIZE <= codewordSize &&
         codewordSize <= maxCodewordWidth + CODEWORD_SKEW_SIZE;
}
CBC_CommonDecoderResult* CBC_PDF417ScanningDecoder::decodeCodewords(
    CFX_Int32Array& codewords,
    int32_t ecLevel,
    CFX_Int32Array& erasures,
    int32_t& e) {
  if (codewords.GetSize() == 0) {
    e = BCExceptionFormatInstance;
    return NULL;
  }
  int32_t numECCodewords = 1 << (ecLevel + 1);
  correctErrors(codewords, erasures, numECCodewords, e);
  BC_EXCEPTION_CHECK_ReturnValue(e, NULL);
  verifyCodewordCount(codewords, numECCodewords, e);
  BC_EXCEPTION_CHECK_ReturnValue(e, NULL);
  CFX_ByteString bytestring;
  CBC_CommonDecoderResult* decoderResult = CBC_DecodedBitStreamPaser::decode(
      codewords, bytestring.FormatInteger(ecLevel), e);
  BC_EXCEPTION_CHECK_ReturnValue(e, NULL);
  return decoderResult;
}
int32_t CBC_PDF417ScanningDecoder::correctErrors(CFX_Int32Array& codewords,
                                                 CFX_Int32Array& erasures,
                                                 int32_t numECCodewords,
                                                 int32_t& e) {
  if ((erasures.GetSize() != 0 &&
       erasures.GetSize() > (numECCodewords / 2 + MAX_ERRORS)) ||
      numECCodewords < 0 || numECCodewords > MAX_EC_CODEWORDS) {
    e = BCExceptionChecksumInstance;
    return -1;
  }
  int32_t result = CBC_PDF417ECErrorCorrection::decode(
      codewords, numECCodewords, erasures, e);
  BC_EXCEPTION_CHECK_ReturnValue(e, -1);
  return result;
}
void CBC_PDF417ScanningDecoder::verifyCodewordCount(CFX_Int32Array& codewords,
                                                    int32_t numECCodewords,
                                                    int32_t& e) {
  if (codewords.GetSize() < 4) {
    e = BCExceptionFormatInstance;
    return;
  }
  int32_t numberOfCodewords = codewords.GetAt(0);
  if (numberOfCodewords > codewords.GetSize()) {
    e = BCExceptionFormatInstance;
    return;
  }
  if (numberOfCodewords == 0) {
    if (numECCodewords < codewords.GetSize()) {
      codewords[0] = codewords.GetSize() - numECCodewords;
    } else {
      e = BCExceptionFormatInstance;
      return;
    }
  }
}
CFX_Int32Array* CBC_PDF417ScanningDecoder::getBitCountForCodeword(
    int32_t codeword) {
  CFX_Int32Array* result = new CFX_Int32Array;
  result->SetSize(8);
  int32_t previousValue = 0;
  int32_t i = result->GetSize() - 1;
  while (TRUE) {
    if ((codeword & 0x1) != previousValue) {
      previousValue = codeword & 0x1;
      i--;
      if (i < 0) {
        break;
      }
    }
    result->SetAt(i, result->GetAt(i) + 1);
    codeword >>= 1;
  }
  return result;
}
int32_t CBC_PDF417ScanningDecoder::getCodewordBucketNumber(int32_t codeword) {
  CFX_Int32Array* array = getBitCountForCodeword(codeword);
  int32_t result = getCodewordBucketNumber(*array);
  delete array;
  return result;
}
int32_t CBC_PDF417ScanningDecoder::getCodewordBucketNumber(
    CFX_Int32Array& moduleBitCount) {
  return (moduleBitCount.GetAt(0) - moduleBitCount.GetAt(2) +
          moduleBitCount.GetAt(4) - moduleBitCount.GetAt(6) + 9) %
         9;
}