OSDN Git Service

ObjectTypeを明示せず推論させるようにした
[kcd/KCD.git] / KCD / ShipMapper.swift
1 //
2 //  ShipMapper.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2017/02/23.
6 //  Copyright © 2017年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10 import SwiftyJSON
11
12 extension MappingConfiguration {
13     
14     func change(dataKeys: [String]) -> MappingConfiguration {
15         
16         return MappingConfiguration(entity: self.entity,
17                                     dataKeys: dataKeys,
18                                     primaryKeys: self.primaryKeys,
19                                     editorStore: self.editorStore,
20                                     ignoreKeys: self.ignoreKeys)
21     }
22 }
23
24 final class ShipMapper: JSONMapper {
25     
26     let apiResponse: APIResponse
27     let configuration: MappingConfiguration<Ship>
28     
29     required init(_ apiResponse: APIResponse) {
30         
31         self.apiResponse = apiResponse
32         self.configuration = MappingConfiguration(entity: Ship.entity,
33                                                   dataKeys: ShipMapper.dataKeys(apiResponse),
34                                                   editorStore: ServerDataStore.oneTimeEditor(),
35                                                   ignoreKeys:
36             ["api_gomes", "api_gomes2", "api_broken", "api_powup",
37              "api_voicef", "api_afterlv", "api_aftershipid", "api_backs",
38              "api_slotnum", "api_stype", "api_name", "api_yomi",
39              "api_raig", "api_luck", "api_saku", "api_raim", "api_baku",
40              "api_taik", "api_houg", "api_houm", "api_tyku",
41              "api_ndock_item", "api_star",
42              "api_ndock_time_str", "api_member_id",
43              "api_fuel_max", "api_bull_max"])
44         
45         // kaisouSlotDepriveでは同時に2種類のデータが入る
46         if let api = ShipAPI(rawValue: apiResponse.api),
47             api == .kaisouSlotDeprive {
48             
49             let conf = self.configuration.change(dataKeys: ["api_data", "api_ship_data", "api_unset_ship"])
50             ShipMapper(apiResponse, configuration: conf).commit()
51         }
52     }
53     
54     private init(_ apiResponse: APIResponse, configuration: MappingConfiguration<Ship>) {
55         
56         self.apiResponse = apiResponse
57         self.configuration = configuration
58     }
59     
60     
61     private enum ShipAPI: String {
62         
63         case getMemberShip = "/kcsapi/api_get_member/ship"
64         case port = "/kcsapi/api_port/port"
65         case getMemberShip3 = "/kcsapi/api_get_member/ship3"
66         case kousyouGetShip = "/kcsapi/api_req_kousyou/getship"
67         case getMemberShipDeck = "/kcsapi/api_get_member/ship_deck"
68         case kaisouPowerUp = "/kcsapi/api_req_kaisou/powerup"
69         case kaisouSlotDeprive = "/kcsapi/api_req_kaisou/slot_deprive"
70     }
71     
72     private class func dataKeys(_ apiResponse: APIResponse) -> [String] {
73         
74         guard let shipApi = ShipAPI(rawValue: apiResponse.api) else { return ["api_data"] }
75         
76         switch shipApi {
77         case .port: return ["api_data", "api_ship"]
78             
79         case .getMemberShip3: return ["api_data", "api_ship_data"]
80             
81         case .kousyouGetShip: return ["api_data", "api_ship"]
82             
83         case .getMemberShipDeck: return ["api_data", "api_ship_data"]
84             
85         case .kaisouPowerUp: return ["api_data", "api_ship"]
86             
87         case .kaisouSlotDeprive: return ["api_data", "api_ship_data", "api_set_ship"]
88             
89         case .getMemberShip: return ["api_data"]
90         }
91     }
92     
93     private var registerIds: [Int] = []
94     private lazy var masterShips: [MasterShip] = {
95         
96         return ServerDataStore.default.sortedMasterShipsById()
97     }()
98     private lazy var slotItems: [SlotItem] = {
99         
100         return ServerDataStore.default.sortedSlotItemsById()
101     }()
102     private var isDeleteNotExist: Bool {
103         
104         guard let shipApi = ShipAPI(rawValue: apiResponse.api) else { return true }
105         
106         switch shipApi {
107         case .getMemberShip3, .kousyouGetShip, .getMemberShipDeck,
108              .kaisouPowerUp, .kaisouSlotDeprive:
109             return false
110             
111         default:
112             return true
113         }
114     }
115     
116     private var store: ServerDataStore? {
117         
118         return configuration.editorStore as? ServerDataStore
119     }
120     
121     func beginRegister(_ ship: Ship) {
122         
123         ship.sally_area = nil
124     }
125     
126     func handleExtraValue(_ value: JSON, forKey key: String, to ship: Ship) -> Bool {
127         
128         // 取得後破棄した装備のデータを削除するため保有IDを保存
129         if key == "api_id" {
130             
131             guard let id = value.int else { return false }
132             
133             registerIds.append(id)
134             
135             return false
136         }
137         
138         if key == "api_ship_id" {
139             
140             guard let masterId = value.int else { return false }
141             
142             setMaster(masterId, to: ship)
143             
144             return true
145         }
146         
147         if key == "api_exp" {
148             
149             guard let exp = value[0].int else { return false }
150             
151             ship.exp = exp
152             
153             return true
154         }
155         
156         if key == "api_slot" {
157             
158             setSlot(value, to: ship)
159             
160             return false
161         }
162         
163         if key == "api_slot_ex" {
164             
165             guard let ex = value.int else { return false }
166             
167             setExtraSlot(ex, to: ship)
168             
169             return false
170         }
171         
172         return false
173     }
174     
175     func finishOperating() {
176         
177         if !isDeleteNotExist { return }
178         
179         store?.ships(exclude: registerIds).forEach { store?.delete($0) }
180     }
181     
182     private func setMaster(_ masterId: Int, to ship: Ship) {
183         
184         if ship.ship_id == masterId { return }
185         
186         guard let mShip = masterShips.binarySearch(comparator: { $0.id ==? masterId }),
187             let masterShip = store?.object(of: MasterShip.entity, with: mShip.objectID) else {
188                 
189                 print("Can not convert to current moc object masterShip")
190                 return
191         }
192         
193         ship.master_ship = masterShip
194         ship.ship_id = masterId
195     }
196     
197     private func setSlot(_ slotItems: JSON, to ship: Ship) {
198         
199         guard let converSlotItems = slotItems.arrayObject as? [Int] else { return }
200         guard let store = store else { return }
201         
202         let newItems: [SlotItem] =
203             converSlotItems.flatMap { item in
204                 
205                 if item == 0 || item == -1 { return nil }
206                 
207                 guard let found = self.slotItems.binarySearch(comparator: { $0.id ==? item }),
208                     let slotItem = store.object(of: SlotItem.entity, with: found.objectID) else {
209                         
210                         let maxV = converSlotItems.last
211                         if maxV != nil, maxV! < item {
212                             
213                             #if DEBUG
214                                 print("item is maybe unregistered, so it is new ship's equipment.")
215                             #endif
216                             return nil
217                         }
218                         print("Can not convert to current moc object slotItem")
219                         return nil
220                 }
221                 
222                 return slotItem
223         }
224         
225         ship.equippedItem = NSOrderedSet(array: newItems)
226     }
227     
228     private func setExtraSlot(_ exSlotItem: Int, to ship: Ship) {
229         
230         guard exSlotItem != -1, exSlotItem != 0 else { return }
231         guard let found = slotItems.binarySearch(comparator: { $0.id ==? exSlotItem }),
232             let ex = store?.object(of: SlotItem.entity, with: found.objectID) else {
233                 
234                 print("Can not convert to current moc object")
235                 return
236         }
237         
238         ship.extraItem = ex
239     }
240 }