OSDN Git Service

AppDelegateからウインドウに関する部分を分離した
[kcd/KCD.git] / KCD / StrengthenListViewController.swift
1 //
2 //  StrengthenListViewController.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2016/12/25.
6 //  Copyright © 2016年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 fileprivate let resourceName = "EnhancementListItem2"
12 fileprivate let resourceExtension = "plist"
13
14
15 fileprivate struct FilterCategories {
16     static let allType: [EquipmentType] = (1...100).flatMap { EquipmentType(rawValue: $0) }
17     static let canonType: [EquipmentType] = [.smallCaliberMainGun, .mediumCaliberMainGun,
18                                              .largeCaliberMainGun, .largeCaliberMainGunII]
19     static let torpedoType: [EquipmentType] = [.secondaryGun, .torpedo,
20                                                .antiAircraftGun, .antiSunmrinerSercher, .submarinTorpedo,
21                                                .largeSonar]
22     static let airplaneType: [EquipmentType] = [.fighter, .bomber, .attacker, .searcher,
23                                                 .airplaneSearcher, .airplaneBomber,
24                                                 .largeAirplane, .airplaneFighter,
25                                                 .landAttecker, .localFighter,
26                                                 .jetFighter, .jetBomber,
27                                                 .jetAttacker, .jetSearcher,
28                                                 .searcherII]
29     static let radarType: [EquipmentType] = [.smallRadar, .largeRadar,
30                                              .sonar, .depthCharge,
31                                              .SubmarineEquipment]
32     static let otherType: [EquipmentType] = {
33         return allType
34             .filter { !canonType.contains($0) }
35             .filter { !torpedoType.contains($0) }
36             .filter { !airplaneType.contains($0) }
37             .filter { !radarType.contains($0) }
38     }()
39     
40     enum FilterType: Int {
41         case all = 0
42         case canon
43         case torpedo
44         case airplane
45         case radar
46         case other
47     }
48     
49     let categories: [EquipmentType]
50     
51     init(type: FilterType) {
52         switch type {
53         case .all: categories = FilterCategories.allType
54         case .canon: categories = FilterCategories.canonType
55         case .torpedo: categories = FilterCategories.torpedoType
56         case .airplane: categories = FilterCategories.airplaneType
57         case .radar: categories = FilterCategories.radarType
58         case .other: categories = FilterCategories.otherType
59         }
60     }
61 }
62
63 class StrengthenListViewController: MainTabVIewItemViewController {
64     
65     @IBOutlet weak var tableView: NSTableView!
66     
67     dynamic var itemList: Any { return filteredItems as Any }
68     dynamic var offsetDay: Int = 0 {
69         didSet { buildList() }
70     }
71     dynamic var filterType: Int = 0 {
72         didSet {
73             if let t = FilterCategories.FilterType(rawValue: filterType) {
74                 showsTypes = FilterCategories(type: t).categories
75             } else {
76                 showsTypes = FilterCategories.allType
77             }
78             buildList()
79         }
80     }
81     override var nibName: String! {
82         return "StrengthenListViewController"
83     }
84     
85     fileprivate var filteredItems: [StrengthenListItem] = [] {
86         willSet { willChangeValue(forKey: #keyPath(itemList)) }
87         didSet { didChangeValue(forKey: #keyPath(itemList)) }
88     }
89     
90     private let notifier = PeriodicNotifier(hour: 0, minutes: 0)
91     private let plistDownloadNotifier = PeriodicNotifier(hour: 23, minutes: 55)
92     private let downloader = EnhancementListItemDownloader()
93     private var equipmentStrengthenList: [EnhancementListItem] = []
94     private var showsTypes: [EquipmentType] = FilterCategories.allType
95     
96     override func viewDidLoad() {
97         super.viewDidLoad()
98         
99         if let url = Bundle.main.url(forResource: resourceName, withExtension: resourceExtension),
100             let data = try? Data(contentsOf: url) {
101             guard let array = NSKeyedUnarchiver.unarchiveObject(with: data) as? [EnhancementListItem]
102                 else {
103                     print("\(resourceName).\(resourceExtension) not found.")
104                     return
105             }
106             equipmentStrengthenList = array
107             buildList()
108         }
109         
110         let nc = NotificationCenter.default
111         nc.addObserver(forName: .Periodic, object: notifier, queue: nil) { [weak self] _ in
112             guard let `self` = self else { return }
113             self.buildList()
114         }
115         nc.addObserver(forName: .Periodic, object: plistDownloadNotifier, queue: nil) { [weak self] _ in
116             guard let `self` = self else { return }
117             self.downloadPList()
118         }
119         
120         #if DEBUG
121 //          downloadPList()
122         #else
123             downloadPList()
124         #endif
125     }
126     private func weekdayFiltered() -> [EnhancementListItem] {
127         if offsetDay == -1 { return allItemList() }
128         
129         let currentDay = NSCalendar.current.dateComponents([.weekday], from: Date())
130         var targetWeekday = currentDay.weekday! + offsetDay
131         if targetWeekday > 7 { targetWeekday = 1 }
132         return equipmentStrengthenList.filter { $0.weekday == targetWeekday }
133     }
134     private func convert(items: [EnhancementListItem]) -> [StrengthenListItem] {
135         guard let item = items.first else { return [] }
136         let group: StrengthenListItem = StrengthenListGroupItem(type: item.equipmentType)
137         let items: [StrengthenListItem] = items.map(StrengthenListEnhancementItem.init(item:))
138         return [group] + items
139     }
140     private func buildList() {
141         let filtered = weekdayFiltered()
142         filteredItems = filtered
143             .map { $0.equipmentType }
144             .unique()
145             .filter { showsTypes.contains($0) }
146             .map { type in filtered.filter { $0.equipmentType == type } }
147             .flatMap(convert)
148     }
149     private func downloadPList() {
150         downloader.download { [weak self] items in
151             guard let `self` = self else { return }
152             DispatchQueue.main.async {
153                 self.equipmentStrengthenList = items
154                 self.buildList()
155             }
156         }
157     }
158     private func packSecondShipName(_ items: [EnhancementListItem]) -> [String] {
159         return items.flatMap { $0.secondsShipNames }.unique()
160     }
161     private func allItemList() -> [EnhancementListItem] {
162         return equipmentStrengthenList
163             .map { $0.identifier }
164             .unique()
165             .map { identifier in equipmentStrengthenList.filter { $0.identifier == identifier } }
166             .flatMap { $0.first?.replace(secondsShipNames: packSecondShipName($0)) }
167     }
168 }
169
170 extension StrengthenListViewController: NSTableViewDelegate {
171     func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
172         let item = filteredItems[row]
173         return item.cellType.makeCellWithItem(item: item, tableView: tableView, owner: nil)
174     }
175     func tableView(_ tableView: NSTableView, isGroupRow row: Int) -> Bool {
176         return filteredItems[row] is StrengthenListGroupItem
177     }
178     func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
179         let item = filteredItems[row]
180         return item.cellType.estimateCellHeightForItem(item: item, tableView: tableView)
181     }
182 }
183
184 fileprivate class EnhancementListItemDownloader: NSObject, URLSessionDownloadDelegate {
185     override init() {
186         super.init()
187         
188         plistDownloadQueue = OperationQueue()
189         plistDownloadQueue.name = "StrengthenListViewControllerPlistDownloadQueue"
190         plistDownloadQueue.maxConcurrentOperationCount = 1
191         plistDownloadQueue.qualityOfService = .background
192         let configuration = URLSessionConfiguration.default
193         plistDownloadSession = URLSession(configuration: configuration,
194                                           delegate: self,
195                                           delegateQueue: plistDownloadQueue)
196     }
197     
198     private var plistDownloadSession: URLSession!
199     private var plistDownloadQueue: OperationQueue!
200     private var plistDownloadTask: URLSessionDownloadTask?
201     private var finishOperation: (([EnhancementListItem]) -> Void)?
202     
203     func download(completeHandler: @escaping ([EnhancementListItem]) -> Void) {
204         if let _ = plistDownloadTask { return }
205         // swiftlint:disable:next line_length
206         guard let plistURL = URL(string: "http://git.osdn.jp/view?p=kcd/KCD.git;a=blob;f=KCD/\(resourceName).\(resourceExtension);hb=HEAD")
207             else { return }
208         
209         finishOperation = completeHandler
210         plistDownloadTask = plistDownloadSession.downloadTask(with: plistURL)
211         plistDownloadTask?.resume()
212     }
213     
214     func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
215         plistDownloadTask = nil
216         guard let data = try? Data(contentsOf: location, options: []),
217             let list = NSKeyedUnarchiver.unarchiveObject(with: data as Data) as? [EnhancementListItem]
218             else { return }
219         finishOperation?(list)
220     }
221     func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
222         plistDownloadTask = nil
223     }
224 }