<commit_before>import Foundation
import RxSwift
import CoreBluetooth

/// Error received when device disconnection event occurs
public typealias DisconnectionReason = Error

/// CentralManager is a class implementing ReactiveX API which wraps all Core Bluetooth Manager's functions allowing to
/// discover, connect to remote peripheral devices and more.
/// You can start using this class by discovering available services of nearby peripherals. Before calling any
/// public `CentralManager`'s functions you should make sure that Bluetooth is turned on and powered on.
/// It can be done by calling and observing returned value of `observeStateWithInitialValue()` and then
/// chaining it with `scanForPeripherals(_:options:)`:
/// ```
/// let disposable = centralManager.observeStateWithInitialValue()
///     .filter { $0 == .poweredOn }
///     .take(1)
///     .flatMap { centralManager.scanForPeripherals(nil) }
/// ```
/// As a result you will receive `ScannedPeripheral` which contains `Peripheral` object, `AdvertisementData` and
/// peripheral's RSSI registered during discovery. You can then `establishConnection(_:options:)` and do other operations.
/// You can also simply stop scanning with just disposing it:
/// ```
/// disposable.dispose()
/// ```
/// - seealso: `Peripheral`
public class CentralManager: ManagerType {

    /// Implementation of CBCentralManager
    public let manager: CBCentralManager

    @available(*, deprecated, renamed: "CentralManager.manager")
    public var centralManager: CBCentralManager { return manager }

    let peripheralProvider: PeripheralProvider

    let delegateWrapper: CBCentralManagerDelegateWrapper

    /// Lock which should be used before accessing any internal structures
    private let lock = NSLock()

    /// Ongoing scan disposable
    private var scanDisposable: Disposable?

    /// Connector instance is used for establishing connection with peripherals
    private let connector: Connector

    // MARK: Initialization

    /// Creates new `CentralManager`
    /// - parameter centralManager: Central instance which is used to perform all of the necessary operations
    /// - parameter delegateWrapper: Wrapper on CoreBluetooth's central manager callbacks.
    /// - parameter peripheralProvider: Provider for providing peripherals and peripheral wrappers
    /// - parameter connector: Connector instance which is used for establishing connection with peripherals.
    init(
        centralManager: CBCentralManager,
        delegateWrapper: CBCentralManagerDelegateWrapper,
        peripheralProvider: PeripheralProvider,
        connector: Connector
    ) {
        self.manager = centralManager
        self.delegateWrapper = delegateWrapper
        self.peripheralProvider = peripheralProvider
        self.connector = connector
        centralManager.delegate = delegateWrapper
    }

    /// Creates new `CentralManager` instance. By default all operations and events are executed and received on main thread.
    /// - warning: If you pass background queue to the method make sure to observe results on main thread for UI related code.
    /// - parameter queue: Queue on which bluetooth callbacks are received. By default main thread is used.
    /// - parameter options: An optional dictionary containing initialization options for a central manager.
    /// For more info about it please refer to [Central Manager initialization options](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCentralManager_Class/index.html)
    public convenience init(queue: DispatchQueue = .main,
                            options: [String: AnyObject]? = nil) {
        let delegateWrapper = CBCentralManagerDelegateWrapper()
        let centralManager = CBCentralManager(delegate: delegateWrapper, queue: queue, options: options)
        self.init(
            centralManager: centralManager,
            delegateWrapper: delegateWrapper,
            peripheralProvider: PeripheralProvider(),
            connector: Connector(centralManager: centralManager, delegateWrapper: delegateWrapper)
        )
    }

    /// Attaches RxBluetoothKit delegate to CBCentralManager.
    /// This method is useful in cases when delegate of CBCentralManager was reassigned outside of
    /// RxBluetoothKit library (e.g. CBCentralManager was used in some other library or used in non-reactive way)
    public func attach() {
        manager.delegate = delegateWrapper
    }

    // MARK: State

    public var state: BluetoothState {
        return BluetoothState(rawValue: manager.state.rawValue) ?? .unknown
    }

    public func observeState() -> Observable<BluetoothState> {
        return self.delegateWrapper.didUpdateState.asObservable()
    }

