/*
 * 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.
 */

#import "ZXPDF417CodewordDecoder.h"
#import "ZXPDF417Common.h"

static float ZX_PDF417_RATIOS_TABLE[ZX_PDF417_SYMBOL_TABLE_LEN][ZX_PDF417_BARS_IN_MODULE];

@implementation ZXPDF417CodewordDecoder

+ (void)initialize {
  if ([self class] != [ZXPDF417CodewordDecoder class]) return;

  // Pre-computes the symbol ratio table.
  for (int i = 0; i < ZX_PDF417_SYMBOL_TABLE_LEN; i++) {
    int currentSymbol = ZX_PDF417_SYMBOL_TABLE[i];
    int currentBit = currentSymbol & 0x1;
    for (int j = 0; j < ZX_PDF417_BARS_IN_MODULE; j++) {
      float size = 0.0f;
      while ((currentSymbol & 0x1) == currentBit) {
        size += 1.0f;
        currentSymbol >>= 1;
      }
      currentBit = currentSymbol & 0x1;
      ZX_PDF417_RATIOS_TABLE[i][ZX_PDF417_BARS_IN_MODULE - j - 1] = size / ZX_PDF417_MODULES_IN_CODEWORD;
    }
  }
}

+ (int)decodedValue:(NSArray *)moduleBitCount {
  int decodedValue = [self decodedCodewordValue:[self sampleBitCounts:moduleBitCount]];
  if (decodedValue != -1) {
    return decodedValue;
  }
  return [self closestDecodedValue:moduleBitCount];
}

+ (NSArray *)sampleBitCounts:(NSArray *)moduleBitCount {
  float bitCountSum = [ZXPDF417Common bitCountSum:moduleBitCount];
  NSMutableArray *result = [NSMutableArray arrayWithCapacity:ZX_PDF417_BARS_IN_MODULE];
  for (int i = 0; i < ZX_PDF417_BARS_IN_MODULE; i++) {
    [result addObject:@0];
  }

  int bitCountIndex = 0;
  int sumPreviousBits = 0;
  for (int i = 0; i < ZX_PDF417_MODULES_IN_CODEWORD; i++) {
    float sampleIndex =
      bitCountSum / (2 * ZX_PDF417_MODULES_IN_CODEWORD) +
      (i * bitCountSum) / ZX_PDF417_MODULES_IN_CODEWORD;
    if (sumPreviousBits + [moduleBitCount[bitCountIndex] intValue] <= sampleIndex) {
      sumPreviousBits += [moduleBitCount[bitCountIndex] intValue];
      bitCountIndex++;
    }
    result[bitCountIndex] = @([result[bitCountIndex] intValue] + 1);
  }
  return result;
}

+ (int)decodedCodewordValue:(NSArray *)moduleBitCount {
  int decodedValue = [self bitValue:moduleBitCount];
  return [ZXPDF417Common codeword:decodedValue] == -1 ? -1 : decodedValue;
}

+ (int)bitValue:(NSArray *)moduleBitCount {
  long result = 0;
  for (int i = 0; i < [moduleBitCount count]; i++) {
    for (int bit = 0; bit < [moduleBitCount[i] intValue]; bit++) {
      result = (result << 1) | (i % 2 == 0 ? 1 : 0);
    }
  }
  return (int) result;
}

+ (int)closestDecodedValue:(NSArray *)moduleBitCount {
  int bitCountSum = [ZXPDF417Common bitCountSum:moduleBitCount];
  float bitCountRatios[ZX_PDF417_BARS_IN_MODULE];
  for (int i = 0; i < ZX_PDF417_BARS_IN_MODULE; i++) {
    bitCountRatios[i] = [moduleBitCount[i] intValue] / (float) bitCountSum;
  }
  float bestMatchError = MAXFLOAT;
  int bestMatch = -1;
  for (int j = 0; j < ZX_PDF417_SYMBOL_TABLE_LEN; j++) {
    float error = 0.0f;
    float *ratioTableRow = ZX_PDF417_RATIOS_TABLE[j];
    for (int k = 0; k < ZX_PDF417_BARS_IN_MODULE; k++) {
      float diff = ratioTableRow[k] - bitCountRatios[k];
      error += diff * diff;
      if (error >= bestMatchError) {
        break;
      }
    }
    if (error < bestMatchError) {
      bestMatchError = error;
      bestMatch = ZX_PDF417_SYMBOL_TABLE[j];
    }
  }
  return bestMatch;
}

@end
