<commit_before>//
//  DeviceOrientationProvider.swift
//  PanoramaView
//
//  Created by <NAME> on 2017/01/17.
//  Copyright © 2017 eje Inc. All rights reserved.
//

import CoreMotion

public protocol DeviceOrientationProvider {
    func deviceOrientation(atTime time: TimeInterval) -> Rotation?
    func shouldWaitDeviceOrientation(atTime time: TimeInterval) -> Bool
}

extension DeviceOrientationProvider {
    public func waitDeviceOrientation(atTime time: TimeInterval) {
        let _ = waitDeviceOrientation(atTime: time, timeout: .distantFuture)
    }

    public func waitDeviceOrientation(atTime time: TimeInterval, timeout: DispatchTime) -> DispatchTimeoutResult {
        let semaphore = DispatchSemaphore(value: 0)

        let queue = DispatchQueue(label: "com.eje-c.PanoramaView.DeviceOrientationProvider.waitingQueue")
        let timer = DispatchSource.makeTimerSource(queue: queue)
        timer.scheduleRepeating(deadline: .now(), interval: .milliseconds(10))
        timer.setEventHandler {
            guard let _ = self.deviceOrientation(atTime: time) else {
                return
            }
            semaphore.signal()
        }
        timer.resume()
        defer { timer.cancel() }

        return semaphore.wait(timeout: timeout)
    }
}

extension CMMotionManager: DeviceOrientationProvider {
    public func deviceOrientation(atTime time: TimeInterval) -> Rotation? {
        guard let motion = deviceMotion, abs(motion.timestamp - time) < 1 else {
            return nil
        }

        var rotation = Rotation(motion)

        let interval = time - motion.timestamp

        let rx = motion.rotationRate.x * interval
        let ry = motion.rotationRate.y * interval
        let rz = motion.rotationRate.z * interval

        rotation.rotate(byX: Float(rx))
        rotation.rotate(byY: Float(ry))
        rotation.rotate(byZ: Float(rz))

        let reference = Rotation(x: .pi / 2)

        return reference.inverted() * rotation.normalized()
    }

    public func shouldWaitDeviceOrientation(atTime time: TimeInterval) -> Bool {
        return isDeviceMotionActive && abs((deviceMotion?.timestamp ?? 0) - time) > 1
    }
}

public final class DefaultDeviceOrientationProvider: DeviceOrientationProvider {
    public static let shared = DefaultDeviceOrientationProvider()

    private lazy var motionManager: CMMotionManager = {
        let manager = CMMotionManager()
        manager.deviceMotionUpdateInterval = 1 / 60
        return manager
    }()

    private let tokenCountQueue = DispatchQueue(label: "com.eje-c.PanoramaView.DefaultDeviceOrientationProvider.tokenCountQueue")

    private var tokenCount: Int = 0 {
        didSet {
            guard motionManager.isDeviceMotionAvailable else {
                return
            }
            if tokenCount > 0 {
                if !motionManager.isDeviceMotionActive {
                    motionManager.startDeviceMotionUpdates()
                }
            } else {
                motionManager.stopDeviceMotionUpdates()
            }
        }
    }

    public var isPaused: Bool {
        return tokenCountQueue.sync { !motionManager.isDeviceMotionActive }
    }

    public func makeToken() -> Token {
        tokenCountQueue.async { self.tokenCount += 1 }
        return Token {
            self.tokenCountQueue.async { self.tokenCount -= 1 }
        }
    }

    public func deviceOrientation(atTime time: TimeInterval) -> Rotation? {
        return motionManager.deviceOrientation(atTime: time)
    }

    public func shouldWaitDeviceOrientation(atTime time: TimeInterval) -> Bool {
        return motionManager.shouldWaitDeviceOrientation(atTime: time)
    }
}

extension DefaultDeviceOrientationProvider {
    public final class Token {
        private let invalidation: () -> Void

        fileprivate init(_ invalidation: @escaping () -> Void) {
            self.invalidation = invalidation
        }

        deinit {
            invalidation()
        }
    }
}<commit_msg>Make DefaultDeviceOrientationProvider internal<commit_after>//
//  DeviceOrientationProvider.swift
//  PanoramaView
//
//  Created by <NAME> on 2017/01/17.
//  Copyright © 2017 eje Inc. All rights reserved.
//