    public func observeStateWithInitialValue() -> Observable<BluetoothState> {
        return Observable.deferred { [weak self] in
            guard let self = self else {
                RxBluetoothKitLog.w("observeState - CentralManager deallocated")
                return .never()
            }

            return self.delegateWrapper.didUpdateState.asObservable()
                .startWith(self.state)
        }
    }

    // MARK: Scanning

    /// Scans for `Peripheral`s after subscription to returned observable. First parameter `serviceUUIDs` is
    /// an array of `Service` UUIDs which needs to be implemented by a peripheral to be discovered. If user don't want to
    /// filter any peripherals, `nil` can be used instead. Additionally dictionary of
    /// [CBCentralManager specific options](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCentralManager_Class/#//apple_ref/doc/constant_group/Peripheral_Scanning_Options)
    /// can be passed to allow further customisation.
    /// Scans by default are infinite streams of `ScannedPeripheral` structures which need to be stopped by the user. For
    /// example this can be done by limiting scanning to certain number of peripherals or time:
    /// ```
    /// centralManager.scanForPeripherals(withServices: nil)
    ///     .timeout(3.0, timeoutScheduler)
    ///     .take(2)
    /// ```
    ///
    /// There can be only one ongoing scanning. It will return `BluetoothError.scanInProgress` error if
    /// this method will be called when there is already ongoing scan.
    /// As a result you will receive `ScannedPeripheral` which contains `Peripheral` object, `AdvertisementData` and
    /// peripheral's RSSI registered during discovery. You can then `establishConnection(_:options:)` and do other
    /// operations.
    ///
    /// - seealso: `Peripheral`
    ///
    /// - parameter serviceUUIDs: Services of peripherals to search for. Nil value will accept all peripherals.
    /// - parameter options: Optional scanning options.
    /// - returns: Infinite stream of scanned peripherals.<commit_msg>Added isScanInProgress property to CentralManager.<commit_after>import Foundation
import RxSwift
import CoreBluetooth

/// Error received when device disconnection event occurs
public typealias DisconnectionReason = Error

/// CentralManager is a class implementing ReactiveX API which wraps all Core Bluetooth Manager's functions allowing to
/// discover, connect to remote peripheral devices and more.
/// You can start using this class by discovering available services of nearby peripherals. Before calling any
/// public `CentralManager`'s functions you should make sure that Bluetooth is turned on and powered on.
/// It can be done by calling and observing returned value of `observeStateWithInitialValue()` and then
/// chaining it with `scanForPeripherals(_:options:)`:
/// ```
/// let disposable = centralManager.observeStateWithInitialValue()
///     .filter { $0 == .poweredOn }
///     .take(1)
///     .flatMap { centralManager.scanForPeripherals(nil) }
/// ```
/// As a result you will receive `ScannedPeripheral` which contains `Peripheral` object, `AdvertisementData` and
/// peripheral's RSSI registered during discovery. You can then `establishConnection(_:options:)` and do other operations.
/// You can also simply stop scanning with just disposing it:
/// ```
/// disposable.dispose()
/// ```
/// - seealso: `Peripheral`
public class CentralManager: ManagerType {

    /// Implementation of CBCentralManager
    public let manager: CBCentralManager

    @available(*, deprecated, renamed: "CentralManager.manager")
    public var centralManager: CBCentralManager { return manager }

    let peripheralProvider: PeripheralProvider

    let delegateWrapper: CBCentralManagerDelegateWrapper

    /// Lock which should be used before accessing any internal structures
    private let lock = NSLock()

    /// Ongoing scan disposable
    private var scanDisposable: Disposable?

    /// Connector instance is used for establishing connection with peripherals
    private let connector: Connector

    // MARK: Initialization

    /// Creates new `CentralManager`
    /// - parameter centralManager: Central instance which is used to perform all of the necessary operations
    /// - parameter delegateWrapper: Wrapper on CoreBluetooth's central manager callbacks.
    /// - parameter peripheralProvider: Provider for providing peripherals and peripheral wrappers
    /// - parameter connector: Connector instance which is used for establishing connection with peripherals.
    init(
        centralManager: CBCentralManager,
        delegateWrapper: CBCentralManagerDelegateWrapper,
        peripheralProvider: PeripheralProvider,
        connector: Connector
    ) {
        self.manager = centralManager
        self.delegateWrapper = delegateWrapper
        self.peripheralProvider = peripheralProvider
        self.connector = connector
        centralManager.delegate = delegateWrapper
    }

