OSDN Git Service

MappingConfigurationをジェネリクスにした
[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
11 fileprivate enum ShipAPI: String {
12     case getMemberShip = "/kcsapi/api_get_member/ship"
13     case port = "/kcsapi/api_port/port"
14     case getMemberShip3 = "/kcsapi/api_get_member/ship3"
15     case kousyouGetShip = "/kcsapi/api_req_kousyou/getship"
16     case getMemberShipDeck = "/kcsapi/api_get_member/ship_deck"
17     case kaisouPowerUp = "/kcsapi/api_req_kaisou/powerup"
18     case kaisouSlotDeprive = "/kcsapi/api_req_kaisou/slot_deprive"
19 }
20
21 fileprivate func dataKey(_ apiResponse: APIResponse) -> String {
22     guard let shipApi = ShipAPI(rawValue: apiResponse.api)
23         else { return "api_data" }
24     switch shipApi {
25     case .port: return "api_data.api_ship"
26     case .getMemberShip3: return "api_data.api_ship_data"
27     case .kousyouGetShip: return "api_data.api_ship"
28     case .getMemberShipDeck: return "api_data.api_ship_data"
29     case .kaisouPowerUp: return "api_data.api_ship"
30     case .kaisouSlotDeprive: return "api_data.api_ship_data.api_set_ship"
31     case .getMemberShip: return "api_data"
32     }
33 }
34
35 extension MappingConfiguration {
36     func change(dataKey: String) -> MappingConfiguration {
37         return MappingConfiguration(entity: self.entity,
38                                     dataKey: dataKey,
39                                     primaryKey: self.primaryKey,
40                                     compositPrimaryKeys: self.compositPrimaryKeys,
41                                     editorStore: self.editorStore,
42                                     ignoreKeys: self.ignoreKeys)
43     }
44 }
45
46 class ShipMapper: JSONMapper {
47     typealias ObjectType = Ship
48     let apiResponse: APIResponse
49     let configuration: MappingConfiguration<Ship>
50     
51     required init(_ apiResponse: APIResponse) {
52         self.apiResponse = apiResponse
53         self.configuration = MappingConfiguration(entity: Ship.entity,
54                                                   dataKey: dataKey(apiResponse),
55                                                   editorStore: ServerDataStore.oneTimeEditor(),
56                                                   ignoreKeys:
57             ["api_gomes", "api_gomes2", "api_broken", "api_powup",
58              "api_voicef", "api_afterlv", "api_aftershipid", "api_backs",
59              "api_slotnum", "api_stype", "api_name", "api_yomi",
60              "api_raig", "api_luck", "api_saku", "api_raim", "api_baku",
61              "api_taik", "api_houg", "api_houm", "api_tyku",
62              "api_ndock_item", "api_star",
63              "api_ndock_time_str", "api_member_id",
64              "api_fuel_max", "api_bull_max"])
65         
66         // kaisouSlotDepriveでは同時に2種類のデータが入る
67         if let api = ShipAPI(rawValue: apiResponse.api),
68             api == .kaisouSlotDeprive {
69             let conf = self.configuration.change(dataKey: "api_data.api_ship_data.api_unset_ship")
70             ShipMapper(apiResponse, configuration: conf).commit()
71         }
72     }
73     private init(_ apiResponse: APIResponse, configuration: MappingConfiguration<Ship>) {
74         self.apiResponse = apiResponse
75         self.configuration = configuration
76     }
77     
78     private var registerIds: [Int] = []
79     private lazy var masterShips: [MasterShip] = {
80         return ServerDataStore.default.sortedMasterShipsById()
81     }()
82     private lazy var slotItems: [SlotItem] = {
83         return ServerDataStore.default.sortedSlotItemsById()
84     }()
85     private var isDeleteNotExist: Bool {
86         guard let shipApi = ShipAPI(rawValue: apiResponse.api)
87             else { return true }
88         switch shipApi {
89         case .getMemberShip3, .kousyouGetShip, .getMemberShipDeck,
90              .kaisouPowerUp, .kaisouSlotDeprive:
91             return false
92         default:
93             return true
94         }
95     }
96     private var store: ServerDataStore? {
97         return configuration.editorStore as? ServerDataStore
98     }
99     
100     func beginRegister(_ ship: Ship) {
101         ship.sally_area = nil
102     }
103     func handleExtraValue(_ value: Any, forKey key: String, to ship: Ship) -> Bool {
104         // 取得後破棄した装備のデータを削除するため保有IDを保存
105         if key == "api_id" {
106             guard let id = value as? Int
107                 else { return false }
108             registerIds.append(id)
109             return false
110         }
111         
112         if key == "api_ship_id" {
113             guard let masterId = value as? Int
114                 else { return false }
115             setMaster(masterId, to: ship)
116             return true
117         }
118         if key == "api_exp" {
119             guard let v = value as? [Any],
120                 let vv = v.first as? Int
121                 else { return false }
122             ship.exp = vv
123             return true
124         }
125         if key == "api_slot" {
126             guard let slotItems = value as? [Any]
127                 else { return false }
128             setSlot(slotItems, to: ship)
129             return false
130         }
131         if key == "api_slot_ex" {
132             guard let ex = value as? Int
133                 else { return false }
134             setExtraSlot(ex, to: ship)
135             return false
136         }
137         
138         return false
139     }
140     func finishOperating() {
141         if !isDeleteNotExist { return }
142         store?.ships(exclude: registerIds).forEach { store?.delete($0) }
143     }
144     
145     private func setMaster(_ masterId: Int, to ship: Ship) {
146         if ship.ship_id == masterId { return }
147         guard let mShip = masterShips.binarySearch(comparator: { $0.id ==? masterId }),
148             let masterShip = store?.object(with: mShip.objectID) as? MasterShip
149             else { return print("Can not convert to current moc object masterShip") }
150         ship.master_ship = masterShip
151         ship.ship_id = masterId
152     }
153     
154     private func setSlot(_ slotItems: [Any], to ship: Ship) {
155         guard let converSlotItems = slotItems as? [Int],
156             let store = store
157             else { return }
158         let newItems: [SlotItem] =
159             converSlotItems.flatMap { (item: Int) in
160                 if item == 0 || item == -1 { return nil }
161                 guard let found = self.slotItems.binarySearch(comparator: { $0.id ==? item }),
162                     let slotItem = store.object(with: found.objectID) as? SlotItem
163                     else {
164                         let maxV = converSlotItems.last
165                         if maxV != nil && maxV! < item {
166                             #if DEBUG
167                                 print("item is maybe unregistered, so it is new ship's equipment.")
168                             #endif
169                             return nil
170                         }
171                         print("Can not convert to current moc object slotItem")
172                         return nil
173                 }
174                 return slotItem
175         }
176         ship.equippedItem = NSOrderedSet(array: newItems)
177     }
178     private func setExtraSlot(_ exSlotItem: Int, to ship: Ship) {
179         guard exSlotItem != -1,
180             exSlotItem != 0
181             else { return }
182         guard let found = slotItems.binarySearch(comparator: { $0.id ==? exSlotItem }),
183             let ex = store?.object(with: found.objectID) as? SlotItem
184             else { return print("Can not convert to current moc object") }
185         ship.extraItem = ex
186     }
187 }