// // ShipMapper.swift // KCD // // Created by Hori,Masaki on 2017/02/23. // Copyright © 2017年 Hori,Masaki. All rights reserved. // 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 dataKeys(_ 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(dataKeys: [String]) -> MappingConfiguration { return MappingConfiguration(entity: self.entity, dataKeys: dataKeys, primaryKeys: self.primaryKeys, editorStore: self.editorStore, ignoreKeys: self.ignoreKeys) } } class ShipMapper: JSONMapper { typealias ObjectType = Ship let apiResponse: APIResponse let configuration: MappingConfiguration required init(_ apiResponse: APIResponse) { self.apiResponse = apiResponse self.configuration = MappingConfiguration(entity: Ship.entity, dataKeys: 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(dataKeys: ["api_data", "api_ship_data", "api_unset_ship"]) ShipMapper(apiResponse, configuration: conf).commit() } } private init(_ apiResponse: APIResponse, configuration: MappingConfiguration) { self.apiResponse = apiResponse self.configuration = configuration } private var registerIds: [Int] = [] private lazy var masterShips: [MasterShip] = { return ServerDataStore.default.sortedMasterShipsById() }() private lazy var slotItems: [SlotItem] = { return ServerDataStore.default.sortedSlotItemsById() }() private var isDeleteNotExist: Bool { guard let shipApi = ShipAPI(rawValue: apiResponse.api) else { return true } switch shipApi { case .getMemberShip3, .kousyouGetShip, .getMemberShipDeck, .kaisouPowerUp, .kaisouSlotDeprive: return false default: return true } } private var store: ServerDataStore? { return configuration.editorStore as? ServerDataStore } func beginRegister(_ ship: Ship) { ship.sally_area = nil } func handleExtraValue(_ value: JSON, forKey key: String, to ship: Ship) -> Bool { // 取得後破棄した装備のデータを削除するため保有IDを保存 if key == "api_id" { guard let id = value.int else { return false } registerIds.append(id) return false } if key == "api_ship_id" { guard let masterId = value.int else { return false } setMaster(masterId, to: ship) return true } if key == "api_exp" { guard let exp = value[0].int else { return false } ship.exp = exp return true } if key == "api_slot" { setSlot(value, to: ship) return false } if key == "api_slot_ex" { guard let ex = value.int else { return false } setExtraSlot(ex, to: ship) return false } return false } func finishOperating() { if !isDeleteNotExist { 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") } ship.master_ship = masterShip ship.ship_id = masterId } private func setSlot(_ slotItems: JSON, to ship: Ship) { guard let converSlotItems = slotItems.arrayObject as? [Int], let store = store else { return } let newItems: [SlotItem] = converSlotItems.flatMap { item in if item == 0 || item == -1 { return nil } 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 return nil } print("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 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") } ship.extraItem = ex } }