2 // FleetViewController.swift
5 // Created by Hori,Masaki on 2016/12/27.
6 // Copyright © 2016年 Hori,Masaki. All rights reserved.
11 enum FleetViewType: Int {
12 case detailViewType = 0
13 case minimumViewType = 1
14 case miniVierticalType = 2
17 fileprivate var shipKeysContext: Int = 0
18 fileprivate var shipsContext: Int = 0
20 class FleetViewController: NSViewController {
26 static let oldStyleFleetViewHeight: CGFloat = 128.0
27 static let detailViewHeight: CGFloat = 288.0
28 static let heightDifference: CGFloat = detailViewHeight - oldStyleFleetViewHeight
29 private static let maxFleetNumber: Int = 4
31 fileprivate let details: [ShipDetailViewController]
32 private let shipKeys = ["ship_0", "ship_1", "ship_2", "ship_3", "ship_4", "ship_5"]
33 private let type: FleetViewType
34 private let fleetController = NSObjectController()
35 private let shipObserveKeys = ["sakuteki_0", "seiku", "totalSeiku", "lv", "totalDrums"]
37 init?(viewType: FleetViewType) {
40 let shipiewType: ShipDetailViewType = {
42 case .detailViewType: return .full
43 case .minimumViewType: return .medium
44 case .miniVierticalType: return .minimum
47 details = (1...6).map {
48 guard let res = ShipDetailViewController(type: shipiewType)
49 else { fatalError("Can not create ShipDetailViewController") }
54 let nibName: String = {
56 case .detailViewType: return "FleetViewController"
57 case .minimumViewType: return "FleetMinimumViewController"
58 case .miniVierticalType: return "VerticalFleetViewController"
61 super.init(nibName: nibName, bundle: nil)
63 required init?(coder: NSCoder) {
64 fatalError("init(coder:) has not been implemented")
67 NotificationCenter.default.removeObserver(self)
70 @IBOutlet weak var placeholder01: NSView!
71 @IBOutlet weak var placeholder02: NSView!
72 @IBOutlet weak var placeholder03: NSView!
73 @IBOutlet weak var placeholder04: NSView!
74 @IBOutlet weak var placeholder05: NSView!
75 @IBOutlet weak var placeholder06: NSView!
77 dynamic var fleetNumber: Int = 1 {
79 ServerDataStore.default
80 .deck(by: fleetNumber)
84 dynamic var fleet: Deck? {
85 get { return representedObject as? Deck }
87 representedObject = newValue
88 title = newValue?.name
93 var enableAnimation: Bool = false
94 var shipOrder: FleetViewController.ShipOrder = .doubleLine {
96 if shipOrder == newValue { return }
98 case .doubleLine: reorderShipToDoubleLine()
99 case .leftToRight: reorderShipToLeftToRight()
103 var canDivide: Bool { return type == .detailViewType }
104 var normalHeight: CGFloat {
106 case .detailViewType: return FleetViewController.detailViewHeight
107 case .minimumViewType: return FleetViewController.oldStyleFleetViewHeight
108 case .miniVierticalType: return 0.0
111 var upsideHeight: CGFloat {
113 case .detailViewType: return 159.0
114 case .minimumViewType: return FleetViewController.oldStyleFleetViewHeight
115 case .miniVierticalType: return 0.0
118 var totalSakuteki: Int { return ships.reduce(0) { $0 + $1.sakuteki_0 } }
119 var totalSeiku: Int { return ships.reduce(0) { $0 + $1.seiku } }
120 var totalCalclatedSeiku: Int { return ships.reduce(0) { $0 + $1.totalSeiku } }
121 var totalLevel: Int { return ships.reduce(0) { $0 + $1.lv } }
122 var totalDrums: Int { return ships.reduce(0) { $0 + $1.totalDrums } }
124 fileprivate var ships: [Ship] = [] {
126 ships.forEach { ship in
127 shipObserveKeys.forEach {
128 ship.removeObserver(self, forKeyPath: $0)
133 ships.forEach { ship in
134 shipObserveKeys.forEach {
135 ship.addObserver(self, forKeyPath: $0, context: &shipsContext)
140 private(set) var anchorageRepair = AnchorageRepairManager.default
141 dynamic fileprivate(set) var repairTime: NSNumber?
143 override func viewDidLoad() {
146 fleetController.bind("content", to:self, withKeyPath:#keyPath(fleet), options:nil)
147 fleetController.addObserver(self, forKeyPath:"selection.name", context:nil)
149 let keyPath = "selection.\($0)"
150 fleetController.addObserver(self, forKeyPath:keyPath, context:&shipKeysContext)
153 buildAnchorageRepairHolder()
156 .appended { placeholder01 }
157 .appended { placeholder02 }
158 .appended { placeholder03 }
159 .appended { placeholder04 }
160 .appended { placeholder05 }
161 .appended { placeholder06 }
164 guard let view = $0.element else { return }
165 let detail = details[$0.offset]
166 detail.view.frame = view.frame
167 detail.view.autoresizingMask = view.autoresizingMask
168 self.view.replaceSubview(view, with: detail.view)
172 NotificationCenter.default
173 .addObserver(forName: .DidPrepareFleet, object: nil, queue: nil) { [weak self] _ in
174 guard let `self` = self else { return }
175 self.notifyChangeValue(forKey: #keyPath(fleetNumber))
178 override func observeValue(forKeyPath keyPath: String?,
180 change: [NSKeyValueChangeKey: Any]?,
181 context: UnsafeMutableRawPointer?) {
183 if keyPath == "selection.name" {
188 if context == &shipKeysContext {
193 if let keyPath = keyPath {
194 if context == &shipsContext {
197 notifyChangeValue(forKey: #keyPath(totalSakuteki))
199 notifyChangeValue(forKey: #keyPath(totalSeiku))
201 notifyChangeValue(forKey: #keyPath(totalCalclatedSeiku))
203 notifyChangeValue(forKey: #keyPath(totalLevel))
205 notifyChangeValue(forKey: #keyPath(totalDrums))
212 super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
215 @IBAction func selectNextFleet(_ sender: AnyObject?) {
216 let next = fleetNumber + 1
217 fleetNumber = next <= FleetViewController.maxFleetNumber ? next : 1
219 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
220 let prev = fleetNumber - 1
221 fleetNumber = prev > 0 ? prev : 4
224 private func setupShips() {
225 let array: [Ship?] = (0..<6).map { fleet?[$0] }
226 zip(details, array).forEach { $0.0.ship = $0.1 }
227 ships = array.flatMap { $0 }
230 .appended { #keyPath(totalSakuteki) }
231 .appended { #keyPath(totalSeiku) }
232 .appended { #keyPath(totalCalclatedSeiku) }
233 .appended { #keyPath(totalLevel) }
234 .appended { #keyPath(totalDrums) }
235 .appended { #keyPath(repairable) }
236 .forEach { notifyChangeValue(forKey: $0) }
240 extension FleetViewController {
241 private func reorder(order: [Int]) {
242 guard order.count == 6 else {
243 print("FleetViewController: order count is not 6.")
246 let views: [NSView] = details.map { $0.view }
247 let options: [NSAutoresizingMaskOptions] = views.map { $0.autoresizingMask }
248 let reorderedOptions = order.map { options[$0] }
249 zip(views, reorderedOptions).forEach { $0.0.autoresizingMask = $0.1 }
251 let frames: [NSRect] = views.map { $0.frame }
252 let reorderedFrames = order.map { frames[$0] }
253 zip(views, reorderedFrames)
254 .forEach { $0.0.setFrame($0.1, animate: enableAnimation) }
256 fileprivate func reorderShipToDoubleLine() {
257 reorder(order: [0, 3, 1, 4, 2, 5])
259 fileprivate func reorderShipToLeftToRight() {
260 reorder(order: [0, 2, 4, 1, 3, 5])
264 extension FleetViewController {
265 func buildAnchorageRepairHolder() {
266 AppDelegate.shared.addCounterUpdate { [weak self] in
267 guard let `self` = self else { return }
268 self.repairTime = self.calcRepairTime()
271 private func calcRepairTime() -> NSNumber? {
272 let time = anchorageRepair.repairTime
273 let complete = time.timeIntervalSince1970
274 let now = Date(timeIntervalSinceNow: 0.0)
275 let diff = complete - now.timeIntervalSince1970
276 return NSNumber(value: diff + 20.0 * 60)
278 private var repairShipIds: [Int] { return [19] }
279 dynamic var repairable: Bool {
280 guard let flagShip = fleet?[0]
281 else { return false }
282 return repairShipIds.contains(flagShip.master_ship.stype.id)