//
import Cocoa
+import SwiftyJSON
-fileprivate enum ShipAPI: String {
- case getMemberShip = "/kcsapi/api_get_member/ship"
- case port = "/kcsapi/api_port/port"
- case getMemberShip3 = "/kcsapi/api_get_member/ship3"
- case kousyouGetShip = "/kcsapi/api_req_kousyou/getship"
- case getMemberShipDeck = "/kcsapi/api_get_member/ship_deck"
- case kaisouPowerUp = "/kcsapi/api_req_kaisou/powerup"
- case kaisouSlotDeprive = "/kcsapi/api_req_kaisou/slot_deprive"
-}
-
-fileprivate func dataKey(_ apiResponse: APIResponse) -> String {
- guard let shipApi = ShipAPI(rawValue: apiResponse.api)
- else { return "api_data" }
- switch shipApi {
- case .port: return "api_data.api_ship"
- case .getMemberShip3: return "api_data.api_ship_data"
- case .kousyouGetShip: return "api_data.api_ship"
- case .getMemberShipDeck: return "api_data.api_ship_data"
- case .kaisouPowerUp: return "api_data.api_ship"
- case .kaisouSlotDeprive: return "api_data.api_ship_data.api_set_ship"
- case .getMemberShip: return "api_data"
- }
-}
-
-extension MappingConfiguration {
- func change(dataKey: String) -> MappingConfiguration {
- return MappingConfiguration(entityType: self.entityType,
- dataKey: dataKey,
- primaryKey: self.primaryKey,
- compositPrimaryKeys: self.compositPrimaryKeys,
- editorStore: self.editorStore,
- ignoreKeys: self.ignoreKeys)
- }
-}
-
-class ShipMapper: JSONMapper {
+final class ShipMapper: JSONMapper {
+
+ private static let ignoreKeys = ["api_gomes", "api_gomes2", "api_broken", "api_powup",
+ "api_voicef", "api_afterlv", "api_aftershipid", "api_backs",
+ "api_slotnum", "api_stype", "api_name", "api_yomi",
+ "api_raig", "api_luck", "api_saku", "api_raim", "api_baku",
+ "api_taik", "api_houg", "api_houm", "api_tyku",
+ "api_ndock_item", "api_star",
+ "api_ndock_time_str", "api_member_id",
+ "api_fuel_max", "api_bull_max"]
+
let apiResponse: APIResponse
- let configuration: MappingConfiguration
+ let configuration: MappingConfiguration<Ship>
required init(_ apiResponse: APIResponse) {
+
self.apiResponse = apiResponse
- self.configuration = MappingConfiguration(entityType: Ship.self,
- dataKey: dataKey(apiResponse),
+ self.configuration = MappingConfiguration(entity: Ship.self,
+ dataKeys: ShipMapper.dataKeys(apiResponse),
editorStore: ServerDataStore.oneTimeEditor(),
- ignoreKeys:
- ["api_gomes", "api_gomes2", "api_broken", "api_powup",
- "api_voicef", "api_afterlv", "api_aftershipid", "api_backs",
- "api_slotnum", "api_stype", "api_name", "api_yomi",
- "api_raig", "api_luck", "api_saku", "api_raim", "api_baku",
- "api_taik", "api_houg", "api_houm", "api_tyku",
- "api_ndock_item", "api_star",
- "api_ndock_time_str", "api_member_id",
- "api_fuel_max", "api_bull_max"])
-
- // kaisouSlotDepriveでは同時に2種類のデータが入る
- if let api = ShipAPI(rawValue: apiResponse.api),
- api == .kaisouSlotDeprive {
- let conf = self.configuration.change(dataKey: "api_data.api_ship_data.api_unset_ship")
- ShipMapper(apiResponse, configuration: conf).commit()
- }
+ ignoreKeys: ShipMapper.ignoreKeys)
}
- private init(_ apiResponse: APIResponse, configuration: MappingConfiguration) {
+
+ // slotDepriveの時に2種類のデータが来るため
+ init(forSlotDepriveUnset apiResponse: APIResponse) {
+
self.apiResponse = apiResponse
- self.configuration = configuration
+ self.configuration = MappingConfiguration(entity: Ship.self,
+ dataKeys: ["api_data", "api_ship_data", "api_unset_ship"],
+ editorStore: ServerDataStore.oneTimeEditor(),
+ ignoreKeys: ShipMapper.ignoreKeys)
+ }
+
+ private class func dataKeys(_ apiResponse: APIResponse) -> [String] {
+
+ switch apiResponse.api.endpoint {
+
+ case .port, .getShip, .powerup: return ["api_data", "api_ship"]
+
+ case .ship3, .shipDeck: return ["api_data", "api_ship_data"]
+
+ case .slotDeprive: return ["api_data", "api_ship_data", "api_set_ship"]
+
+ case .ship, .ship2: return ["api_data"]
+
+ default:
+
+ Logger.shared.log("Missing API: \(apiResponse.api)")
+
+ return ["api_data"]
+ }
}
private var registerIds: [Int] = []
private lazy var masterShips: [MasterShip] = {
- return ServerDataStore.default.sortedMasterShipsById()
+
+ guard let store = configuration.editorStore as? ServerDataStore else {
+
+ return []
+ }
+
+ return store.sortedMasterShipsById()
+
}()
private lazy var slotItems: [SlotItem] = {
- return ServerDataStore.default.sortedSlotItemsById()
+
+ guard let store = configuration.editorStore as? ServerDataStore else {
+
+ return []
+ }
+
+ return store.sortedSlotItemsById()
}()
- private var isDeleteNotExist: Bool {
- guard let shipApi = ShipAPI(rawValue: apiResponse.api)
- else { return true }
- switch shipApi {
- case .getMemberShip3, .kousyouGetShip, .getMemberShipDeck,
- .kaisouPowerUp, .kaisouSlotDeprive:
+
+ private var needsDeleteUnregisteredShip: Bool {
+
+ switch apiResponse.api.endpoint {
+
+ case .ship3, .getShip, .shipDeck, .powerup, .slotDeprive:
+
return false
+
+ case .ship2:
+ // 特殊任務のクリア時にship2がapi_shipid付きでリクエストされ、その艦娘のデータしかない時があるため
+
+ return !apiResponse.parameter["api_shipid"].valid
+
default:
+
return true
+
}
}
+
private var store: ServerDataStore? {
+
return configuration.editorStore as? ServerDataStore
}
- func beginRegister(_ object: NSManagedObject) {
- guard let ship = object as? Ship
- else { return }
+ func beginRegister(_ ship: Ship) {
+
ship.sally_area = nil
}
- func handleExtraValue(_ value: Any, forKey key: String, to object: NSManagedObject) -> Bool {
- guard let ship = object as? Ship
- else { return false }
+
+ func handleExtraValue(_ value: JSON, forKey key: String, to ship: Ship) -> Bool {
// 取得後破棄した装備のデータを削除するため保有IDを保存
if key == "api_id" {
- guard let id = value as? Int
- else { return false }
+
+ guard let id = value.int else {
+
+ return false
+ }
+
registerIds.append(id)
+
return false
}
if key == "api_ship_id" {
- guard let masterId = value as? Int
- else { return false }
+
+ guard let masterId = value.int else {
+
+ return false
+ }
+
+ if ship.ship_id == masterId {
+
+ return true
+ }
+
setMaster(masterId, to: ship)
+
return true
}
+
if key == "api_exp" {
- guard let v = value as? [Any],
- let vv = v.first as? Int
- else { return false }
- ship.exp = vv
+
+ guard let exp = value[0].int else {
+
+ return false
+ }
+
+ if ship.exp == exp {
+
+ return true
+ }
+
+ ship.exp = exp
+
return true
}
+
if key == "api_slot" {
- guard let slotItems = value as? [Any]
- else { return false }
- setSlot(slotItems, to: ship)
+
+ setSlot(value, to: ship)
+
return false
}
+
if key == "api_slot_ex" {
- guard let ex = value as? Int
- else { return false }
+
+ guard let ex = value.int else {
+
+ ship.extraItem = nil
+
+ return false
+ }
+
+ if ship.slot_ex == ex {
+
+ return true
+ }
+
setExtraSlot(ex, to: ship)
- return false
+
+ ship.slot_ex = ex
+
+ return true
}
return false
}
+
func finishOperating() {
- if !isDeleteNotExist { return }
+
+ if !needsDeleteUnregisteredShip {
+
+ return
+ }
+
store?.ships(exclude: registerIds).forEach { store?.delete($0) }
}
private func setMaster(_ masterId: Int, to ship: Ship) {
- if ship.ship_id == masterId { return }
+
guard let mShip = masterShips.binarySearch(comparator: { $0.id ==? masterId }),
- let masterShip = store?.object(with: mShip.objectID) as? MasterShip
- else { return print("Can not convert to current moc object masterShip") }
+ let masterShip = store?.exchange(mShip) else {
+
+ Logger.shared.log("Can not convert to current moc object masterShip")
+
+ return
+ }
+
ship.master_ship = masterShip
ship.ship_id = masterId
}
- private func setSlot(_ slotItems: [Any], to ship: Ship) {
- guard let converSlotItems = slotItems as? [Int],
- let store = store
- else { return }
- let newItems: [SlotItem] =
- converSlotItems.flatMap { (item: Int) in
- if item == 0 || item == -1 { return nil }
+ private func setSlot(_ slotItems: JSON, to ship: Ship) {
+
+ guard let convertedSlotItems = slotItems.arrayObject as? [Int] else {
+
+ return
+ }
+ guard let store = store else {
+
+ return
+ }
+
+ let newItems: [SlotItem] = convertedSlotItems
+ .filter { $0 != 0 && $0 != -1 }
+ .compactMap { item in
+
guard let found = self.slotItems.binarySearch(comparator: { $0.id ==? item }),
- let slotItem = store.object(with: found.objectID) as? SlotItem
- else {
- let maxV = converSlotItems.last
- if maxV != nil && maxV! < item {
- #if DEBUG
- print("item is maybe unregistered, so it is new ship's equipment.")
- #endif
+ let slotItem = store.exchange(found) else {
+
+ let maxV = convertedSlotItems.last
+ if maxV != nil, maxV! < item {
+
+ Debug.print("item is maybe unregistered, so it is new ship's equipment.")
+
return nil
}
- print("Can not convert to current moc object slotItem")
+ Logger.shared.log("Can not convert to current moc object slotItem")
+
return nil
}
+
return slotItem
}
+
ship.equippedItem = NSOrderedSet(array: newItems)
}
+
private func setExtraSlot(_ exSlotItem: Int, to ship: Ship) {
- guard exSlotItem != -1,
- exSlotItem != 0
- else { return }
+
+ guard exSlotItem != -1, exSlotItem != 0 else {
+
+ ship.extraItem = nil
+
+ return
+ }
guard let found = slotItems.binarySearch(comparator: { $0.id ==? exSlotItem }),
- let ex = store?.object(with: found.objectID) as? SlotItem
- else { return print("Can not convert to current moc object") }
+ let ex = store?.exchange(found) else {
+
+ Logger.shared.log("Can not convert to current moc object")
+
+ return
+ }
+
ship.extraItem = ex
}
}