<commit_before>/*
 * QRCodeReader.swift
 *
 * Copyright 2014-present <NAME>.
 * http://yannickloriot.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

import UIKit
import AVFoundation

/// Convenient controller to display a view to scan/read 1D or 2D bar codes like the QRCodes. It is based on the `AVFoundation` framework from Apple. It aims to replace ZXing or ZBar for iOS 7 and over.
public class QRCodeReaderViewController: UIViewController {
  /// The code reader object used to scan the bar code.
  public let codeReader: QRCodeReader

  let readerView: QRCodeReaderContainer
  let startScanningAtLoad: Bool
  let showCancelButton: Bool
  let showSwitchCameraButton: Bool
  let showTorchButton: Bool
  let showOverlayView: Bool
  let handleOrientationChange: Bool

  // MARK: - Managing the Callback Responders

  /// The receiver's delegate that will be called when a result is found.
  public weak var delegate: QRCodeReaderViewControllerDelegate?

  /// The completion blocak that will be called when a result is found.
  public var completionBlock: ((QRCodeReaderResult?) -> Void)?

  deinit {
    codeReader.stopScanning()

    NotificationCenter.default.removeObserver(self)
  }

  // MARK: - Creating the View Controller

  /**
   Initializes a view controller using a builder.

   - parameter builder: A QRCodeViewController builder object.
   */
  required public init(builder: QRCodeReaderViewControllerBuilder) {
    readerView              = builder.readerView
    startScanningAtLoad     = builder.startScanningAtLoad
    codeReader              = builder.reader
    showCancelButton        = builder.showCancelButton
    showSwitchCameraButton  = builder.showSwitchCameraButton
    showTorchButton         = builder.showTorchButton
    showOverlayView         = builder.showOverlayView
    handleOrientationChange = builder.handleOrientationChange

    super.init(nibName: nil, bundle: nil)

    view.backgroundColor = .black

    codeReader.didFindCode = { [weak self] resultAsObject in
      if let weakSelf = self {
        if let qrv = weakSelf.readerView.displayable as? QRCodeReaderView {
          qrv.addGreenBorder()
        }
        weakSelf.completionBlock?(resultAsObject)
        weakSelf.delegate?.reader(weakSelf, didScanResult: resultAsObject)
      }
    }

    codeReader.didFailDecoding = { [weak self] in
      if let weakSelf = self {
        if let qrv = weakSelf.readerView.displayable as? QRCodeReaderView {
          qrv.addRedBorder()
        }
      }
    }

    setupUIComponentsWithCancelButtonTitle(builder.cancelButtonTitle)

    NotificationCenter.default.addObserver(self, selector: #selector(orientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
  }

  required public init?(coder aDecoder: NSCoder) {
    codeReader              = QRCodeReader()
    readerView              = QRCodeReaderContainer(displayable: QRCodeReaderView())
    startScanningAtLoad     = false
    showCancelButton        = false
    showTorchButton         = false
    showSwitchCameraButton  = false
    showOverlayView         = false
    handleOrientationChange = false

    super.init(coder: aDecoder)
  }

  // MARK: - Responding to View Events
  override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return parent?.supportedInterfaceOrientations ?? .all
  }

  override public func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    if startScanningAtLoad {
      startScanning()
    }
  }

  override public func viewWillDisappear(_ animated: Bool) {
    stopScanning()

    super.viewWillDisappear(animated)
  }

  override public func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    codeReader.previewLayer.frame = view.bounds
  }

  // MARK: - Managing the Orientation

  func orientationDidChange(_ notification: Notification) {
    readerView.view.setNeedsDisplay()

    if showOverlayView, let qrv = readerView.displayable as? QRCodeReaderView {
        qrv.overlayView?.setNeedsDisplay()
    }

    if handleOrientationChange == true, let device = notification.object as? UIDevice , codeReader.previewLayer.connection.isVideoOrientationSupported {
      codeReader.previewLayer.connection.videoOrientation = QRCodeReader.videoOrientation(deviceOrientation: device.orientation, withSupportedOrientations: supportedInterfaceOrientations, fallbackOrientation: codeReader.previewLayer.connection.videoOrientation)
    }
  }

  // MARK: - Initializing the AV Components

  private func setupUIComponentsWithCancelButtonTitle(_ cancelButtonTitle: String) {
    view.addSubview(readerView.view)

    let sscb = showSwitchCameraButton && codeReader.hasFrontDevice
    let stb  = showTorchButton && codeReader.isTorchAvailable

    readerView.setupComponents(showCancelButton: showCancelButton, showSwitchCameraButton: sscb, showTorchButton: stb, showOverlayView: showOverlayView)

    // Setup action methods

    readerView.displayable.switchCameraButton?.addTarget(self, action: #selector(switchCameraAction), for: .touchUpInside)
    readerView.displayable.toggleTorchButton?.addTarget(self, action: #selector(toggleTorchAction), for: .touchUpInside)
    readerView.displayable.cancelButton?.setTitle(cancelButtonTitle, for: .normal)
    readerView.displayable.cancelButton?.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)

    // Setup camera preview layer
    codeReader.previewLayer.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height)

    if codeReader.previewLayer.connection.isVideoOrientationSupported {
      let orientation = UIDevice.current.orientation

      codeReader.previewLayer.connection.videoOrientation = QRCodeReader.videoOrientation(deviceOrientation: orientation, withSupportedOrientations: supportedInterfaceOrientations)
    }
<commit_msg>AVCaptureVideoPreviewLayer connection property can be nil<commit_after>/*
 * QRCodeReader.swift
 *
 * Copyright 2014-present <NAME>.
 * http://yannickloriot.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

import UIKit
import AVFoundation

/// Convenient controller to display a view to scan/read 1D or 2D bar codes like the QRCodes. It is based on the `AVFoundation` framework from Apple. It aims to replace ZXing or ZBar for iOS 7 and over.
public class QRCodeReaderViewController: UIViewController {
  /// The code reader object used to scan the bar code.
  public let codeReader: QRCodeReader

  let readerView: QRCodeReaderContainer
  let startScanningAtLoad: Bool
  let showCancelButton: Bool
  let showSwitchCameraButton: Bool
  let showTorchButton: Bool
  let showOverlayView: Bool
  let handleOrientationChange: Bool

  // MARK: - Managing the Callback Responders

  /// The receiver's delegate that will be called when a result is found.
  public weak var delegate: QRCodeReaderViewControllerDelegate?

  /// The completion blocak that will be called when a result is found.
  public var completionBlock: ((QRCodeReaderResult?) -> Void)?

  deinit {
    codeReader.stopScanning()

    NotificationCenter.default.removeObserver(self)
  }

  // MARK: - Creating the View Controller

  /**
   Initializes a view controller using a builder.

   - parameter builder: A QRCodeViewController builder object.
   */
  required public init(builder: QRCodeReaderViewControllerBuilder) {
    readerView              = builder.readerView
    startScanningAtLoad     = builder.startScanningAtLoad
    codeReader              = builder.reader
    showCancelButton        = builder.showCancelButton
    showSwitchCameraButton  = builder.showSwitchCameraButton
    showTorchButton         = builder.showTorchButton
    showOverlayView         = builder.showOverlayView
    handleOrientationChange = builder.handleOrientationChange

    super.init(nibName: nil, bundle: nil)

    view.backgroundColor = .black

    codeReader.didFindCode = { [weak self] resultAsObject in
      if let weakSelf = self {
        if let qrv = weakSelf.readerView.displayable as? QRCodeReaderView {
          qrv.addGreenBorder()
        }
        weakSelf.completionBlock?(resultAsObject)
        weakSelf.delegate?.reader(weakSelf, didScanResult: resultAsObject)
      }
    }

    codeReader.didFailDecoding = { [weak self] in
      if let weakSelf = self {
        if let qrv = weakSelf.readerView.displayable as? QRCodeReaderView {
          qrv.addRedBorder()
        }
      }
    }

    setupUIComponentsWithCancelButtonTitle(builder.cancelButtonTitle)

    NotificationCenter.default.addObserver(self, selector: #selector(orientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
  }

  required public init?(coder aDecoder: NSCoder) {
    codeReader              = QRCodeReader()
    readerView              = QRCodeReaderContainer(displayable: QRCodeReaderView())
    startScanningAtLoad     = false
    showCancelButton        = false
    showTorchButton         = false
    showSwitchCameraButton  = false
    showOverlayView         = false
    handleOrientationChange = false

    super.init(coder: aDecoder)
  }

  // MARK: - Responding to View Events
  override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return parent?.supportedInterfaceOrientations ?? .all
  }

  override public func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    if startScanningAtLoad {
      startScanning()
    }
  }

  override public func viewWillDisappear(_ animated: Bool) {
    stopScanning()

    super.viewWillDisappear(animated)
  }

  override public func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    codeReader.previewLayer.frame = view.bounds
  }

  // MARK: - Managing the Orientation

  func orientationDidChange(_ notification: Notification) {
    readerView.view.setNeedsDisplay()

    if showOverlayView, let qrv = readerView.displayable as? QRCodeReaderView {
        qrv.overlayView?.setNeedsDisplay()
    }

    if handleOrientationChange == true, let device = notification.object as? UIDevice, let connection = codeReader.previewLayer.connection, connection.isVideoOrientationSupported {
      connection.videoOrientation = QRCodeReader.videoOrientation(deviceOrientation: device.orientation, withSupportedOrientations: supportedInterfaceOrientations, fallbackOrientation: connection.videoOrientation)
    }
  }

  // MARK: - Initializing the AV Components

  private func setupUIComponentsWithCancelButtonTitle(_ cancelButtonTitle: String) {
    view.addSubview(readerView.view)

    let sscb = showSwitchCameraButton && codeReader.hasFrontDevice
    let stb  = showTorchButton && codeReader.isTorchAvailable

    readerView.setupComponents(showCancelButton: showCancelButton, showSwitchCameraButton: sscb, showTorchButton: stb, showOverlayView: showOverlayView)

    // Setup action methods

    readerView.displayable.switchCameraButton?.addTarget(self, action: #selector(switchCameraAction), for: .touchUpInside)
    readerView.displayable.toggleTorchButton?.addTarget(self, action: #selector(toggleTorchAction), for: .touchUpInside)
    readerView.displayable.cancelButton?.setTitle(cancelButtonTitle, for: .normal)
    readerView.displayable.cancelButton?.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)

    // Setup camera preview layer
    codeReader.previewLayer.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height)

    if let connection = codeReader.previewLayer.connection, connection.isVideoOrientationSupported {
      let orientation = UIDevice.current.orientation

      connection.videoOrientation = QRCodeReader.videoOrientation(deviceOrientation: orientation, withSupportedOrientations: supportedInterfaceOrientations)
    }

{'fraction_non_alphanumeric': 0.06901032267378907, 'fraction_numerical': 0.0012993575398830579, 'mean_word_length': 3.9320042719829122, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7575743', 'n_tokens_mistral': 3778, 'n_tokens_neox': 3543, 'n_words': 1097}