content: /*
   * 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.
   */
  
  package com.ailiwean.core.zxing.core.pdf417.decoder;
  
  
  import com.ailiwean.core.zxing.core.pdf417.PDF417Common;
  
  import java.util.Formatter;
  
  /**
   * @author Guenther Grau
   */
  final class DetectionResult {
  
    private static final int ADJUST_ROW_NUMBER_SKIP = 2;
  
    private final BarcodeMetadata barcodeMetadata;
    private final DetectionResultColumn[] detectionResultColumns;
    private BoundingBox boundingBox;
    private final int barcodeColumnCount;
  
    DetectionResult(BarcodeMetadata barcodeMetadata, BoundingBox boundingBox) {
      this.barcodeMetadata = barcodeMetadata;
      this.barcodeColumnCount = barcodeMetadata.getColumnCount();
      this.boundingBox = boundingBox;
      detectionResultColumns = new DetectionResultColumn[barcodeColumnCount + 2];
    }
  
    DetectionResultColumn[] getDetectionResultColumns() {
      adjustIndicatorColumnRowNumbers(detectionResultColumns[0]);
      adjustIndicatorColumnRowNumbers(detectionResultColumns[barcodeColumnCount + 1]);
      int unadjustedCodewordCount = PDF417Common.MAX_CODEWORDS_IN_BARCODE;
      int previousUnadjustedCount;
      do {
        previousUnadjustedCount = unadjustedCodewordCount;
        unadjustedCodewordCount = adjustRowNumbers();
      } while (unadjustedCodewordCount > 0 && unadjustedCodewordCount < previousUnadjustedCount);
      return detectionResultColumns;
    }
  
    private void adjustIndicatorColumnRowNumbers(DetectionResultColumn detectionResultColumn) {
      if (detectionResultColumn != null) {
        ((DetectionResultRowIndicatorColumn) detectionResultColumn)
            .adjustCompleteIndicatorColumnRowNumbers(barcodeMetadata);
      }
    }
  
    // TODO ensure that no detected codewords with unknown row number are left
    // we should be able to estimate the row height and use it as a hint for the row number
    // we should also fill the rows top to bottom and bottom to top
    /**
     * @return number of codewords which don't have a valid row number. Note that the count is not accurate as codewords
     * will be counted several times. It just serves as an indicator to see when we can stop adjusting row numbers
     */
    private int adjustRowNumbers() {
      int unadjustedCount = adjustRowNumbersByRow();
      if (unadjustedCount == 0) {
        return 0;
      }
      for (int barcodeColumn = 1; barcodeColumn < barcodeColumnCount + 1; barcodeColumn++) {
        Codeword[] codewords = detectionResultColumns[barcodeColumn].getCodewords();
        for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
          if (codewords[codewordsRow] == null) {
            continue;
          }
          if (!codewords[codewordsRow].hasValidRowNumber()) {
            adjustRowNumbers(barcodeColumn, codewordsRow, codewords);
          }
        }
      }
      return unadjustedCount;
    }
  
    private int adjustRowNumbersByRow() {
      adjustRowNumbersFromBothRI();
      // TODO we should only do full row adjustments if row numbers of left and right row indicator column match.
      // Maybe it's even better to calculated the height (in codeword rows) and divide it by the number of barcode
      // rows. This, together with the LRI and RRI row numbers should allow us to get a good estimate where a row
      // number starts and ends.
      int unadjustedCount = adjustRowNumbersFromLRI();
      return unadjustedCount + adjustRowNumbersFromRRI();
    }
  
    private void adjustRowNumbersFromBothRI() {
      if (detectionResultColumns[0] == null || detectionResultColumns[barcodeColumnCount + 1] == null) {
        return;
      }
      Codeword[] LRIcodewords = detectionResultColumns[0].getCodewords();
      Codeword[] RRIcodewords = detectionResultColumns[barcodeColumnCount + 1].getCodewords();
      for (int codewordsRow = 0; codewordsRow < LRIcodewords.length; codewordsRow++) {
        if (LRIcodewords[codewordsRow] != null &&
            RRIcodewords[codewordsRow] != null &&
            LRIcodewords[codewordsRow].getRowNumber() == RRIcodewords[codewordsRow].getRowNumber()) {
          for (int barcodeColumn = 1; barcodeColumn <= barcodeColumnCount; barcodeColumn++) {
            Codeword codeword = detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow];
            if (codeword == null) {
              continue;
            }
            codeword.setRowNumber(LRIcodewords[codewordsRow].getRowNumber());
            if (!codeword.hasValidRowNumber()) {
              detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow] = null;
            }
          }
        }
      }
    }
  
    private int adjustRowNumbersFromRRI() {
      if (detectionResultColumns[barcodeColumnCount + 1] == null) {
        return 0;
      }
      int unadjustedCount = 0;
      Codeword[] codewords = detectionResultColumns[barcodeColumnCount + 1].getCodewords();
      for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
        if (codewords[codewordsRow] == null) {
          continue;
        }
        int rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber();
        int invalidRowCounts = 0;
        for (int barcodeColumn = barcodeColumnCount + 1; barcodeColumn > 0 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP; barcodeColumn--) {
          Codeword codeword = detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow];
          if (codeword != null) {
            invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);
            if (!codeword.hasValidRowNumber()) {
              unadjustedCount++;
            }
          }
        }
      }
      return unadjustedCount;
    }
  
    private int adjustRowNumbersFromLRI() {
      if (detectionResultColumns[0] == null) {
        return 0;
      }
      int unadjustedCount = 0;
      Codeword[] codewords = detectionResultColumns[0].getCodewords();
      for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
        if (codewords[codewordsRow] == null) {
          continue;
        }
        int rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber();
        int invalidRowCounts = 0;
        for (int barcodeColumn = 1; barcodeColumn < barcodeColumnCount + 1 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP; barcodeColumn++) {
          Codeword codeword = detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow];
          if (codeword != null) {
            invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);
            if (!codeword.hasValidRowNumber()) {
              unadjustedCount++;
            }
          }
        }
      }
      return unadjustedCount;
    }
  
    private static int adjustRowNumberIfValid(int rowIndicatorRowNumber, int invalidRowCounts, Codeword codeword) {
      if (codeword == null) {
        return invalidRowCounts;
      }
      if (!codeword.hasValidRowNumber()) {
        if (codeword.isValidRowNumber(rowIndicatorRowNumber)) {
          codeword.setRowNumber(rowIndicatorRowNumber);
          invalidRowCounts = 0;
        } else {
          ++invalidRowCounts;
        }
      }
      return invalidRowCounts;
    }
  
    private void adjustRowNumbers(int barcodeColumn, int codewordsRow, Codeword[] codewords) {
      Codeword codeword = codewords[codewordsRow];
      Codeword[] previousColumnCodewords = detectionResultColumns[barcodeColumn - 1].getCodewords();
      Codeword[] nextColumnCodewords = previousColumnCodewords;
      if (detectionResultColumns[barcodeColumn + 1] != null) {
        nextColumnCodewords = detectionResultColumns[barcodeColumn + 1].getCodewords();
      }
  
      Codeword[] otherCodewords = new Codeword[14];
  
      otherCodewords[2] = previousColumnCodewords[codewordsRow];
      otherCodewords[3] = nextColumnCodewords[codewordsRow];
  
      if (codewordsRow > 0) {
        otherCodewords[0] = codewords[codewordsRow - 1];
        otherCodewords[4] = previousColumnCodewords[codewordsRow - 1];
        otherCodewords[5] = nextColumnCodewords[codewordsRow - 1];
      }
      if (codewordsRow > 1) {
        otherCodewords[8] = codewords[codewordsRow - 2];
        otherCodewords[10] = previousColumnCodewords[codewordsRow - 2];
        otherCodewords[11] = nextColumnCodewords[codewordsRow - 2];
      }
      if (codewordsRow < codewords.length - 1) {
        otherCodewords[1] = codewords[codewordsRow + 1];
        otherCodewords[6] = previousColumnCodewords[codewordsRow + 1];
        otherCodewords[7] = nextColumnCodewords[codewordsRow + 1];
      }
      if (codewordsRow < codewords.length - 2) {
        otherCodewords[9] = codewords[codewordsRow + 2];
        otherCodewords[12] = previousColumnCodewords[codewordsRow + 2];
        otherCodewords[13] = nextColumnCodewords[codewordsRow + 2];
      }
      for (Codeword otherCodeword : otherCodewords) {
        if (adjustRowNumber(codeword, otherCodeword)) {
          return;
        }
      }
    }
  
    /**
     * @return true, if row number was adjusted, false otherwise
     */
    private static boolean adjustRowNumber(Codeword codeword, Codeword otherCodeword) {
      if (otherCodeword == null) {
        return false;
      }
      if (otherCodeword.hasValidRowNumber() && otherCodeword.getBucket() == codeword.getBucket()) {
        codeword.setRowNumber(otherCodeword.getRowNumber());
        return true;
      }
      return false;
    }
  
    int getBarcodeColumnCount() {
      return barcodeColumnCount;
    }
  
    int getBarcodeRowCount() {
      return barcodeMetadata.getRowCount();
    }
  
    int getBarcodeECLevel() {
      return barcodeMetadata.getErrorCorrectionLevel();
    }
  
    void setBoundingBox(BoundingBox boundingBox) {
      this.boundingBox = boundingBox;
    }
  
    BoundingBox getBoundingBox() {
      return boundingBox;
    }
  
    void setDetectionResultColumn(int barcodeColumn, DetectionResultColumn detectionResultColumn) {
      detectionResultColumns[barcodeColumn] = detectionResultColumn;
    }
  
    DetectionResultColumn getDetectionResultColumn(int barcodeColumn) {
      return detectionResultColumns[barcodeColumn];
    }
  
    @Override
    public String toString() {
      DetectionResultColumn rowIndicatorColumn = detectionResultColumns[0];
      if (rowIndicatorColumn == null) {
        rowIndicatorColumn = detectionResultColumns[barcodeColumnCount + 1];
      }
      try (Formatter formatter = new Formatter()) {
        for (int codewordsRow = 0; codewordsRow < rowIndicatorColumn.getCodewords().length; codewordsRow++) {
          formatter.format("CW %3d:", codewordsRow);
          for (int barcodeColumn = 0; barcodeColumn < barcodeColumnCount + 2; barcodeColumn++) {
            if (detectionResultColumns[barcodeColumn] == null) {
              formatter.format("    |   ");
              continue;
            }
            Codeword codeword = detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow];
            if (codeword == null) {
              formatter.format("    |   ");
              continue;
            }
            formatter.format(" %3d|%3d", codeword.getRowNumber(), codeword.getValue());
          }
          formatter.format("%n");
        }
        return formatter.toString();
      }
    }
  
  }
  