2 // StrengthenListViewController.swift
5 // Created by Hori,Masaki on 2016/12/25.
6 // Copyright © 2016年 Hori,Masaki. All rights reserved.
11 private let resourceName = "EnhancementListItem2"
12 private let resourceExtension = "plist"
15 private struct FilterCategories {
17 static let allType: [EquipmentType] = (1...100).flatMap { EquipmentType(rawValue: $0) }
18 static let canonType: [EquipmentType] = [.smallCaliberMainGun, .mediumCaliberMainGun,
19 .largeCaliberMainGun, .largeCaliberMainGunII]
20 static let torpedoType: [EquipmentType] = [.secondaryGun, .torpedo,
21 .antiAircraftGun, .antiSunmrinerSercher, .submarinTorpedo,
23 static let airplaneType: [EquipmentType] = [.fighter, .bomber, .attacker, .searcher,
24 .airplaneSearcher, .airplaneBomber,
25 .largeAirplane, .airplaneFighter,
26 .landAttecker, .localFighter,
27 .jetFighter, .jetBomber,
28 .jetAttacker, .jetSearcher,
30 static let radarType: [EquipmentType] = [.smallRadar, .largeRadar,
33 static let otherType: [EquipmentType] = {
35 .filter { !canonType.contains($0) }
36 .filter { !torpedoType.contains($0) }
37 .filter { !airplaneType.contains($0) }
38 .filter { !radarType.contains($0) }
41 enum FilterType: Int {
51 let categories: [EquipmentType]
53 init(type: FilterType) {
56 case .all: categories = FilterCategories.allType
57 case .canon: categories = FilterCategories.canonType
58 case .torpedo: categories = FilterCategories.torpedoType
59 case .airplane: categories = FilterCategories.airplaneType
60 case .radar: categories = FilterCategories.radarType
61 case .other: categories = FilterCategories.otherType
66 final class StrengthenListViewController: MainTabVIewItemViewController {
68 @IBOutlet weak var tableView: NSTableView!
70 @objc dynamic var itemList: Any { return filteredItems as Any }
71 @objc dynamic var offsetDay: Int = 0 {
73 didSet { buildList() }
75 @objc dynamic var filterType: Int = 0 {
78 if let t = FilterCategories.FilterType(rawValue: filterType) {
80 showsTypes = FilterCategories(type: t).categories
84 showsTypes = FilterCategories.allType
90 override var nibName: NSNib.Name {
92 return .nibName(instanceOf: self)
95 private var filteredItems: [StrengthenListItem] = [] {
97 willSet { willChangeValue(forKey: #keyPath(itemList)) }
98 didSet { didChangeValue(forKey: #keyPath(itemList)) }
101 private let notifier = PeriodicNotifier(hour: 0, minutes: 0)
102 private let plistDownloadNotifier = PeriodicNotifier(hour: 23, minutes: 55)
103 private let downloader = EnhancementListItemDownloader()
104 private var equipmentStrengthenList: [EnhancementListItem] = []
105 private var showsTypes: [EquipmentType] = FilterCategories.allType
107 override func viewDidLoad() {
111 if let url = Bundle.main.url(forResource: resourceName, withExtension: resourceExtension),
112 let data = try? Data(contentsOf: url) {
114 guard let array = NSKeyedUnarchiver.unarchiveObject(with: data) as? [EnhancementListItem]
116 print("\(resourceName).\(resourceExtension) not found.")
120 equipmentStrengthenList = array
124 let nc = NotificationCenter.default
125 nc.addObserver(forName: .Periodic, object: notifier, queue: nil) { [weak self] _ in
127 guard let `self` = self else { return }
132 nc.addObserver(forName: .Periodic, object: plistDownloadNotifier, queue: nil) { [weak self] _ in
134 guard let `self` = self else { return }
146 private func weekdayFiltered() -> [EnhancementListItem] {
148 if offsetDay == -1 { return allItemList() }
150 let currentDay = NSCalendar.current.dateComponents([.weekday], from: Date())
151 var targetWeekday = currentDay.weekday! + offsetDay
152 if targetWeekday > 7 { targetWeekday = 1 }
154 return equipmentStrengthenList.filter { $0.weekday == targetWeekday }
157 private func convert(items: [EnhancementListItem]) -> [StrengthenListItem] {
159 guard let item = items.first
162 let group: StrengthenListItem = StrengthenListGroupItem(type: item.equipmentType)
163 let items: [StrengthenListItem] = items.map(StrengthenListEnhancementItem.init(item:))
165 return [group] + items
168 private func buildList() {
170 let filtered = weekdayFiltered()
171 filteredItems = filtered
172 .map { $0.equipmentType }
174 .filter { showsTypes.contains($0) }
175 .map { type in filtered.filter { $0.equipmentType == type } }
179 private func downloadPList() {
181 downloader.download { [weak self] items in
183 guard let `self` = self else { return }
185 DispatchQueue.main.async {
187 self.equipmentStrengthenList = items
193 private func packSecondShipName(_ items: [EnhancementListItem]) -> [String] {
195 return items.flatMap { $0.secondsShipNames }.unique()
198 private func allItemList() -> [EnhancementListItem] {
200 return equipmentStrengthenList
201 .map { $0.identifier }
203 .map { identifier in equipmentStrengthenList.filter { $0.identifier == identifier } }
204 .flatMap { $0.first?.replace(secondsShipNames: packSecondShipName($0)) }
208 extension StrengthenListViewController: NSTableViewDelegate {
210 func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
212 let item = filteredItems[row]
213 return item.cellType.makeCellWithItem(item: item, tableView: tableView, owner: nil)
216 func tableView(_ tableView: NSTableView, isGroupRow row: Int) -> Bool {
218 return filteredItems[row] is StrengthenListGroupItem
221 func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
223 let item = filteredItems[row]
225 return item.cellType.estimateCellHeightForItem(item: item, tableView: tableView)
229 private final class EnhancementListItemDownloader: NSObject, URLSessionDownloadDelegate {
235 plistDownloadQueue = OperationQueue()
236 plistDownloadQueue.name = "StrengthenListViewControllerPlistDownloadQueue"
237 plistDownloadQueue.maxConcurrentOperationCount = 1
238 plistDownloadQueue.qualityOfService = .background
240 let configuration = URLSessionConfiguration.default
242 plistDownloadSession = URLSession(configuration: configuration,
244 delegateQueue: plistDownloadQueue)
247 private var plistDownloadSession: URLSession!
248 private var plistDownloadQueue: OperationQueue!
249 private var plistDownloadTask: URLSessionDownloadTask?
250 private var finishOperation: (([EnhancementListItem]) -> Void)?
252 func download(completeHandler: @escaping ([EnhancementListItem]) -> Void) {
254 if let _ = plistDownloadTask { return }
256 guard let plistURL = URL(string: "http://git.osdn.jp/view?p=kcd/KCD.git;a=blob;f=KCD/\(resourceName).\(resourceExtension);hb=HEAD")
259 finishOperation = completeHandler
260 plistDownloadTask = plistDownloadSession.downloadTask(with: plistURL)
261 plistDownloadTask?.resume()
264 func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
266 plistDownloadTask = nil
268 guard let data = try? Data(contentsOf: location, options: []),
269 let list = NSKeyedUnarchiver.unarchiveObject(with: data as Data) as? [EnhancementListItem]
272 finishOperation?(list)
275 func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
277 plistDownloadTask = nil