<commit_before>//
//  AppDelegate.swift
//  MenubarCountdown
//
//  Copyright © 2009,2015,2019 <NAME>. All rights reserved.

import Cocoa
import AudioToolbox
import UserNotifications

/**
 Application delegate which implements most of the logic of Menubar Countdown.

 From the user's point of view, the application switches between these states:

 - reset: timer is not running
 - running: timer has been started and is counting down
 - paused: timer has been started but is in a paused state
 - expired: timer has reached 00:00:00 and is waiting to be turned off

 The persistent settings of the app are managed by the UserDefaults system. 
 */
@NSApplicationMain
final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {

    /**
     Initial timer setting.
     */
    var timerSettingSeconds = 25 * 60

    /**
     Number of seconds remaining in countdown.
     */
    var secondsRemaining = 0

    /**
     Indicates whether timer is running.
     
     KVC compliant so it can be used to enable/disable menu items and other controls
     */
    @objc var isTimerRunning = false

    /**
     Indicates whether the timer can be paused.
    
     KVC compliant so it can be used to enable/disable menu items and other controls
    */
    @objc var canPause = false

    /**
     Indicates whether the timer can be resumed.
    
     KVC compliant so it can be used to enable/disable menu items and other controls
     */
    @objc var canResume = false

    var stopwatch: Stopwatch!

    var statusItem: NSStatusItem!

    var notificationId: String = ""

    /**
     Reference to menu loaded from MainMenu.xib.
     */
    @IBOutlet var menu: NSMenu!

    /**
     Reference to dialog loaded from StartTimerDialog.xib.
     */
    @IBOutlet var startTimerDialogController: StartTimerDialogController?

    /**
     Reference to dialog loaded from TimerExpiredAlert.xib.
     */
    @IBOutlet var timerExpiredAlertController: TimerExpiredAlertController?

    let defaults = UserDefaults.standard

    override init() {
        super.init()
        AppUserDefaults.registerUserDefaults()
    }

    func applicationDidFinishLaunching(_ notification: Notification) {
        Log.debug("application did finish launching: \(notification)")

        UNUserNotificationCenter.current().delegate = self

        stopwatch.reset()

        initializeStatusItem()

        if showStartDialogOnLaunch {
            showStartTimerDialog(self)
        }

        NSApp.servicesProvider = ServicesProvider(appDelegate: self)
    }

    func applicationWillTerminate(_ notification: Notification) {
        Log.debug("application will terminate")
    }

    // MARK: Timer updating

    /**
     Determine time to the next top of second, and set a timer to call `nextSecondTimerDidFire` at that time.
     */
    func waitForNextSecond() {
        let elapsed = stopwatch.elapsedTimeInterval()
        let intervalToNextSecond = ceil(elapsed) - elapsed

        Timer.scheduledTimer(timeInterval: intervalToNextSecond,
            target: self,
            selector: #selector(nextSecondTimerDidFire(_:)),
            userInfo: nil,
            repeats: false)
    }

    /**
     Called at the top of each second.

     Updates the timer display if the timer is running.
     */<commit_msg>Add tolerance to the per-second timer<commit_after>//
//  AppDelegate.swift
//  MenubarCountdown
//
//  Copyright © 2009,2015,2019 <NAME>. All rights reserved.

import Cocoa
import AudioToolbox
import UserNotifications

/**
 Application delegate which implements most of the logic of Menubar Countdown.

 From the user's point of view, the application switches between these states:

 - reset: timer is not running
 - running: timer has been started and is counting down
 - paused: timer has been started but is in a paused state
 - expired: timer has reached 00:00:00 and is waiting to be turned off

 The persistent settings of the app are managed by the UserDefaults system. 
 */
@NSApplicationMain
final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {

    /**
     Initial timer setting.
     */
    var timerSettingSeconds = 25 * 60

    /**
     Number of seconds remaining in countdown.
     */
    var secondsRemaining = 0

    /**
     Indicates whether timer is running.
     
     KVC compliant so it can be used to enable/disable menu items and other controls
     */
    @objc var isTimerRunning = false

    /**
     Indicates whether the timer can be paused.
    
     KVC compliant so it can be used to enable/disable menu items and other controls
    */
    @objc var canPause = false

    /**
     Indicates whether the timer can be resumed.
    
     KVC compliant so it can be used to enable/disable menu items and other controls
     */
    @objc var canResume = false

    var stopwatch: Stopwatch!

    var statusItem: NSStatusItem!

    var notificationId: String = ""

    /**
     Reference to menu loaded from MainMenu.xib.
     */
    @IBOutlet var menu: NSMenu!

    /**
     Reference to dialog loaded from StartTimerDialog.xib.
     */
    @IBOutlet var startTimerDialogController: StartTimerDialogController?

    /**
     Reference to dialog loaded from TimerExpiredAlert.xib.
     */
    @IBOutlet var timerExpiredAlertController: TimerExpiredAlertController?

    let defaults = UserDefaults.standard

    override init() {
        super.init()
        AppUserDefaults.registerUserDefaults()
    }

    func applicationDidFinishLaunching(_ notification: Notification) {
        Log.debug("application did finish launching: \(notification)")

        UNUserNotificationCenter.current().delegate = self

        stopwatch.reset()

        initializeStatusItem()

        if showStartDialogOnLaunch {
            showStartTimerDialog(self)
        }

        NSApp.servicesProvider = ServicesProvider(appDelegate: self)
    }

    func applicationWillTerminate(_ notification: Notification) {
        Log.debug("application will terminate")
    }

    // MARK: Timer updating

    /**
     Determine time to the next top of second, and set a timer to call `nextSecondTimerDidFire` at that time.
     */
    func waitForNextSecond() {
        let elapsed = stopwatch.elapsedTimeInterval()
        let intervalToNextSecond = ceil(elapsed) - elapsed

        let t = Timer.scheduledTimer(timeInterval: intervalToNextSecond,
            target: self,
            selector: #selector(nextSecondTimerDidFire(_:)),
            userInfo: nil,
            repeats: false)
        t.tolerance = 0.25
    }

    /**
     Called at the top of each second.

     Updates the timer display if the timer is running.
     */
{'fraction_non_alphanumeric': 0.06939262796597523, 'fraction_numerical': 0.007312341441575884, 'mean_word_length': 2.6563011456628476, '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': '7466818', 'n_tokens_mistral': 1834, 'n_tokens_neox': 1682, 'n_words': 633}