package games.strategy.triplea.delegate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import games.strategy.engine.data.Change;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.GameMap;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.Territory;
import games.strategy.engine.data.Unit;
import games.strategy.engine.data.changefactory.ChangeFactory;
import games.strategy.engine.delegate.IDelegateBridge;
import games.strategy.triplea.attachments.UnitAttachment;
import games.strategy.triplea.formatter.MyFormatter;
import games.strategy.util.CompositeMatch;
import games.strategy.util.CompositeMatchAnd;
/**
* Utility for detecting and removing units that can't land at the end of a phase.
*/
public class AirThatCantLandUtil {
private final IDelegateBridge m_bridge;
public AirThatCantLandUtil(final IDelegateBridge bridge) {
m_bridge = bridge;
}
public static boolean isLHTRCarrierProduction(final GameData data) {
return games.strategy.triplea.Properties.getLHTRCarrierProductionRules(data);
}
public static boolean isLandExistingFightersOnNewCarriers(final GameData data) {
return games.strategy.triplea.Properties.getLandExistingFightersOnNewCarriers(data);
}
public Collection<Territory> getTerritoriesWhereAirCantLand(final PlayerID player) {
final GameData data = m_bridge.getData();
final Collection<Territory> cantLand = new ArrayList<>();
final Iterator<Territory> territories = data.getMap().getTerritories().iterator();
while (territories.hasNext()) {
final Territory current = territories.next();
final CompositeMatch<Unit> ownedAir = new CompositeMatchAnd<>();
ownedAir.add(Matches.UnitIsAir);
ownedAir.add(Matches.unitIsOwnedBy(player));
final Collection<Unit> air = current.getUnits().getMatches(ownedAir);
if (air.size() != 0 && !AirMovementValidator.canLand(air, current, player, data)) {
cantLand.add(current);
}
}
return cantLand;
}
public void removeAirThatCantLand(final PlayerID player, final boolean spareAirInSeaZonesBesideFactories) {
final GameData data = m_bridge.getData();
final GameMap map = data.getMap();
final Iterator<Territory> territories = getTerritoriesWhereAirCantLand(player).iterator();
while (territories.hasNext()) {
final Territory current = territories.next();
final CompositeMatch<Unit> ownedAir = new CompositeMatchAnd<>();
ownedAir.add(Matches.UnitIsAir);
ownedAir.add(Matches.alliedUnit(player, data));
final Collection<Unit> air = current.getUnits().getMatches(ownedAir);
final boolean hasNeighboringFriendlyFactory =
map.getNeighbors(current, Matches.territoryHasAlliedIsFactoryOrCanProduceUnits(data, player)).size() > 0;
final boolean skip = spareAirInSeaZonesBesideFactories && current.isWater() && hasNeighboringFriendlyFactory;
if (!skip) {
removeAirThatCantLand(player, current, air);
}
}
}
private void removeAirThatCantLand(final PlayerID player, final Territory territory,
final Collection<Unit> airUnits) {
final Collection<Unit> toRemove = new ArrayList<>(airUnits.size());
// if we cant land on land then none can
if (!territory.isWater()) {
toRemove.addAll(airUnits);
} else { // on water we may just no have enough carriers
// find the carrier capacity
final Collection<Unit> carriers = territory.getUnits().getMatches(Matches.alliedUnit(player, m_bridge.getData()));
int capacity = AirMovementValidator.carrierCapacity(carriers, territory);
final Iterator<Unit> iter = airUnits.iterator();
while (iter.hasNext()) {
final Unit unit = iter.next();
final UnitAttachment ua = UnitAttachment.get(unit.getType());
final int cost = ua.getCarrierCost();
if (cost == -1 || cost > capacity) {
toRemove.add(unit);
} else {
capacity -= cost;
}
}
}
final Change remove = ChangeFactory.removeUnits(territory, toRemove);
final String transcriptText = MyFormatter.unitsToTextNoOwner(toRemove) + " could not land in " + territory.getName()
+ " and " + (toRemove.size() > 1 ? "were" : "was") + " removed";
m_bridge.getHistoryWriter().startEvent(transcriptText);
m_bridge.addChange(remove);
}
}