<commit_before>//
//  PinpointKit.swift
//  PinpointKit
//
//  Created by <NAME> on 1/22/16.
//  Copyright © 2016 Lickability. All rights reserved.
//

import Foundation


/// `PinpointKit` is an object that can be used to collect feedback from application users.
open class PinpointKit {
    
    /// The configuration struct that specifies how PinpointKit should be configured.
    fileprivate let configuration: Configuration
    
    /// A delegate that is notified of significant events.
    fileprivate weak var delegate: PinpointKitDelegate?
    
    fileprivate weak var displayingViewController: UIViewController?
    
    /**
     Initializes a `PinpointKit` object with a configuration and an optional delegate.
     
     - parameter configuration: The configuration struct that specifies how PinpointKit should be configured.
     - parameter delegate:      A delegate that is notified of significant events.
     */
    public init(configuration: Configuration, delegate: PinpointKitDelegate? = nil) {
        self.configuration = configuration
        self.delegate = delegate
        
        self.configuration.feedbackCollector.feedbackDelegate = self
        self.configuration.sender.delegate = self
    }
    
    /**
     Initializes a `PinpointKit` with a default configuration supplied with feedback recipients and an optional delegate.
     
     - parameter feedbackRecipients: The recipients of the feedback submission. Suitable for email recipients in the "To:" field.
     - parameter title:              The default title of the feedback.
     - parameter body:               The default body text of the feedback.
     - parameter delegate:           A delegate that is notified of significant events.
     */
    public convenience init(feedbackRecipients: [String], title: String? = FeedbackConfiguration.DefaultTitle, body: String? = nil, delegate: PinpointKitDelegate? = nil) {
        let feedbackConfiguration = FeedbackConfiguration(recipients: feedbackRecipients, title: title, body: body)
        let configuration = Configuration(feedbackConfiguration: feedbackConfiguration)
        
        self.init(configuration: configuration, delegate: delegate)
    }
    
    /**
     Shows PinpointKit’s feedback collection UI from a given view controller.
     
     - parameter viewController: The view controller from which to present.
     - parameter screenshot:     The screenshot to be annotated. The default value is a screenshot taken at the time this method is called. This image is intended to match the device’s screen size in points.
     */
    open func show(from viewController: UIViewController, screenshot: UIImage = Screenshotter.takeScreenshot()) {
        displayingViewController = viewController
        configuration.editor.clearAllAnnotations()
        configuration.feedbackCollector.collectFeedback(with: screenshot, from: viewController)
    }
}

// MARK: - FeedbackCollectorDelegate

extension PinpointKit: FeedbackCollectorDelegate {
    
    public func feedbackCollector(_ feedbackCollector: FeedbackCollector, didCollect feedback: Feedback) {
        delegate?.pinpointKit(self, willSend: feedback)
        configuration.sender.send(feedback, from: feedbackCollector.viewController)
    }
}

// MARK: - SenderDelegate

extension PinpointKit: SenderDelegate {
    
    public func sender(_ sender: Sender, didSend feedback: Feedback?, success: SuccessType?) {
        guard let feedback = feedback else { return }
        
        delegate?.pinpointKit(self, didSend: feedback)
        displayingViewController?.dismiss(animated: true, completion: nil)
    }
    
    public func sender(_ sender: Sender, didFailToSend feedback: Feedback?, error: Error) {
        if case MailSender.Error.mailCanceled = error { return }
        
        NSLog("An error occurred sending mail: \(error)")
    }
}

/// A protocol describing an object that can be notified of events from PinpointKit.
public protocol PinpointKitDelegate: class {

    /**
     Notifies the delegate that PinpointKit is about to send user feedback.
     
     - parameter pinpointKit:   The `PinpointKit` instance responsible for the feedback.
     - parameter feedback:      The feedback that’s about to be sent.
     */
    func pinpointKit(_ pinpointKit: PinpointKit, willSend feedback: Feedback)
    
    /**
     Notifies the delegate that PinpointKit has just sent user feedback.
     
     - parameter pinpointKit:   The `PinpointKit` instance responsible for the feedback.
     - parameter feedback:      The feedback that’s just been sent.
     */
    func pinpointKit(_ pinpointKit: PinpointKit, didSend feedback: Feedback)
}

/// An extension on PinpointKitDelegate that makes all delegate methods optional by giving them empty implementations by default.
public extension PinpointKitDelegate {
    
    func pinpointKit(_ pinpointKit: PinpointKit, willSend feedback: Feedback) {}
    func pinpointKit(_ pinpointKit: PinpointKit, didSend feedback: Feedback) {}
}<commit_msg>Added GUI alert on mailer failure.<commit_after>//
//  PinpointKit.swift
//  PinpointKit
//
//  Created by <NAME> on 1/22/16.
//  Copyright © 2016 Lickability. All rights reserved.
//

import Foundation


/// `PinpointKit` is an object that can be used to collect feedback from application users.
open class PinpointKit {
    
    /// The configuration struct that specifies how PinpointKit should be configured.
    fileprivate let configuration: Configuration
    
    /// A delegate that is notified of significant events.
    fileprivate weak var delegate: PinpointKitDelegate?
    
    fileprivate weak var displayingViewController: UIViewController?
    
