content: /*
   * Copyright 2008 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.oned;
  
  
  import com.ailiwean.core.zxing.core.BarcodeFormat;
  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.NotFoundException;
  import com.ailiwean.core.zxing.core.Result;
  import com.ailiwean.core.zxing.core.ResultPoint;
  import com.ailiwean.core.zxing.core.common.BitArray;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Map;
  
  /**
   * <p>Decodes Code 128 barcodes.</p>
   *
   * @author Sean Owen
   */
  public final class Code128Reader extends OneDReader {
  
    static final int[][] CODE_PATTERNS = {
        {2, 1, 2, 2, 2, 2}, // 0
        {2, 2, 2, 1, 2, 2},
        {2, 2, 2, 2, 2, 1},
        {1, 2, 1, 2, 2, 3},
        {1, 2, 1, 3, 2, 2},
        {1, 3, 1, 2, 2, 2}, // 5
        {1, 2, 2, 2, 1, 3},
        {1, 2, 2, 3, 1, 2},
        {1, 3, 2, 2, 1, 2},
        {2, 2, 1, 2, 1, 3},
        {2, 2, 1, 3, 1, 2}, // 10
        {2, 3, 1, 2, 1, 2},
        {1, 1, 2, 2, 3, 2},
        {1, 2, 2, 1, 3, 2},
        {1, 2, 2, 2, 3, 1},
        {1, 1, 3, 2, 2, 2}, // 15
        {1, 2, 3, 1, 2, 2},
        {1, 2, 3, 2, 2, 1},
        {2, 2, 3, 2, 1, 1},
        {2, 2, 1, 1, 3, 2},
        {2, 2, 1, 2, 3, 1}, // 20
        {2, 1, 3, 2, 1, 2},
        {2, 2, 3, 1, 1, 2},
        {3, 1, 2, 1, 3, 1},
        {3, 1, 1, 2, 2, 2},
        {3, 2, 1, 1, 2, 2}, // 25
        {3, 2, 1, 2, 2, 1},
        {3, 1, 2, 2, 1, 2},
        {3, 2, 2, 1, 1, 2},
        {3, 2, 2, 2, 1, 1},
        {2, 1, 2, 1, 2, 3}, // 30
        {2, 1, 2, 3, 2, 1},
        {2, 3, 2, 1, 2, 1},
        {1, 1, 1, 3, 2, 3},
        {1, 3, 1, 1, 2, 3},
        {1, 3, 1, 3, 2, 1}, // 35
        {1, 1, 2, 3, 1, 3},
        {1, 3, 2, 1, 1, 3},
        {1, 3, 2, 3, 1, 1},
        {2, 1, 1, 3, 1, 3},
        {2, 3, 1, 1, 1, 3}, // 40
        {2, 3, 1, 3, 1, 1},
        {1, 1, 2, 1, 3, 3},
        {1, 1, 2, 3, 3, 1},
        {1, 3, 2, 1, 3, 1},
        {1, 1, 3, 1, 2, 3}, // 45
        {1, 1, 3, 3, 2, 1},
        {1, 3, 3, 1, 2, 1},
        {3, 1, 3, 1, 2, 1},
        {2, 1, 1, 3, 3, 1},
        {2, 3, 1, 1, 3, 1}, // 50
        {2, 1, 3, 1, 1, 3},
        {2, 1, 3, 3, 1, 1},
        {2, 1, 3, 1, 3, 1},
        {3, 1, 1, 1, 2, 3},
        {3, 1, 1, 3, 2, 1}, // 55
        {3, 3, 1, 1, 2, 1},
        {3, 1, 2, 1, 1, 3},
        {3, 1, 2, 3, 1, 1},
        {3, 3, 2, 1, 1, 1},
        {3, 1, 4, 1, 1, 1}, // 60
        {2, 2, 1, 4, 1, 1},
        {4, 3, 1, 1, 1, 1},
        {1, 1, 1, 2, 2, 4},
        {1, 1, 1, 4, 2, 2},
        {1, 2, 1, 1, 2, 4}, // 65
        {1, 2, 1, 4, 2, 1},
        {1, 4, 1, 1, 2, 2},
        {1, 4, 1, 2, 2, 1},
        {1, 1, 2, 2, 1, 4},
        {1, 1, 2, 4, 1, 2}, // 70
        {1, 2, 2, 1, 1, 4},
        {1, 2, 2, 4, 1, 1},
        {1, 4, 2, 1, 1, 2},
        {1, 4, 2, 2, 1, 1},
        {2, 4, 1, 2, 1, 1}, // 75
        {2, 2, 1, 1, 1, 4},
        {4, 1, 3, 1, 1, 1},
        {2, 4, 1, 1, 1, 2},
        {1, 3, 4, 1, 1, 1},
        {1, 1, 1, 2, 4, 2}, // 80
        {1, 2, 1, 1, 4, 2},
        {1, 2, 1, 2, 4, 1},
        {1, 1, 4, 2, 1, 2},
        {1, 2, 4, 1, 1, 2},
        {1, 2, 4, 2, 1, 1}, // 85
        {4, 1, 1, 2, 1, 2},
        {4, 2, 1, 1, 1, 2},
        {4, 2, 1, 2, 1, 1},
        {2, 1, 2, 1, 4, 1},
        {2, 1, 4, 1, 2, 1}, // 90
        {4, 1, 2, 1, 2, 1},
        {1, 1, 1, 1, 4, 3},
        {1, 1, 1, 3, 4, 1},
        {1, 3, 1, 1, 4, 1},
        {1, 1, 4, 1, 1, 3}, // 95
        {1, 1, 4, 3, 1, 1},
        {4, 1, 1, 1, 1, 3},
        {4, 1, 1, 3, 1, 1},
        {1, 1, 3, 1, 4, 1},
        {1, 1, 4, 1, 3, 1}, // 100
        {3, 1, 1, 1, 4, 1},
        {4, 1, 1, 1, 3, 1},
        {2, 1, 1, 4, 1, 2},
        {2, 1, 1, 2, 1, 4},
        {2, 1, 1, 2, 3, 2}, // 105
        {2, 3, 3, 1, 1, 1, 2}
    };
  
    private static final float MAX_AVG_VARIANCE = 0.25f;
    private static final float MAX_INDIVIDUAL_VARIANCE = 0.7f;
  
    private static final int CODE_SHIFT = 98;
  
    private static final int CODE_CODE_C = 99;
    private static final int CODE_CODE_B = 100;
    private static final int CODE_CODE_A = 101;
  
    private static final int CODE_FNC_1 = 102;
    private static final int CODE_FNC_2 = 97;
    private static final int CODE_FNC_3 = 96;
    private static final int CODE_FNC_4_A = 101;
    private static final int CODE_FNC_4_B = 100;
  
    private static final int CODE_START_A = 103;
    private static final int CODE_START_B = 104;
    private static final int CODE_START_C = 105;
    private static final int CODE_STOP = 106;
  
    private static int[] findStartPattern(BitArray row) throws NotFoundException {
      int width = row.getSize();
      int rowOffset = row.getNextSet(0);
  
      int counterPosition = 0;
      int[] counters = new int[6];
      int patternStart = rowOffset;
      boolean isWhite = false;
      int patternLength = counters.length;
  
      for (int i = rowOffset; i < width; i++) {
        if (row.get(i) != isWhite) {
          counters[counterPosition]++;
        } else {
          if (counterPosition == patternLength - 1) {
            float bestVariance = MAX_AVG_VARIANCE;
            int bestMatch = -1;
            for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
              float variance = patternMatchVariance(counters, CODE_PATTERNS[startCode],
                  MAX_INDIVIDUAL_VARIANCE);
              if (variance < bestVariance) {
                bestVariance = variance;
                bestMatch = startCode;
              }
            }
            // Look for whitespace before start pattern, >= 50% of width of start pattern
            if (bestMatch >= 0 &&
                row.isRange(Math.max(0, patternStart - (i - patternStart) / 2), patternStart, false)) {
              return new int[]{patternStart, i, bestMatch};
            }
            patternStart += counters[0] + counters[1];
            System.arraycopy(counters, 2, counters, 0, counterPosition - 1);
            counters[counterPosition - 1] = 0;
            counters[counterPosition] = 0;
            counterPosition--;
          } else {
            counterPosition++;
          }
          counters[counterPosition] = 1;
          isWhite = !isWhite;
        }
      }
      throw NotFoundException.getNotFoundInstance();
    }
  
    private static int decodeCode(BitArray row, int[] counters, int rowOffset)
        throws NotFoundException {
      recordPattern(row, rowOffset, counters);
      float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
      int bestMatch = -1;
      for (int d = 0; d < CODE_PATTERNS.length; d++) {
        int[] pattern = CODE_PATTERNS[d];
        float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
        if (variance < bestVariance) {
          bestVariance = variance;
          bestMatch = d;
        }
      }
      // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6.
      if (bestMatch >= 0) {
        return bestMatch;
      } else {
        throw NotFoundException.getNotFoundInstance();
      }
    }
  
    @Override
    public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)
        throws NotFoundException, FormatException, ChecksumException {
  
      boolean convertFNC1 = hints != null && hints.containsKey(DecodeHintType.ASSUME_GS1);
  
      int[] startPatternInfo = findStartPattern(row);
      int startCode = startPatternInfo[2];
  
      List<Byte> rawCodes = new ArrayList<>(20);
      rawCodes.add((byte) startCode);
  
      int codeSet;
      switch (startCode) {
        case CODE_START_A:
          codeSet = CODE_CODE_A;
          break;
        case CODE_START_B:
          codeSet = CODE_CODE_B;
          break;
        case CODE_START_C:
          codeSet = CODE_CODE_C;
          break;
        default:
          throw FormatException.getFormatInstance();
      }
  
      boolean done = false;
      boolean isNextShifted = false;
  
      StringBuilder result = new StringBuilder(20);
  
      int lastStart = startPatternInfo[0];
      int nextStart = startPatternInfo[1];
      int[] counters = new int[6];
  
      int lastCode = 0;
      int code = 0;
      int checksumTotal = startCode;
      int multiplier = 0;
      boolean lastCharacterWasPrintable = true;
      boolean upperMode = false;
      boolean shiftUpperMode = false;
  
      while (!done) {
  
        boolean unshift = isNextShifted;
        isNextShifted = false;
  
        // Save off last code
        lastCode = code;
  
        // Decode another code from image
        code = decodeCode(row, counters, nextStart);
  
        rawCodes.add((byte) code);
  
        // Remember whether the last code was printable or not (excluding CODE_STOP)
        if (code != CODE_STOP) {
          lastCharacterWasPrintable = true;
        }
  
        // Add to checksum computation (if not CODE_STOP of course)
        if (code != CODE_STOP) {
          multiplier++;
          checksumTotal += multiplier * code;
        }
  
        // Advance to where the next code will to start
        lastStart = nextStart;
        for (int counter : counters) {
          nextStart += counter;
        }
  
        // Take care of illegal start codes
        switch (code) {
          case CODE_START_A:
          case CODE_START_B:
          case CODE_START_C:
            throw FormatException.getFormatInstance();
        }
  
        switch (codeSet) {
  
          case CODE_CODE_A:
            if (code < 64) {
              if (shiftUpperMode == upperMode) {
                result.append((char) (' ' + code));
              } else {
                result.append((char) (' ' + code + 128));
              }
              shiftUpperMode = false;
            } else if (code < 96) {
              if (shiftUpperMode == upperMode) {
                result.append((char) (code - 64));
              } else {
                result.append((char) (code + 64));
              }
              shiftUpperMode = false;
            } else {
              // Don't let CODE_STOP, which always appears, affect whether whether we think the last
              // code was printable or not.
              if (code != CODE_STOP) {
                lastCharacterWasPrintable = false;
              }
              switch (code) {
                case CODE_FNC_1:
                  if (convertFNC1) {
                    if (result.length() == 0) {
                      // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
                      // is FNC1 then this is GS1-128. We add the symbology identifier.
                      result.append("]C1");
                    } else {
                      // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
                      result.append((char) 29);
                    }
                  }
                  break;
                case CODE_FNC_2:
                case CODE_FNC_3:
                  // do nothing?
                  break;
                case CODE_FNC_4_A:
                  if (!upperMode && shiftUpperMode) {
                    upperMode = true;
                    shiftUpperMode = false;
                  } else if (upperMode && shiftUpperMode) {
                    upperMode = false;
                    shiftUpperMode = false;
                  } else {
                    shiftUpperMode = true;
                  }
                  break;
                case CODE_SHIFT:
                  isNextShifted = true;
                  codeSet = CODE_CODE_B;
                  break;
                case CODE_CODE_B:
                  codeSet = CODE_CODE_B;
                  break;
                case CODE_CODE_C:
                  codeSet = CODE_CODE_C;
                  break;
                case CODE_STOP:
                  done = true;
                  break;
              }
            }
            break;
          case CODE_CODE_B:
            if (code < 96) {
              if (shiftUpperMode == upperMode) {
                result.append((char) (' ' + code));
              } else {
                result.append((char) (' ' + code + 128));
              }
              shiftUpperMode = false;
            } else {
              if (code != CODE_STOP) {
                lastCharacterWasPrintable = false;
              }
              switch (code) {
                case CODE_FNC_1:
                  if (convertFNC1) {
                    if (result.length() == 0) {
                      // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
                      // is FNC1 then this is GS1-128. We add the symbology identifier.
                      result.append("]C1");
                    } else {
                      // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
                      result.append((char) 29);
                    }
                  }
                  break;
                case CODE_FNC_2:
                case CODE_FNC_3:
                  // do nothing?
                  break;
                case CODE_FNC_4_B:
                  if (!upperMode && shiftUpperMode) {
                    upperMode = true;
                    shiftUpperMode = false;
                  } else if (upperMode && shiftUpperMode) {
                    upperMode = false;
                    shiftUpperMode = false;
                  } else {
                    shiftUpperMode = true;
                  }
                  break;
                case CODE_SHIFT:
                  isNextShifted = true;
                  codeSet = CODE_CODE_A;
                  break;
                case CODE_CODE_A:
                  codeSet = CODE_CODE_A;
                  break;
                case CODE_CODE_C:
                  codeSet = CODE_CODE_C;
                  break;
                case CODE_STOP:
                  done = true;
                  break;
              }
            }
            break;
          case CODE_CODE_C:
            if (code < 100) {
              if (code < 10) {
                result.append('0');
              }
              result.append(code);
            } else {
              if (code != CODE_STOP) {
                lastCharacterWasPrintable = false;
              }
              switch (code) {
                case CODE_FNC_1:
                  if (convertFNC1) {
                    if (result.length() == 0) {
                      // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
                      // is FNC1 then this is GS1-128. We add the symbology identifier.
                      result.append("]C1");
                    } else {
                      // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
                      result.append((char) 29);
                    }
                  }
                  break;
                case CODE_CODE_A:
                  codeSet = CODE_CODE_A;
                  break;
                case CODE_CODE_B:
                  codeSet = CODE_CODE_B;
                  break;
                case CODE_STOP:
                  done = true;
                  break;
              }
            }
            break;
        }
  
        // Unshift back to another code set if we were shifted
        if (unshift) {
          codeSet = codeSet == CODE_CODE_A ? CODE_CODE_B : CODE_CODE_A;
        }
  
      }
  
      int lastPatternSize = nextStart - lastStart;
  
      // Check for ample whitespace following pattern, but, to do this we first need to remember that
      // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left
      // to read off. Would be slightly better to properly read. Here we just skip it:
      nextStart = row.getNextUnset(nextStart);
      if (!row.isRange(nextStart,
                       Math.min(row.getSize(), nextStart + (nextStart - lastStart) / 2),
                       false)) {
        throw NotFoundException.getNotFoundInstance();
      }
  
      // Pull out from sum the value of the penultimate check code
      checksumTotal -= multiplier * lastCode;
      // lastCode is the checksum then:
      if (checksumTotal % 103 != lastCode) {
        throw ChecksumException.getChecksumInstance();
      }
  
      // Need to pull out the check digits from string
      int resultLength = result.length();
      if (resultLength == 0) {
        // false positive
        throw NotFoundException.getNotFoundInstance();
      }
  
      // Only bother if the result had at least one character, and if the checksum digit happened to
      // be a printable character. If it was just interpreted as a control code, nothing to remove.
      if (resultLength > 0 && lastCharacterWasPrintable) {
        if (codeSet == CODE_CODE_C) {
          result.delete(resultLength - 2, resultLength);
        } else {
          result.delete(resultLength - 1, resultLength);
        }
      }
  
      float left = (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;
      float right = lastStart + lastPatternSize / 2.0f;
  
      int rawCodesSize = rawCodes.size();
      byte[] rawBytes = new byte[rawCodesSize];
      for (int i = 0; i < rawCodesSize; i++) {
        rawBytes[i] = rawCodes.get(i);
      }
  
      return new Result(
          result.toString(),
          rawBytes,
          new ResultPoint[]{
              new ResultPoint(left, rowNumber),
              new ResultPoint(right, rowNumber)},
          BarcodeFormat.CODE_128);
  
    }
  
  }
  