import CoreMotion

public protocol DeviceOrientationProvider {
    func deviceOrientation(atTime time: TimeInterval) -> Rotation?
    func shouldWaitDeviceOrientation(atTime time: TimeInterval) -> Bool
}

extension DeviceOrientationProvider {
    public func waitDeviceOrientation(atTime time: TimeInterval) {
        let _ = waitDeviceOrientation(atTime: time, timeout: .distantFuture)
    }

    public func waitDeviceOrientation(atTime time: TimeInterval, timeout: DispatchTime) -> DispatchTimeoutResult {
        let semaphore = DispatchSemaphore(value: 0)

        let queue = DispatchQueue(label: "com.eje-c.PanoramaView.DeviceOrientationProvider.waitingQueue")
        let timer = DispatchSource.makeTimerSource(queue: queue)
        timer.scheduleRepeating(deadline: .now(), interval: .milliseconds(10))
        timer.setEventHandler {
            guard let _ = self.deviceOrientation(atTime: time) else {
                return
            }
            semaphore.signal()
        }
        timer.resume()
        defer { timer.cancel() }

        return semaphore.wait(timeout: timeout)
    }
}

extension CMMotionManager: DeviceOrientationProvider {
    public func deviceOrientation(atTime time: TimeInterval) -> Rotation? {
        guard let motion = deviceMotion, abs(motion.timestamp - time) < 1 else {
            return nil
        }

        var rotation = Rotation(motion)

        let interval = time - motion.timestamp

        let rx = motion.rotationRate.x * interval
        let ry = motion.rotationRate.y * interval
        let rz = motion.rotationRate.z * interval

        rotation.rotate(byX: Float(rx))
        rotation.rotate(byY: Float(ry))
        rotation.rotate(byZ: Float(rz))

        let reference = Rotation(x: .pi / 2)

        return reference.inverted() * rotation.normalized()
    }

    public func shouldWaitDeviceOrientation(atTime time: TimeInterval) -> Bool {
        return isDeviceMotionActive && abs((deviceMotion?.timestamp ?? 0) - time) > 1
    }
}

internal final class DefaultDeviceOrientationProvider: DeviceOrientationProvider {
    static let shared = DefaultDeviceOrientationProvider()

    private lazy var motionManager: CMMotionManager = {
        let manager = CMMotionManager()
        manager.deviceMotionUpdateInterval = 1 / 60
        return manager
    }()

    final class Token {
        private let invalidation: () -> Void

        fileprivate init(_ invalidation: @escaping () -> Void) {
            self.invalidation = invalidation
        }

        deinit {
            invalidation()
        }
    }

    private let tokenCountQueue = DispatchQueue(label: "com.eje-c.PanoramaView.DefaultDeviceOrientationProvider.tokenCountQueue")

    private var tokenCount: Int = 0 {
        didSet {
            guard motionManager.isDeviceMotionAvailable else {
                return
            }
            if tokenCount > 0 {
                if !motionManager.isDeviceMotionActive {
                    motionManager.startDeviceMotionUpdates()
                }
            } else {
                motionManager.stopDeviceMotionUpdates()
            }
        }
    }

    var isPaused: Bool {
        return tokenCountQueue.sync { !motionManager.isDeviceMotionActive }
    }

    func makeToken() -> Token {
        tokenCountQueue.async { self.tokenCount += 1 }
        return Token {
            self.tokenCountQueue.async { self.tokenCount -= 1 }
        }
    }

    func deviceOrientation(atTime time: TimeInterval) -> Rotation? {
        return motionManager.deviceOrientation(atTime: time)
    }

    func shouldWaitDeviceOrientation(atTime time: TimeInterval) -> Bool {
        return motionManager.shouldWaitDeviceOrientation(atTime: time)
    }
}
{'fraction_non_alphanumeric': 0.08474148802017654, 'fraction_numerical': 0.006557377049180328, 'mean_word_length': 2.4922941435490973, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 29, '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': '6705084', 'n_tokens_mistral': 2201, 'n_tokens_neox': 2016, 'n_words': 488}