OSDN Git Service

コーディングスタイルを変更
[kcd/KCD.git] / KCD / ChangeHenseiCommand.swift
1 //
2 //  ChangeHenseiCommand.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2017/01/09.
6 //  Copyright © 2017年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 enum ChangeHenseiType: Int {
12     
13     case append
14     
15     case replace
16     
17     case remove
18     
19     case removeAllWithoutFlagship
20 }
21
22 extension Notification.Name {
23     
24     static let HenseiDidChange = Notification.Name("com.masakih.KCD.Notification.HenseiDidChange")
25 }
26
27 final class HenseiDidChangeUserInfo: NSObject {
28     
29     let type: ChangeHenseiType
30     
31     let fleetNumber: Int
32     let position: Int
33     let shipID: Int
34     
35     let replaceFleetNumber: Int?
36     let replacePosition: Int?
37     let replaceShipID: Int?
38     
39     required init(type: ChangeHenseiType,
40                   fleetNumber: Int,
41                   position: Int,
42                   shipID: Int,
43                   replaceFleetNumber: Int? = nil,
44                   replacePosition: Int? = nil,
45                   replaceShipID: Int? = nil) {
46         
47         self.type = type
48         self.fleetNumber = fleetNumber
49         self.position = position
50         self.shipID = shipID
51         self.replaceFleetNumber = replaceFleetNumber
52         self.replacePosition = replacePosition
53         self.replaceShipID = replaceShipID
54         
55         super.init()
56     }
57 }
58
59 final class ChangeHenseiCommand: JSONCommand {
60     
61     static let userInfoKey = "HenseiDidChangeUserInfoKey"
62     
63     override class func canExecuteAPI(_ api: API) -> Bool {
64         
65         return api.endpoint == .change
66     }
67     
68     // api_ship_id の値
69     // ship_id > 0 : 艦娘のID append or replace
70     // ship_id == -1 : remove.
71     // ship_id == -2 : remove all without flag ship.
72     override func execute() {
73         
74         guard let deckNumber = parameter["api_id"].int,
75             let shipId = parameter["api_ship_id"].int,
76             let shipIndex = parameter["api_ship_idx"].int else {
77                 
78                 Logger.shared.log("parameter is wrong")
79                 
80                 return
81         }
82         
83         if shipId == -1 {
84             
85             guard let shipId = removeShip(deckNumber: deckNumber, index: shipIndex) else {
86                 
87                 return
88             }
89             notify(type: .remove, fleetNumber: deckNumber, position: shipIndex, shipID: shipId)
90             
91             return
92         }
93         
94         if shipId == -2 {
95             
96             excludeShipsWithoutFlagShip(deckNumber: deckNumber)
97             notify(type: .removeAllWithoutFlagship)
98             
99             return
100         }
101         
102         guard case 0..<Deck.maxShipCount = shipIndex else {
103             
104             return
105         }
106         
107         let store = ServerDataStore.oneTimeEditor()
108         guard let deck = store.sync(execute: { store.deck(by: deckNumber) }) else {
109             
110             return
111         }
112         
113         // すでに編成されているか? どこに?
114         let (shipDeckNumber, shipDeckIndex) = position(of: shipId)
115         
116         // 配置しようとする位置に今配置されている艦娘
117         let replaceShipId = store.sync { deck[shipIndex]?.id }
118         
119         // 艦隊に配備
120         store.sync { deck.setShip(id: shipId, for: shipIndex) }
121         
122         // 入れ替え
123         if shipDeckNumber != nil {
124             
125             let shipDeck = store.sync { store.deck(by: shipDeckNumber!) }
126             store.sync { shipDeck?.setShip(id: replaceShipId ?? -1, for: shipDeckIndex) }
127             shipDeck.map { packFleet(store: store, deck: $0) }
128         }
129         
130         packFleet(store: store, deck: deck)
131         
132         // Notify
133         if shipDeckNumber != nil {
134             
135             notify(type: .replace,
136                    fleetNumber: deckNumber,
137                    position: shipIndex,
138                    shipID: shipId,
139                    replaceFleetNumber: shipDeckNumber!,
140                    replacePosition: shipDeckIndex,
141                    replaceShipID: replaceShipId)
142             
143         } else {
144             
145             notify(type: .append, fleetNumber: deckNumber, position: shipIndex, shipID: shipId)
146         }
147     }
148     
149     private func position(of shipId: Int) -> (deckNumber: Int?, shipId: Int) {
150         
151         let store = ServerDataStore.default
152         return store.sync {
153             
154             store
155                 .decksSortedById()
156                 .lazy
157                 .enumerated()
158                 .map { (idx, deck) -> (Int, [Ship]) in (idx + 1, deck[0..<Deck.maxShipCount]) }
159                 .filter { $0.1.contains { $0.id == shipId } }
160                 .map { (deck, ships) in (deck, ships.index(where: { $0.id == shipId })!) }
161                 .first ?? (nil, -1)
162         }
163     }
164     
165     private func removeShip(deckNumber: Int, index: Int) -> Int? {
166         
167         let store = ServerDataStore.oneTimeEditor()
168         
169         guard let deck = store.sync(execute: { store.deck(by: deckNumber) }) else {
170             
171             Logger.shared.log("Deck not found")
172             
173             return nil
174         }
175         
176         let shipId = store.sync { deck[index]?.id ?? -1 }
177         store.sync { deck.setShip(id: -1, for: index) }
178         
179         packFleet(store: store, deck: deck)
180         
181         return shipId
182     }
183     
184     private func excludeShipsWithoutFlagShip(deckNumber: Int) {
185         
186         let store = ServerDataStore.oneTimeEditor()
187         store.sync {
188             guard let deck = store.deck(by: deckNumber) else {
189                 
190                 Logger.shared.log("Deck not found")
191                 
192                 return
193             }
194             
195             (1..<Deck.maxShipCount).forEach { deck.setShip(id: -1, for: $0) }
196         }
197     }
198     
199     private func packFleet(store: ServerDataStore, deck: Deck) {
200         
201         func set(_ ships: [Ship], at index: Int, in deck: Deck) {
202             
203             guard index < Deck.maxShipCount else {
204                 
205                 return
206             }
207             
208             deck.setShip(id: ships.first?.id ?? -1, for: index)
209             
210             let newShips = ships.isEmpty ? [] : Array(ships[1...])
211             
212             set(newShips, at: index + 1, in: deck)
213         }
214         
215         store.sync { set(deck[0..<Deck.maxShipCount], at: 0, in: deck) }
216     }
217     
218     private func notify(type: ChangeHenseiType,
219                         fleetNumber: Int = 0,
220                         position: Int = 0,
221                         shipID: Int = 0,
222                         replaceFleetNumber: Int? = nil,
223                         replacePosition: Int? = nil,
224                         replaceShipID: Int? = nil) {
225         
226         let userInfo = HenseiDidChangeUserInfo(type: type,
227                                                fleetNumber: fleetNumber,
228                                                position: position,
229                                                shipID: shipID,
230                                                replaceFleetNumber: replaceFleetNumber,
231                                                replacePosition: replacePosition,
232                                                replaceShipID: replaceShipID)
233         NotificationCenter.default
234             .post(name: .HenseiDidChange,
235                   object: self,
236                   userInfo: [ChangeHenseiCommand.userInfoKey: userInfo])
237     }
238 }