<commit_before>//
//  EventManager.swift
//  ZamzamKit iOS
//  https://github.com/AlbertMontserrat/AMGCalendarManager
//
//  Created by <NAME> on 2018-09-06.
//  Copyright © 2018 Zamzam. All rights reserved.
//

import EventKit

/// An object that accesses the user’s calendar and reminder events and supports the scheduling of new events.
public struct EventsWorker: EventsWorkerType {
    private let store: EKEventStore
    private let calendarIdentifier: String?
    private let queue: DispatchQueue
    private let type: EKEntityType = .event
    
    private var calendar: EKCalendar? {
        guard let calendarIdentifier = calendarIdentifier else {
            switch type {
            case .event: return store.defaultCalendarForNewEvents
            case .reminder: return store.defaultCalendarForNewReminders()
            }
        }
        
        return store.calendar(withIdentifier: calendarIdentifier)
    }
}

public extension EventsWorker {
    
    /// Initializer of object for accessess the user's calendar and reminder events.
    ///
    /// - Parameters:
    ///   - store: The EKEventStore class is an application’s point of contact for accessing calendar and reminder data.
    ///   - calendarIdentifier: The calendar’s unique identifier.
    ///   - queue: The dispatch queue used for tasks and queries.
    init(store: EKEventStore = EKEventStore(),
         withCalendarIdentifier calendarIdentifier: String? = nil,
         queue: DispatchQueue = DispatchQueue(label: "\(ZamzamConstants.bundleNamespace).EventsWorker", qos: .userInteractive)) {
        self.store = store
        self.calendarIdentifier = calendarIdentifier
        self.queue = queue
    }
}

public extension EventsWorker {
    
    var isAuthorized: Bool {
        return EKEventStore.authorizationStatus(for: type) == .authorized
    }
    
    func requestAccess(completion: @escaping (Bool) -> Void) {
        switch EKEventStore.authorizationStatus(for: type) {
        case .authorized:
            completion(true)
        case .notDetermined:
            store.requestAccess(to: type) { granted, _ in
                DispatchQueue.main.async {
                    completion(granted)
                }
            }
        case .restricted, .denied:
            completion(false)
        }
    }
}

public extension EventsWorker {
    
    func fetchEvent(withIdentifier identifier: String, completion: @escaping (Result<EKEvent?, ZamzamError>) -> Void) {
        requestAccess { granted in
            guard granted else { return completion(.failure(.unauthorized)) }
            
            self.queue.async {
                let event = self.store.event(withIdentifier: identifier)
                
                DispatchQueue.main.async {
                    completion(.success(event))
                }
            }
        }
    }
    
    func fetchEvents(from startDate: Date, to endDate: Date, completion: @escaping (Result<[EKEvent], ZamzamError>) -> Void) {
        requestAccess { granted in
            guard granted else { return completion(.failure(.unauthorized)) }
            
            self.queue.async {
                guard let calendar = self.calendar else {
                    return completion(.failure(.invalidData))
                }
                
                let events = self.store.events(
                    matching: self.store.predicateForEvents(
                        withStart: startDate,
                        end: endDate,
                        calendars: [calendar]
                    )
                )
                
                DispatchQueue.main.async {
                    completion(.success(events))
                }
            }
        }
    }
}

public extension EventsWorker {
    
    func createEvent(configure: @escaping (EKEvent) -> Void,
                     completion: ((Result<EKEvent, ZamzamError>) -> Void)?) {
        requestAccess { granted in
            guard granted else { completion?(.failure(.unauthorized)); return }
            
            self.queue.async {
                guard let calendar = self.calendar else {
                    completion?(.failure(.invalidData))
                    return
                }
                
                let event = EKEvent(eventStore: self.store).with {
                    $0.calendar = calendar
                    configure($0)
                }
                
                do {
                    try self.store.save(event, span: .thisEvent, commit: true)
                    
                    DispatchQueue.main.async {
                        completion?(.success(event))
                    }
                } catch {
                    DispatchQueue.main.async {
                        completion?(.failure(.other(error)))
                    }
                }
            }
        }
    }
    
