package games.strategy.triplea.delegate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import games.strategy.engine.data.Change;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.Route;
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.delegate.dataObjects.BattleRecord;
import games.strategy.triplea.formatter.MyFormatter;
import games.strategy.triplea.oddsCalculator.ta.BattleResults;
import games.strategy.util.CompositeMatch;
import games.strategy.util.CompositeMatchAnd;
import games.strategy.util.Match;
import games.strategy.util.Util;
/**
* Battle in which no fighting occurs.
* Example is a naval invasion into an empty country,
* but the battle cannot be fought until a naval battle
* occurs.
*/
public class NonFightingBattle extends DependentBattle {
private static final long serialVersionUID = -1699534010648145123L;
public NonFightingBattle(final Territory battleSite, final PlayerID attacker, final BattleTracker battleTracker,
final GameData data) {
super(battleSite, attacker, battleTracker, false, BattleType.NORMAL, data);
}
@Override
public Change addAttackChange(final Route route, final Collection<Unit> units,
final HashMap<Unit, HashSet<Unit>> targets) {
final Map<Unit, Collection<Unit>> addedTransporting = TransportTracker.transporting(units);
for (final Unit unit : addedTransporting.keySet()) {
if (m_dependentUnits.get(unit) != null) {
m_dependentUnits.get(unit).addAll(addedTransporting.get(unit));
} else {
m_dependentUnits.put(unit, addedTransporting.get(unit));
}
}
final Territory attackingFrom = route.getTerritoryBeforeEnd();
m_attackingFrom.add(attackingFrom);
m_attackingUnits.addAll(units);
m_attackingFromMap.putIfAbsent(attackingFrom, new ArrayList<>());
final Collection<Unit> attackingFromMapUnits = m_attackingFromMap.get(attackingFrom);
attackingFromMapUnits.addAll(units);
// are we amphibious
if (route.getStart().isWater() && route.getEnd() != null && !route.getEnd().isWater()
&& Match.someMatch(units, Matches.UnitIsLand)) {
m_amphibiousAttackFrom.add(route.getTerritoryBeforeEnd());
m_amphibiousLandAttackers.addAll(Match.getMatches(units, Matches.UnitIsLand));
m_isAmphibious = true;
}
return ChangeFactory.EMPTY_CHANGE;
}
@Override
public void fight(final IDelegateBridge bridge) {
if (!m_battleTracker.getDependentOn(this).isEmpty()) {
throw new IllegalStateException("Must fight battles that this battle depends on first");
}
// create event
bridge.getHistoryWriter().startEvent("Battle in " + m_battleSite, m_battleSite);
// if any attacking non air units then win
final boolean someAttacking = hasAttackingUnits();
if (someAttacking) {
m_whoWon = WhoWon.ATTACKER;
m_battleResultDescription = BattleRecord.BattleResultDescription.BLITZED;
m_battleTracker.takeOver(m_battleSite, m_attacker, bridge, null, null);
m_battleTracker.addToConquered(m_battleSite);
} else {
m_whoWon = WhoWon.DEFENDER;
m_battleResultDescription = BattleRecord.BattleResultDescription.LOST;
}
m_battleTracker.getBattleRecords().addResultToBattle(m_attacker, m_battleID, m_defender, m_attackerLostTUV,
m_defenderLostTUV, m_battleResultDescription, new BattleResults(this, m_data));
end();
}
private void end() {
m_battleTracker.removeBattle(this);
m_isOver = true;
}
boolean hasAttackingUnits() {
final CompositeMatch<Unit> attackingLand = new CompositeMatchAnd<>();
attackingLand.add(Matches.alliedUnit(m_attacker, m_data));
attackingLand.add(Matches.UnitIsLand);
final boolean someAttacking = m_battleSite.getUnits().someMatch(attackingLand);
return someAttacking;
}
@Override
public void removeAttack(final Route route, final Collection<Unit> units) {
m_attackingUnits.removeAll(units);
// the route could be null, in the case of a unit in a territory where a sub is submerged.
if (route == null) {
return;
}
final Territory attackingFrom = route.getTerritoryBeforeEnd();
Collection<Unit> attackingFromMapUnits = m_attackingFromMap.get(attackingFrom);
// handle possible null pointer
if (attackingFromMapUnits == null) {
attackingFromMapUnits = new ArrayList<>();
}
attackingFromMapUnits.removeAll(units);
if (attackingFromMapUnits.isEmpty()) {
m_attackingFrom.remove(attackingFrom);
}
// deal with amphibious assaults
if (attackingFrom.isWater()) {
if (route.getEnd() != null && !route.getEnd().isWater() && Match.someMatch(units, Matches.UnitIsLand)) {
m_amphibiousLandAttackers.removeAll(Match.getMatches(units, Matches.UnitIsLand));
}
// if none of the units is a land unit, the attack from
// that territory is no longer an amphibious assault
if (Match.noneMatch(attackingFromMapUnits, Matches.UnitIsLand)) {
m_amphibiousAttackFrom.remove(attackingFrom);
// do we have any amphibious attacks left?
m_isAmphibious = !m_amphibiousAttackFrom.isEmpty();
}
}
final Iterator<Unit> dependents = m_dependentUnits.keySet().iterator();
while (dependents.hasNext()) {
final Unit dependence = dependents.next();
final Collection<Unit> dependent = m_dependentUnits.get(dependence);
dependent.removeAll(units);
}
}
@Override
public boolean isEmpty() {
return !hasAttackingUnits();
}
@Override
public void unitsLostInPrecedingBattle(final IBattle battle, final Collection<Unit> units,
final IDelegateBridge bridge, final boolean withdrawn) {
if (withdrawn) {
return;
}
Collection<Unit> lost = getDependentUnits(units);
lost.addAll(Util.intersection(units, m_attackingUnits));
lost = Match.getMatches(lost, Matches.unitIsInTerritory(m_battleSite));
if (lost.size() != 0) {
final String transcriptText = MyFormatter.unitsToText(lost) + " lost in " + m_battleSite.getName();
bridge.getHistoryWriter().addChildToEvent(transcriptText, lost);
final Change change = ChangeFactory.removeUnits(m_battleSite, lost);
bridge.addChange(change);
}
}
public void addDependentUnits(final Map<Unit, Collection<Unit>> dependencies) {
for (final Unit holder : dependencies.keySet()) {
final Collection<Unit> transporting = dependencies.get(holder);
if (m_dependentUnits.get(holder) != null) {
m_dependentUnits.get(holder).addAll(transporting);
} else {
m_dependentUnits.put(holder, new LinkedHashSet<>(transporting));
}
}
}
}