/**
*
*/
package logbook.internal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import logbook.dto.DockDto;
import logbook.dto.ShipDto;
/**
* 泊地修理タイマー
* @author Nekopanda
*/
public class AkashiTimer {
public static final long MINIMUM_TIME = 20 * 60 * 1000;
public static final long REPAIR_BASE = 30 * 1000;
public static final long AKASHI_DELAY = 30 * 1000;
private Date startTime = null;
/** shipId -> HP */
private final Map<Integer, ShipState> stateMap = new TreeMap<>();
public static class RepairState {
ShipState[] ships;
long elapsed;
boolean firstNotify;
RepairState(ShipState[] ships, long elapsed, boolean firstNotify) {
this.ships = ships;
this.elapsed = elapsed;
this.firstNotify = firstNotify;
}
/**
* 泊地修理中か?
* @return
*/
public boolean isRepairing() {
return this.ships != null;
}
public List<ShipState> get() {
if (this.ships == null) {
throw new IllegalStateException("泊地修理中ではありません");
}
return Arrays.asList(this.ships);
}
/**
* 今回の更新で最初の20分が経過したか?
* @return
*/
public boolean isFirstNotify() {
return this.firstNotify;
}
/**
* @return elapsed
*/
public long getElapsed() {
return this.elapsed;
}
}
public static class ShipState {
ShipDto ship;
int currentGain;
long next;
Date finish;
long elapsed;
boolean stepNotify;
/**
* 母港更新すると回復するHP
* @return
*/
public int getCurrentGain() {
return this.currentGain;
}
/**
* 次の回復ポイントまでの時間
* @return
*/
public long getNext() {
return this.next;
}
/**
* 全回復する時間
* @return
*/
public Date getFinish() {
return this.finish;
}
/**
* 今回の更新で回復したか
* @return
*/
public boolean isStepNotify() {
return this.stepNotify;
}
/**
* @return ship
*/
public ShipDto getShip() {
return this.ship;
}
/**
* @param ship セットする ship
*/
public void setShip(ShipDto ship) {
this.ship = ship;
}
}
public void reset() {
this.startTime = new Date();
this.stateMap.clear();
}
public Date getStartTime() {
return this.startTime;
}
/**
* @param startTime セットする startTime
*/
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
/** 状態を更新 */
public RepairState update(DockDto dock, Date now) {
int akashiCapacity = dock.getAkashiCapacity();
if ((this.startTime == null) || (akashiCapacity == 0)) {
return new RepairState(null, 0, false); // ショートカット
}
List<ShipDto> ships = dock.getShips();
ShipState[] states = new ShipState[ships.size()];
boolean repairing = false;
boolean firstNotify = false;
long start = this.startTime.getTime();
long elapsed = now.getTime() - start;
for (int p = 0; p < ships.size(); ++p) {
ShipDto ship = ships.get(p);
if ((p < akashiCapacity) && !ship.isHalfDamage() && (ship.getNowhp() != ship.getMaxhp())) {
repairing = true;
ShipState state = this.stateMap.get(ship.getShipId());
if (state == null) {
state = new ShipState();
}
int nowhp = ship.getNowhp();
int maxhp = ship.getMaxhp();
long docktime = ship.getDocktime();
// 修理時間は30秒足されている.素の修理時間からHP1あたりを算出
long time1pt = (docktime - REPAIR_BASE) / (maxhp - nowhp);
// 修理時間で30秒+泊地修理遅延で30秒
long rawAkashiTime = (time1pt * (maxhp - nowhp)) + REPAIR_BASE + AKASHI_DELAY;
// 本当に必要な時間
long requiredTime = Math.max(MINIMUM_TIME, rawAkashiTime);
int gain; // 現在まで回復したポイント
long next; // 次の回復までの時間
if (elapsed < MINIMUM_TIME) {
gain = 0;
next = MINIMUM_TIME - elapsed;
}
else {
// 実質的な修理経過時間
long validTime = elapsed - (REPAIR_BASE + AKASHI_DELAY);
gain = (int) (validTime / time1pt);
if (gain == 0) { // 20分は経過しているので最低1は回復している
gain = 1;
}
next = (time1pt * (gain + 1)) - validTime;
if (state.elapsed < MINIMUM_TIME) {
firstNotify = true;
}
}
int damage = ship.getMaxhp() - ship.getNowhp();
if (gain > damage) {
// MAX以上は回復しない
gain = damage;
}
boolean stepNotify = (state.currentGain != gain);
state.setShip(ship);
state.currentGain = gain;
state.next = next;
state.finish = new Date(start + requiredTime);
state.elapsed = elapsed;
state.stepNotify = stepNotify;
states[p] = state;
this.stateMap.put(ship.getShipId(), state);
}
}
return new RepairState(repairing ? states : null, elapsed, firstNotify);
}
}