Pods/ZXingObjC/ZXingObjC/qrcode/multi/ZXQRCodeMultiReader.m
/*
 * Copyright 2012 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 "ZXByteArray.h"
#import "ZXDecoderResult.h"
#import "ZXDetectorResult.h"
#import "ZXMultiDetector.h"
#import "ZXQRCodeDecoder.h"
#import "ZXQRCodeDecoderMetaData.h"
#import "ZXQRCodeMultiReader.h"
#import "ZXResult.h"

@implementation ZXQRCodeMultiReader

- (NSArray *)decodeMultiple:(ZXBinaryBitmap *)image error:(NSError **)error {
  return [self decodeMultiple:image hints:nil error:error];
}

- (NSArray *)decodeMultiple:(ZXBinaryBitmap *)image hints:(ZXDecodeHints *)hints error:(NSError **)error {
  ZXBitMatrix *matrix = [image blackMatrixWithError:error];
  if (!matrix) {
    return nil;
  }
  NSMutableArray *results = [NSMutableArray array];
  NSArray *detectorResults = [[[ZXMultiDetector alloc] initWithImage:matrix] detectMulti:hints error:error];
  if (!detectorResults) {
    return nil;
  }
  for (ZXDetectorResult *detectorResult in detectorResults) {
    ZXDecoderResult *decoderResult = [[self decoder] decodeMatrix:[detectorResult bits] hints:hints error:nil];
    if (decoderResult) {
      NSMutableArray *points = [[detectorResult points] mutableCopy];
      // If the code was mirrored: swap the bottom-left and the top-right points.
      if ([decoderResult.other isKindOfClass:[ZXQRCodeDecoderMetaData class]]) {
        [(ZXQRCodeDecoderMetaData *)decoderResult.other applyMirroredCorrection:points];
      }
      ZXResult *result = [ZXResult resultWithText:decoderResult.text
                                         rawBytes:decoderResult.rawBytes
                                     resultPoints:points
                                           format:kBarcodeFormatQRCode];
      NSMutableArray *byteSegments = decoderResult.byteSegments;
      if (byteSegments != nil) {
        [result putMetadata:kResultMetadataTypeByteSegments value:byteSegments];
      }
      NSString *ecLevel = decoderResult.ecLevel;
      if (ecLevel != nil) {
        [result putMetadata:kResultMetadataTypeErrorCorrectionLevel value:ecLevel];
      }
      if ([decoderResult hasStructuredAppend]) {
        [result putMetadata:kResultMetadataTypeStructuredAppendSequence
                      value:@(decoderResult.structuredAppendSequenceNumber)];
        [result putMetadata:kResultMetadataTypeStructuredAppendParity
                      value:@(decoderResult.structuredAppendParity)];
      }
      [results addObject:result];
    }
  }

  results = [self processStructuredAppend:results];
  return results;
}

- (NSMutableArray *)processStructuredAppend:(NSMutableArray *)results {
  BOOL hasSA = NO;

  // first, check, if there is at least on SA result in the list
  for (ZXResult *result in results) {
    if (result.resultMetadata[@(kResultMetadataTypeStructuredAppendSequence)]) {
      hasSA = YES;
      break;
    }
  }
  if (!hasSA) {
    return results;
  }

  // it is, second, split the lists and built a new result list
  NSMutableArray *newResults = [NSMutableArray array];
  NSMutableArray *saResults = [NSMutableArray array];
  for (ZXResult *result in results) {
    [newResults addObject:result];
    if (result.resultMetadata[@(kResultMetadataTypeStructuredAppendSequence)]) {
      [saResults addObject:result];
    }
  }
  // sort and concatenate the SA list items
  [saResults sortUsingComparator:^NSComparisonResult(ZXResult *a, ZXResult *b) {
    int aNumber = [a.resultMetadata[@(kResultMetadataTypeStructuredAppendSequence)] intValue];
    int bNumber = [b.resultMetadata[@(kResultMetadataTypeStructuredAppendSequence)] intValue];
    if (aNumber < bNumber) {
      return NSOrderedAscending;
    }
    if (aNumber > bNumber) {
      return NSOrderedDescending;
    }
    return NSOrderedSame;
  }];
  NSMutableString *concatedText = [NSMutableString string];
  int rawBytesLen = 0;
  int byteSegmentLength = 0;
  for (ZXResult *saResult in saResults) {
    [concatedText appendString:saResult.text];
    rawBytesLen += saResult.rawBytes.length;
    if (saResult.resultMetadata[@(kResultMetadataTypeByteSegments)]) {
      for (ZXByteArray *segment in saResult.resultMetadata[@(kResultMetadataTypeByteSegments)]) {
        byteSegmentLength += segment.length;
      }
    }
  }
  ZXByteArray *newRawBytes = [[ZXByteArray alloc] initWithLength:rawBytesLen];
  ZXByteArray *newByteSegment = [[ZXByteArray alloc] initWithLength:byteSegmentLength];
  int newRawBytesIndex = 0;
  int byteSegmentIndex = 0;
  for (ZXResult *saResult in saResults) {
    memcpy(newRawBytes.array, saResult.rawBytes.array, saResult.rawBytes.length * sizeof(int8_t));
    newRawBytesIndex += saResult.rawBytes.length;
    if (saResult.resultMetadata[@(kResultMetadataTypeByteSegments)]) {
      for (ZXByteArray *segment in saResult.resultMetadata[@(kResultMetadataTypeByteSegments)]) {
        memcpy(newByteSegment.array, segment.array, segment.length * sizeof(int8_t));
        byteSegmentIndex += segment.length;
      }
    }
  }
  ZXResult *newResult = [[ZXResult alloc] initWithText:concatedText rawBytes:newRawBytes resultPoints:@[] format:kBarcodeFormatQRCode];
  if (byteSegmentLength > 0) {
    NSMutableArray *byteSegmentList = [NSMutableArray array];
    [byteSegmentList addObject:newByteSegment];
    [newResult putMetadata:kResultMetadataTypeByteSegments value:byteSegmentList];
  }
  [newResults addObject:newResult];
  return newResults;
}

@end