    /**
     Initializes a `PinpointKit` object with a configuration and an optional delegate.
     
     - parameter configuration: The configuration struct that specifies how PinpointKit should be configured.
     - parameter delegate:      A delegate that is notified of significant events.
     */
    public init(configuration: Configuration, delegate: PinpointKitDelegate? = nil) {
        self.configuration = configuration
        self.delegate = delegate
        
        self.configuration.feedbackCollector.feedbackDelegate = self
        self.configuration.sender.delegate = self
    }
    
    /**
     Initializes a `PinpointKit` with a default configuration supplied with feedback recipients and an optional delegate.
     
     - parameter feedbackRecipients: The recipients of the feedback submission. Suitable for email recipients in the "To:" field.
     - parameter title:              The default title of the feedback.
     - parameter body:               The default body text of the feedback.
     - parameter delegate:           A delegate that is notified of significant events.
     */
    public convenience init(feedbackRecipients: [String], title: String? = FeedbackConfiguration.DefaultTitle, body: String? = nil, delegate: PinpointKitDelegate? = nil) {
        let feedbackConfiguration = FeedbackConfiguration(recipients: feedbackRecipients, title: title, body: body)
        let configuration = Configuration(feedbackConfiguration: feedbackConfiguration)
        
        self.init(configuration: configuration, delegate: delegate)
    }
    
    /**
     Shows PinpointKit’s feedback collection UI from a given view controller.
     
     - parameter viewController: The view controller from which to present.
     - parameter screenshot:     The screenshot to be annotated. The default value is a screenshot taken at the time this method is called. This image is intended to match the device’s screen size in points.
     */
    open func show(from viewController: UIViewController, screenshot: UIImage = Screenshotter.takeScreenshot()) {
        displayingViewController = viewController
        configuration.editor.clearAllAnnotations()
        configuration.feedbackCollector.collectFeedback(with: screenshot, from: viewController)
    }
}

// MARK: - FeedbackCollectorDelegate

extension PinpointKit: FeedbackCollectorDelegate {
    
    public func feedbackCollector(_ feedbackCollector: FeedbackCollector, didCollect feedback: Feedback) {
        delegate?.pinpointKit(self, willSend: feedback)
        configuration.sender.send(feedback, from: feedbackCollector.viewController)
    }
}

// MARK: - SenderDelegate

extension PinpointKit: SenderDelegate {
    
    public func sender(_ sender: Sender, didSend feedback: Feedback?, success: SuccessType?) {
        guard let feedback = feedback else { return }
        
        delegate?.pinpointKit(self, didSend: feedback)
        displayingViewController?.dismiss(animated: true, completion: nil)
    }
    
    public func sender(_ sender: Sender, didFailToSend feedback: Feedback?, error: Error) {
        if case MailSender.Error.mailCanceled = error { return }
        
        let alert = UIAlertController(
            title: "Mailer error",
            message: "Unable to prepare an email to send. Ensure that you have at least one email account set up.",
            preferredStyle: UIAlertControllerStyle.alert)
        
        let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
        alert.addAction(okAction)
        
        self.configuration.feedbackCollector.viewController.present(alert, animated: true, completion: nil)
        
        guard let feedback = feedback else { return }
        delegate?.pinpointKit(self, didFailToSend: feedback, error: error)
    }
}

/// A protocol describing an object that can be notified of events from PinpointKit.
public protocol PinpointKitDelegate: class {

    /**
     Notifies the delegate that PinpointKit is about to send user feedback.
     
     - parameter pinpointKit:   The `PinpointKit` instance responsible for the feedback.
     - parameter feedback:      The feedback that’s about to be sent.
     */
    func pinpointKit(_ pinpointKit: PinpointKit, willSend feedback: Feedback)
    
    /**
     Notifies the delegate that PinpointKit has just sent user feedback.
     
     - parameter pinpointKit:   The `PinpointKit` instance responsible for the feedback.
     - parameter feedback:      The feedback that’s just been sent.
     */
    func pinpointKit(_ pinpointKit: PinpointKit, didSend feedback: Feedback)
    
    /**
     Notifies the delegate that PinpointKit has failed to send user feedback.
     
     - parameter pinpointKit:   The `PinpointKit` instance responsible for the feedback.
     - parameter feedback:      The feedback that failed to send.
     - parameter error:         The error that occurred.
     */
    func pinpointKit(_ pinpointKit: PinpointKit, didFailToSend feedback: Feedback, error: Error)
}

/// An extension on PinpointKitDelegate that makes all delegate methods optional by giving them empty implementations by default.
public extension PinpointKitDelegate {
    
    func pinpointKit(_ pinpointKit: PinpointKit, willSend feedback: Feedback) {}
    func pinpointKit(_ pinpointKit: PinpointKit, didSend feedback: Feedback) {}
    func pinpointKit(_ pinpointKit: PinpointKit, didFailToSend feedback: Feedback, error: Error) {}
}
{'fraction_non_alphanumeric': 0.06672645739910314, 'fraction_numerical': 0.0016143497757847534, 'mean_word_length': 3.1577181208053693, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, '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': '1688165', 'n_tokens_mistral': 2765, 'n_tokens_neox': 2620, 'n_words': 1015}