content: /*
   * Copyright 2007 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.qrcode.decoder;
  
  import com.ailiwean.core.zxing.core.ChecksumException;
  import com.ailiwean.core.zxing.core.DecodeHintType;
  import com.ailiwean.core.zxing.core.FormatException;
  import com.ailiwean.core.zxing.core.common.BitMatrix;
  import com.ailiwean.core.zxing.core.common.DecoderResult;
  import com.ailiwean.core.zxing.core.common.reedsolomon.GenericGF;
  import com.ailiwean.core.zxing.core.common.reedsolomon.ReedSolomonDecoder;
  import com.ailiwean.core.zxing.core.common.reedsolomon.ReedSolomonException;
  
  import java.util.Map;
  
  /**
   * <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
   * the QR Code from an image.</p>
   *
   * @author Sean Owen
   */
  public final class Decoder {
  
    private final ReedSolomonDecoder rsDecoder;
  
    public Decoder() {
      rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256);
    }
  
    public DecoderResult decode(boolean[][] image) throws ChecksumException, FormatException {
      return decode(image, null);
    }
  
    /**
     * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
     * "true" is taken to mean a black module.</p>
     *
     * @param image booleans representing white/black QR Code modules
     * @param hints decoding hints that should be used to influence decoding
     * @return text and bytes encoded within the QR Code
     * @throws FormatException if the QR Code cannot be decoded
     * @throws ChecksumException if error correction fails
     */
    public DecoderResult decode(boolean[][] image, Map<DecodeHintType,?> hints)
        throws ChecksumException, FormatException {
      return decode(BitMatrix.parse(image), hints);
    }
  
    public DecoderResult decode(BitMatrix bits) throws ChecksumException, FormatException {
      return decode(bits, null);
    }
  
    /**
     * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
     *
     * @param bits booleans representing white/black QR Code modules
     * @param hints decoding hints that should be used to influence decoding
     * @return text and bytes encoded within the QR Code
     * @throws FormatException if the QR Code cannot be decoded
     * @throws ChecksumException if error correction fails
     */
    public DecoderResult decode(BitMatrix bits, Map<DecodeHintType,?> hints)
        throws FormatException, ChecksumException {
  
      // Construct a parser and read version, error-correction level
      BitMatrixParser parser = new BitMatrixParser(bits);
      FormatException fe = null;
      ChecksumException ce = null;
      try {
        return decode(parser, hints);
      } catch (FormatException e) {
        fe = e;
      } catch (ChecksumException e) {
        ce = e;
      }
  
      try {
  
        // Revert the bit matrix
        parser.remask();
  
        // Will be attempting a mirrored reading of the version and format info.
        parser.setMirror(true);
  
        // Preemptively read the version.
        parser.readVersion();
  
        // Preemptively read the format information.
        parser.readFormatInformation();
  
        /*
         * Since we're here, this means we have successfully detected some kind
         * of version and format information when mirrored. This is a good sign,
         * that the QR code may be mirrored, and we should try once more with a
         * mirrored content.
         */
        // Prepare for a mirrored reading.
        parser.mirror();
  
        DecoderResult result = decode(parser, hints);
  
        // Success! Notify the caller that the code was mirrored.
        result.setOther(new QRCodeDecoderMetaData(true));
  
        return result;
  
      } catch (FormatException | ChecksumException e) {
        // Throw the exception from the original reading
        if (fe != null) {
          throw fe;
        }
        throw ce; // If fe is null, this can't be
      }
    }
  
    private DecoderResult decode(BitMatrixParser parser, Map<DecodeHintType,?> hints)
        throws FormatException, ChecksumException {
      Version version = parser.readVersion();
      ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();
  
      // Read codewords
      byte[] codewords = parser.readCodewords();
      // Separate into data blocks
      DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
  
      // Count total number of data bytes
      int totalBytes = 0;
      for (DataBlock dataBlock : dataBlocks) {
        totalBytes += dataBlock.getNumDataCodewords();
      }
      byte[] resultBytes = new byte[totalBytes];
      int resultOffset = 0;
  
      // Error-correct and copy data blocks together into a stream of bytes
      for (DataBlock dataBlock : dataBlocks) {
        byte[] codewordBytes = dataBlock.getCodewords();
        int numDataCodewords = dataBlock.getNumDataCodewords();
        correctErrors(codewordBytes, numDataCodewords);
        for (int i = 0; i < numDataCodewords; i++) {
          resultBytes[resultOffset++] = codewordBytes[i];
        }
      }
  
      // Decode the contents of that stream of bytes
      return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints);
    }
  
    /**
     * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
     * correct the errors in-place using Reed-Solomon error correction.</p>
     *
     * @param codewordBytes data and error correction codewords
     * @param numDataCodewords number of codewords that are data bytes
     * @throws ChecksumException if error correction fails
     */
    private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException {
      int numCodewords = codewordBytes.length;
      // First read into an array of ints
      int[] codewordsInts = new int[numCodewords];
      for (int i = 0; i < numCodewords; i++) {
        codewordsInts[i] = codewordBytes[i] & 0xFF;
      }
      try {
        rsDecoder.decode(codewordsInts, codewordBytes.length - numDataCodewords);
      } catch (ReedSolomonException ignored) {
        throw ChecksumException.getChecksumInstance();
      }
      // Copy back into array of bytes -- only need to worry about the bytes that were data
      // We don't care about errors in the error-correction codewords
      for (int i = 0; i < numDataCodewords; i++) {
        codewordBytes[i] = (byte) codewordsInts[i];
      }
    }
  
  }
  