    /// Creates new `CentralManager` instance. By default all operations and events are executed and received on main thread.
    /// - warning: If you pass background queue to the method make sure to observe results on main thread for UI related code.
    /// - parameter queue: Queue on which bluetooth callbacks are received. By default main thread is used.
    /// - parameter options: An optional dictionary containing initialization options for a central manager.
    /// For more info about it please refer to [Central Manager initialization options](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCentralManager_Class/index.html)
    public convenience init(queue: DispatchQueue = .main,
                            options: [String: AnyObject]? = nil) {
        let delegateWrapper = CBCentralManagerDelegateWrapper()
        let centralManager = CBCentralManager(delegate: delegateWrapper, queue: queue, options: options)
        self.init(
            centralManager: centralManager,
            delegateWrapper: delegateWrapper,
            peripheralProvider: PeripheralProvider(),
            connector: Connector(centralManager: centralManager, delegateWrapper: delegateWrapper)
        )
    }

    /// Attaches RxBluetoothKit delegate to CBCentralManager.
    /// This method is useful in cases when delegate of CBCentralManager was reassigned outside of
    /// RxBluetoothKit library (e.g. CBCentralManager was used in some other library or used in non-reactive way)
    public func attach() {
        manager.delegate = delegateWrapper
    }

    // MARK: State

    public var state: BluetoothState {
        return BluetoothState(rawValue: manager.state.rawValue) ?? .unknown
    }

    public func observeState() -> Observable<BluetoothState> {
        return self.delegateWrapper.didUpdateState.asObservable()
    }

    public func observeStateWithInitialValue() -> Observable<BluetoothState> {
        return Observable.deferred { [weak self] in
            guard let self = self else {
                RxBluetoothKitLog.w("observeState - CentralManager deallocated")
                return .never()
            }

            return self.delegateWrapper.didUpdateState.asObservable()
                .startWith(self.state)
        }
    }

    // MARK: Scanning

    /// Value indicating if manager is currently scanning.
    public var isScanInProgress: Bool {
        lock.lock(); defer { lock.unlock() }
        return scanDisposable != nil
    }

    /// Scans for `Peripheral`s after subscription to returned observable. First parameter `serviceUUIDs` is
    /// an array of `Service` UUIDs which needs to be implemented by a peripheral to be discovered. If user don't want to
    /// filter any peripherals, `nil` can be used instead. Additionally dictionary of
    /// [CBCentralManager specific options](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCentralManager_Class/#//apple_ref/doc/constant_group/Peripheral_Scanning_Options)
    /// can be passed to allow further customisation.
    /// Scans by default are infinite streams of `ScannedPeripheral` structures which need to be stopped by the user. For
    /// example this can be done by limiting scanning to certain number of peripherals or time:
    /// ```
    /// centralManager.scanForPeripherals(withServices: nil)
    ///     .timeout(3.0, timeoutScheduler)
    ///     .take(2)
    /// ```
    ///
    /// There can be only one ongoing scanning. It will return `BluetoothError.scanInProgress` error if
    /// this method will be called when there is already ongoing scan.
    /// As a result you will receive `ScannedPeripheral` which contains `Peripheral` object, `AdvertisementData` and
    /// peripheral's RSSI registered during discovery. You can then `establishConnection(_:options:)` and do other
    /// operations.
    ///
    /// - seealso: `Peripheral`
    ///
    /// - parameter serviceUUIDs: Services of peripherals to search for. Nil value will accept all peripherals.
    /// - parameter options: Optional scanning options.
    /// - returns: Infinite stream of scanned peripherals.
{'fraction_non_alphanumeric': 0.08245425188374596, 'fraction_numerical': 0.0007176175098672408, 'mean_word_length': 3.1451516954193934, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 4, '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': '3920774', 'n_tokens_mistral': 3463, 'n_tokens_neox': 3225, 'n_words': 1266}