package games.strategy.triplea.delegate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.RelationshipType;
import games.strategy.engine.data.Resource;
import games.strategy.engine.data.Territory;
import games.strategy.engine.data.Unit;
import games.strategy.engine.data.changefactory.ChangeFactory;
import games.strategy.engine.message.IRemote;
import games.strategy.triplea.Constants;
import games.strategy.triplea.TripleAUnit;
import games.strategy.triplea.attachments.TerritoryAttachment;
import games.strategy.triplea.delegate.remote.IEditDelegate;
import games.strategy.triplea.formatter.MyFormatter;
import games.strategy.triplea.util.TransportUtils;
import games.strategy.util.CompositeMatch;
import games.strategy.util.CompositeMatchAnd;
import games.strategy.util.IntegerMap;
import games.strategy.util.Match;
import games.strategy.util.Triple;
/**
* Edit game state.
*/
public class EditDelegate extends BaseEditDelegate implements IEditDelegate {
/**
* Called before the delegate will run.
*/
@Override
public void start() {
super.start();
}
@Override
public void end() {}
@Override
public String removeUnits(final Territory territory, final Collection<Unit> units) {
String result = null;
if (null != (result = checkEditMode())) {
return result;
}
if (null != (result = EditValidator.validateRemoveUnits(getData(), territory, units))) {
return result;
}
if (units == null || units.isEmpty()) {
return null;
}
final Collection<PlayerID> owners = new HashSet<>();
for (final Unit u : units) {
owners.add(u.getOwner());
}
for (final PlayerID p : owners) {
final List<Unit> unitsOwned = Match.getMatches(units, Matches.unitIsOwnedBy(p));
logEvent("Removing units owned by " + p.getName() + " from " + territory.getName() + ": "
+ MyFormatter.unitsToTextNoOwner(unitsOwned), unitsOwned);
m_bridge.addChange(ChangeFactory.removeUnits(territory, unitsOwned));
}
return null;
}
@Override
public String addUnits(final Territory territory, final Collection<Unit> units) {
String result = null;
if (null != (result = checkEditMode())) {
return result;
}
if (null != (result = EditValidator.validateAddUnits(getData(), territory, units))) {
return result;
}
if (units == null || units.isEmpty()) {
return null;
}
// now make sure land units are put on transports properly
final PlayerID player = units.iterator().next().getOwner();
final GameData data = getData();
Map<Unit, Unit> mapLoading = null;
if (territory.isWater()) {
if (!Match.allMatch(units, Matches.UnitIsSea)) {
if (Match.someMatch(units, Matches.UnitIsLand)) {
// this should be exact same as the one in the EditValidator
if (!Match.allMatch(units, Matches.alliedUnit(player, data))) {
return "Can't add mixed nationality units to water";
}
final Match<Unit> friendlySeaTransports =
new CompositeMatchAnd<>(Matches.UnitIsTransport, Matches.UnitIsSea, Matches.alliedUnit(player, data));
final Collection<Unit> seaTransports = Match.getMatches(units, friendlySeaTransports);
final Collection<Unit> landUnitsToAdd = Match.getMatches(units, Matches.UnitIsLand);
if (!Match.allMatch(landUnitsToAdd, Matches.UnitCanBeTransported)) {
return "Can't add land units that can't be transported, to water";
}
seaTransports.addAll(territory.getUnits().getMatches(friendlySeaTransports));
if (seaTransports.isEmpty()) {
return "Can't add land units to water without enough transports";
}
mapLoading = TransportUtils.mapTransportsToLoad(landUnitsToAdd, seaTransports);
if (!mapLoading.keySet().containsAll(landUnitsToAdd)) {
return "Can't add land units to water without enough transports";
}
}
}
}
// now perform the changes
logEvent("Adding units owned by " + units.iterator().next().getOwner().getName() + " to " + territory.getName()
+ ": " + MyFormatter.unitsToTextNoOwner(units), units);
m_bridge.addChange(ChangeFactory.addUnits(territory, units));
if (mapLoading != null && !mapLoading.isEmpty()) {
for (final Entry<Unit, Unit> entry : mapLoading.entrySet()) {
m_bridge.addChange(TransportTracker.loadTransportChange((TripleAUnit) entry.getValue(), entry.getKey()));
}
}
return null;
}
/**
* @return gets the production of the territory, ignores whether the territory was an original factory.
*/
protected int getProduction(final Territory territory) {
final TerritoryAttachment ta = TerritoryAttachment.get(territory);
if (ta != null) {
return ta.getProduction();
}
return 0;
}
@Override
public String changeTerritoryOwner(final Territory territory, final PlayerID player) {
String result = null;
if (null != (result = checkEditMode())) {
return result;
}
final GameData data = getData();
// validate this edit
if (null != (result = EditValidator.validateChangeTerritoryOwner(data, territory, player))) {
return result;
}
logEvent("Changing ownership of " + territory.getName() + " from " + territory.getOwner().getName() + " to "
+ player.getName(), territory);
if (!data.getRelationshipTracker().isAtWar(territory.getOwner(), player)) {
// change ownership of friendly factories
final Collection<Unit> units = territory.getUnits().getMatches(Matches.UnitIsInfrastructure);
for (final Unit unit : units) {
m_bridge.addChange(ChangeFactory.changeOwner(unit, player, territory));
}
} else {
final CompositeMatch<Unit> enemyNonCom = new CompositeMatchAnd<>();
enemyNonCom.add(Matches.UnitIsInfrastructure);
enemyNonCom.add(Matches.enemyUnit(player, data));
final Collection<Unit> units = territory.getUnits().getMatches(enemyNonCom);
// mark no movement for enemy units
m_bridge.addChange(ChangeFactory.markNoMovementChange(units));
// change ownership of enemy AA and factories
for (final Unit unit : units) {
m_bridge.addChange(ChangeFactory.changeOwner(unit, player, territory));
}
}
// change ownership of territory
m_bridge.addChange(ChangeFactory.changeOwner(territory, player));
return null;
}
@Override
public String changePUs(final PlayerID player, final int newTotal) {
String result = null;
if (null != (result = checkEditMode())) {
return result;
}
final Resource PUs = getData().getResourceList().getResource(Constants.PUS);
final int oldTotal = player.getResources().getQuantity(PUs);
if (oldTotal == newTotal) {
return "New PUs total is unchanged";
}
if (newTotal < 0) {
return "New PUs total is invalid";
}
logEvent("Changing PUs for " + player.getName() + " from " + oldTotal + " to " + newTotal, null);
m_bridge.addChange(ChangeFactory.changeResourcesChange(player, PUs, (newTotal - oldTotal)));
return null;
}
@Override
public String changeTechTokens(final PlayerID player, final int newTotal) {
String result = null;
if (null != (result = checkEditMode())) {
return result;
}
final Resource techTokens = getData().getResourceList().getResource(Constants.TECH_TOKENS);
final int oldTotal = player.getResources().getQuantity(techTokens);
if (oldTotal == newTotal) {
return "New token total is unchanged";
}
if (newTotal < 0) {
return "New token total is invalid";
}
logEvent("Changing tech tokens for " + player.getName() + " from " + oldTotal + " to " + newTotal, null);
m_bridge.addChange(ChangeFactory.changeResourcesChange(player, techTokens, (newTotal - oldTotal)));
return null;
}
@Override
public String addTechAdvance(final PlayerID player, final Collection<TechAdvance> advances) {
String result = null;
if (null != (result = checkEditMode())) {
return result;
}
if (null != (result = EditValidator.validateAddTech(getData(), advances, player))) {
return result;
}
for (final TechAdvance advance : advances) {
logEvent("Adding Technology " + advance.getName() + " for " + player.getName(), null);
TechTracker.addAdvance(player, m_bridge, advance);
}
return null;
}
@Override
public String removeTechAdvance(final PlayerID player, final Collection<TechAdvance> advances) {
String result = null;
if (null != (result = checkEditMode())) {
return result;
}
if (null != (result = EditValidator.validateRemoveTech(getData(), advances, player))) {
return result;
}
for (final TechAdvance advance : advances) {
logEvent("Removing Technology " + advance.getName() + " for " + player.getName(), null);
TechTracker.removeAdvance(player, m_bridge, advance);
}
return null;
}
@Override
public String changeUnitHitDamage(final IntegerMap<Unit> unitDamageMap, final Territory territory) {
String result = null;
if (null != (result = checkEditMode())) {
return result;
}
if (null != (result = EditValidator.validateChangeHitDamage(getData(), unitDamageMap, territory))) {
return result;
}
// remove anyone who is the same
final Collection<Unit> units = new ArrayList<>(unitDamageMap.keySet());
for (final Unit u : units) {
final int dmg = unitDamageMap.getInt(u);
if (u.getHits() == dmg) {
unitDamageMap.removeKey(u);
}
}
if (unitDamageMap.isEmpty()) {
return null;
}
final Collection<Unit> unitsFinal = new ArrayList<>(unitDamageMap.keySet());
logEvent("Changing unit hit damage for these " + unitsFinal.iterator().next().getOwner().getName()
+ " owned units to: " + MyFormatter.integerUnitMapToString(unitDamageMap, ", ", " = ", false), unitsFinal);
m_bridge.addChange(ChangeFactory.unitsHit(unitDamageMap));
// territory.notifyChanged();
return null;
}
@Override
public String changeUnitBombingDamage(final IntegerMap<Unit> unitDamageMap, final Territory territory) {
String result = null;
if (null != (result = checkEditMode())) {
return result;
}
if (null != (result = EditValidator.validateChangeBombingDamage(getData(), unitDamageMap, territory))) {
return result;
}
// remove anyone who is the same
final Collection<Unit> units = new ArrayList<>(unitDamageMap.keySet());
for (final Unit u : units) {
final int dmg = unitDamageMap.getInt(u);
final int currentDamage = ((TripleAUnit) u).getUnitDamage();
if (currentDamage == dmg) {
unitDamageMap.removeKey(u);
}
}
if (unitDamageMap.isEmpty()) {
return null;
}
// we do damage to the unit
final Collection<Unit> unitsFinal = new ArrayList<>(unitDamageMap.keySet());
logEvent("Changing unit bombing damage for these " + unitsFinal.iterator().next().getOwner().getName()
+ " owned units to: " + MyFormatter.integerUnitMapToString(unitDamageMap, ", ", " = ", false), unitsFinal);
m_bridge.addChange(ChangeFactory.bombingUnitDamage(unitDamageMap));
// territory.notifyChanged();
return null;
}
@Override
public String changePoliticalRelationships(
final Collection<Triple<PlayerID, PlayerID, RelationshipType>> relationshipChanges) {
String result = null;
if (relationshipChanges == null || relationshipChanges.isEmpty()) {
return result;
}
if (null != (result = checkEditMode())) {
return result;
}
if (null != (result = EditValidator.validateChangePoliticalRelationships(getData(), relationshipChanges))) {
return result;
}
final BattleTracker battleTracker = AbstractMoveDelegate.getBattleTracker(getData());
for (final Triple<PlayerID, PlayerID, RelationshipType> relationshipChange : relationshipChanges) {
final RelationshipType currentRelation = getData().getRelationshipTracker()
.getRelationshipType(relationshipChange.getFirst(), relationshipChange.getSecond());
if (!currentRelation.equals(relationshipChange.getThird())) {
logEvent("Editing Political Relationship for " + relationshipChange.getFirst().getName() + " and "
+ relationshipChange.getSecond().getName() + " from " + currentRelation.getName() + " to "
+ relationshipChange.getThird().getName(), null);
m_bridge.addChange(ChangeFactory.relationshipChange(relationshipChange.getFirst(),
relationshipChange.getSecond(), currentRelation, relationshipChange.getThird()));
battleTracker.addRelationshipChangesThisTurn(relationshipChange.getFirst(), relationshipChange.getSecond(),
currentRelation, relationshipChange.getThird());
}
}
return null;
}
@Override
public Class<? extends IRemote> getRemoteType() {
return IEditDelegate.class;
}
}