    func createEvents<T>(from elements: Array<T>,
                         configure: @escaping (EKEvent, T) -> Void,
                         completion: ((Result<[EKEvent], ZamzamError>) -> Void)?) {
        requestAccess { granted in
            guard granted else { completion?(.failure(.unauthorized)); return }
            
            self.queue.async {
                guard let calendar = self.calendar else {
                    completion?(.failure(.invalidData))
                    return
                }<commit_msg>Ensure completion handler called on main thread for convention and safety<commit_after>//
//  EventManager.swift
//  ZamzamKit iOS
//  https://github.com/AlbertMontserrat/AMGCalendarManager
//
//  Created by <NAME> on 2018-09-06.
//  Copyright © 2018 Zamzam. All rights reserved.
//

import EventKit

/// An object that accesses the user’s calendar and reminder events and supports the scheduling of new events.
public struct EventsWorker: EventsWorkerType {
    private let store: EKEventStore
    private let calendarIdentifier: String?
    private let queue: DispatchQueue
    private let type: EKEntityType = .event
    
    private var calendar: EKCalendar? {
        guard let calendarIdentifier = calendarIdentifier else {
            switch type {
            case .event: return store.defaultCalendarForNewEvents
            case .reminder: return store.defaultCalendarForNewReminders()
            }
        }
        
        return store.calendar(withIdentifier: calendarIdentifier)
    }
}

public extension EventsWorker {
    
    /// Initializer of object for accessess the user's calendar and reminder events.
    ///
    /// - Parameters:
    ///   - store: The EKEventStore class is an application’s point of contact for accessing calendar and reminder data.
    ///   - calendarIdentifier: The calendar’s unique identifier.
    ///   - queue: The dispatch queue used for tasks and queries.
    init(store: EKEventStore = EKEventStore(),
         withCalendarIdentifier calendarIdentifier: String? = nil,
         queue: DispatchQueue = DispatchQueue(label: "\(ZamzamConstants.bundleNamespace).EventsWorker", qos: .userInteractive)) {
        self.store = store
        self.calendarIdentifier = calendarIdentifier
        self.queue = queue
    }
}

public extension EventsWorker {
    
    var isAuthorized: Bool {
        return EKEventStore.authorizationStatus(for: type) == .authorized
    }
    
    func requestAccess(completion: @escaping (Bool) -> Void) {
        switch EKEventStore.authorizationStatus(for: type) {
        case .authorized:
            completion(true)
        case .notDetermined:
            store.requestAccess(to: type) { granted, _ in
                DispatchQueue.main.async {
                    completion(granted)
                }
            }
        case .restricted, .denied:
            completion(false)
        }
    }
}

public extension EventsWorker {
    
    func fetchEvent(withIdentifier identifier: String, completion: @escaping (Result<EKEvent?, ZamzamError>) -> Void) {
        requestAccess { granted in
            guard granted else { return completion(.failure(.unauthorized)) }
            
            self.queue.async {
                let event = self.store.event(withIdentifier: identifier)
                
                DispatchQueue.main.async {
                    completion(.success(event))
                }
            }
        }
    }
    
    func fetchEvents(from startDate: Date, to endDate: Date, completion: @escaping (Result<[EKEvent], ZamzamError>) -> Void) {
        requestAccess { granted in
            guard granted else { return completion(.failure(.unauthorized)) }
            
            self.queue.async {
                guard let calendar = self.calendar else {
                    return DispatchQueue.main.async {
                        completion(.failure(.invalidData))
                    }
                }
                
                let events = self.store.events(
                    matching: self.store.predicateForEvents(
                        withStart: startDate,
                        end: endDate,
                        calendars: [calendar]
                    )
                )
                
                DispatchQueue.main.async {
                    completion(.success(events))
                }
            }
        }
    }
}

public extension EventsWorker {
    
    func createEvent(configure: @escaping (EKEvent) -> Void,
                     completion: ((Result<EKEvent, ZamzamError>) -> Void)?) {
        requestAccess { granted in
            guard granted else { completion?(.failure(.unauthorized)); return }
            
            self.queue.async {
                guard let calendar = self.calendar else {
                    return DispatchQueue.main.async {
                        completion?(.failure(.invalidData))
                    }
                }
                
                let event = EKEvent(eventStore: self.store).with {
                    $0.calendar = calendar
                    configure($0)
                }
                
                do {
                    try self.store.save(event, span: .thisEvent, commit: true)
                    
                    DispatchQueue.main.async {
                        completion?(.success(event))
                    }
                } catch {
                    DispatchQueue.main.async {
                        completion?(.failure(.other(error)))
                    }
                }
            }
        }
    }
    
    func createEvents<T>(from elements: Array<T>,
                         configure: @escaping (EKEvent, T) -> Void,
                         completion: ((Result<[EKEvent], ZamzamError>) -> Void)?) {
        requestAccess { granted in
            guard granted else { completion?(.failure(.unauthorized)); return }
            
            self.queue.async {
                guard let calendar = self.calendar else {
                    return DispatchQueue.main.async {
                        completion?(.failure(.invalidData))
                    }
                }
{'fraction_non_alphanumeric': 0.08478712826538913, 'fraction_numerical': 0.0025309590526981832, 'mean_word_length': 1.5054347826086956, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 31, 'https://': 2, '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': '3810940', 'n_tokens_mistral': 2755, 'n_tokens_neox': 2556, 'n_words': 653}