OSDN Git Service

洋上補給の補強増設用のショートネームをつけた
[kcd/KCD.git] / KCD / MissionStatus.swift
index eb3838b..8f594e6 100644 (file)
 
 import Cocoa
 
-fileprivate enum State: Int {
-    case none = 0
-    case hasMission = 1
-    case finish = 2
-    case earlyReturn = 3
+protocol MissionStatusObserver: class {
+    
+    func didUpdate(state: MissionStatus)
 }
 
-class MissionStatus: NSObject {
-    private let number: Int
-    private let controller: NSArrayController
-    private var didNotify = false
-    private var realTime: TimeInterval = 0.0 {
-        didSet { time = realTime as NSNumber }
+protocol DeckMissionObserverDelegate: class {
+    
+    func didChangeState(deck: Deck)
+}
+
+final class DeckMissionObserver {
+    
+    private let deck: Deck
+    private var observation: NSKeyValueObservation?
+    
+    weak var delegate: DeckMissionObserverDelegate? {
+        
+        didSet {
+            delegate?.didChangeState(deck: deck)
+        }
+    }
+    
+    init(deck: Deck) {
+        
+        self.deck = deck
+        
+        observation = deck.observe(\Deck.mission_2) { _, _ in
+            self.delegate?.didChangeState(deck: deck)
+        }
     }
-    dynamic var name: String?
-    dynamic var time: NSNumber?
-    dynamic var state: NSNumber?
-    dynamic var missionId: NSNumber? {
-        didSet { updateState() }
+}
+
+final class MissionStatus: NSObject {
+    
+    private enum State: Int {
+        
+        case none = 0
+        case hasMission = 1
+        case finish = 2
+        case earlyReturn = 3
+        
+        case unknown = 999999
+    }
+    
+    static func valid(number: Int) -> Bool {
+        
+        return 2...4 ~= number
+    }
+    
+    let number: Int
+    private let observer: DeckMissionObserver
+    
+    private(set) var name: String?
+    private(set) var time: TimeInterval?
+    
+    private var state: State = .unknown
+    private var rawState: Int = 0 {
+        
+        didSet {
+            state = State(rawValue: rawState) ?? .unknown
+        }
     }
+    private var missionId: Int = 0
+    private var milliseconds: Int = 0
+    private var fleetName: String = ""
+    
+    private var didNotify = false
+    
+    weak var delegate: MissionStatusObserver?
     
+    /// CAUTION: 初回起動時/マスタ更新時にはデータがないので失敗する
     init?(number: Int) {
-        guard (2...4).contains(number) else { return nil }
+        
+        guard MissionStatus.valid(number: number) else { return nil }
+        
         self.number = number
-        controller = NSArrayController()
+        
+        guard let deck = ServerDataStore.default.deck(by: number) else { return nil }
+        self.observer = DeckMissionObserver(deck: deck)
+        
         super.init()
-        controller.managedObjectContext = ServerDataStore.default.managedObjectContext
-        controller.entityName = Entity.deck.name
-        controller.fetchPredicate = NSPredicate(format: "id = %ld", number)
-        controller.automaticallyRearrangesObjects = true
-        controller.fetch(nil)
-        
-        bind("state", to: controller, withKeyPath: "selection.mission_0")
-        bind("missionId", to: controller, withKeyPath: "selection.mission_1")
+        
+        observer.delegate = self
     }
     
     private func updateState() {
-        guard let stat = State(rawValue: state as! Int)
-            else { return print("unknown State") }
-        if stat == .none || stat == .finish  {
-            if stat == .none { didNotify = false }
+        
+        switch state {
+            
+        case .none, .finish:
+            didNotify = false
             name = nil
             time = nil
-            return
+            
+        case .hasMission, .earlyReturn:
+            name = ServerDataStore.default.masterMission(by: missionId)?.name ?? "Unknown"
+            
+        case .unknown:
+            Logger.shared.log("unknown State")
         }
         
-        guard let missionId = self.missionId as? Int
-            else { return }
-        guard let mission = ServerDataStore.default.masterMission(by: missionId)
-            else {
-                name = "Unknown"
-                DispatchQueue(label: "MissionStatus")
-                    .asyncAfter(deadline: .now() + 0.33) {
-                        self.updateState()
-                }
-                return
-        }
-        name = mission.name
+        delegate?.didUpdate(state: self)
     }
+}
+
+extension MissionStatus: DockInformationUpdater {
     
     func update() {
-        if name == nil {
-            time = nil
-            return
-        }
-        guard let t = controller.value(forKeyPath: "selection.mission_2") as? Int
-            else {
-                name = nil
-                time = nil
-                return
+        
+        defer {
+            delegate?.didUpdate(state: self)
         }
-        let compTime = TimeInterval(Int(t / 1_000))
-        let now = Date()
-        let diff = compTime - now.timeIntervalSince1970
         
-        if diff < 0 { realTime = 0 }
-        else { realTime = diff }
+        guard let name = name else { return }
+        
+        let compTime = TimeInterval(Int(milliseconds / 1_000))
+        let diff = compTime - Date().timeIntervalSince1970
+        
+        // set to 0. if diff is less than 0.
+        time = max(0, diff)
         
+        // notify UserNotification.
         if didNotify { return }
         if diff >= 1 * 60 { return }
         
-        guard let fleetName = controller.value(forKeyPath: "selection.name") as? String
-            else { return }
         let notification = NSUserNotification()
-        let format = NSLocalizedString("%@ Will Return From Mission.", comment: "%@ Will Return From Mission.")
+        let format = LocalizedStrings.missionWillReturnMessage.string
         notification.title = String(format: format, fleetName)
-        let txtFormat = NSLocalizedString("%@ Will Return From %@.", comment: "%@ Will Return From %@.")
-        notification.informativeText = String(format: txtFormat, fleetName, name!)
-        if UserDefaults.standard.playFinishMissionSound {
+        let txtFormat = LocalizedStrings.missionWillReturnInformation.string
+        notification.informativeText = String(format: txtFormat, fleetName, name)
+        
+        if UserDefaults.standard[.playFinishMissionSound] {
+            
             notification.soundName = NSUserNotificationDefaultSoundName
         }
+        
         NSUserNotificationCenter.default.deliver(notification)
+        
         didNotify = true
     }
 }
+
+extension MissionStatus: DeckMissionObserverDelegate {
+    
+    func didChangeState(deck: Deck) {
+        
+        rawState = deck.mission_0
+        missionId = deck.mission_1
+        milliseconds = deck.mission_2
+        fleetName = deck.name
+        
+        updateState()
+    }
+}