package games.strategy.engine.data;
import java.awt.Image;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import games.strategy.debug.ClientLogger;
import games.strategy.triplea.TripleAUnit;
import games.strategy.triplea.attachments.UnitAttachment;
import games.strategy.triplea.image.UnitImageFactory;
import games.strategy.triplea.ui.IUIContext;
import games.strategy.triplea.ui.TooltipProperties;
import games.strategy.util.LocalizeHTML;
/**
* A prototype for units.
*/
public class UnitType extends NamedAttachable {
private static final long serialVersionUID = 4885339076798905247L;
public UnitType(final String name, final GameData data) {
super(name, data);
}
public List<Unit> create(final int quantity, final PlayerID owner) {
return create(quantity, owner, false);
}
public List<Unit> create(final int quantity, final PlayerID owner, final boolean isTemp) {
return create(quantity, owner, isTemp, 0, 0);
}
public List<Unit> create(final int quantity, final PlayerID owner, final boolean isTemp, final int hitsTaken,
final int bombingUnitDamage) {
final List<Unit> collection = new ArrayList<>();
for (int i = 0; i < quantity; i++) {
collection.add(create(owner, isTemp, hitsTaken, bombingUnitDamage));
}
return collection;
}
private Unit create(final PlayerID owner, final boolean isTemp, final int hitsTaken, final int bombingUnitDamage) {
final Unit u = getData().getGameLoader().getUnitFactory().createUnit(this, owner, getData());
u.setHits(hitsTaken);
if (u instanceof TripleAUnit) {
((TripleAUnit) u).setUnitDamage(bombingUnitDamage);
}
if (!isTemp) {
getData().getUnits().put(u);
}
return u;
}
public Unit create(final PlayerID owner) {
return create(owner, false, 0, 0);
}
@Override
public boolean equals(final Object o) {
if (o == null) {
return false;
}
if (!(o instanceof UnitType)) {
return false;
}
return ((UnitType) o).getName().equals(this.getName());
}
@Override
public int hashCode() {
return getName().hashCode();
}
public String getTooltip(final PlayerID playerId) {
final String customTip = TooltipProperties.getInstance().getToolTip(this, playerId);
if (customTip == null || customTip.trim().length() <= 0) {
return UnitAttachment.get(this).toStringShortAndOnlyImportantDifferences(
(playerId == null ? PlayerID.NULL_PLAYERID : playerId), true, false);
} else {
return LocalizeHTML.localizeImgLinksInHTML(customTip.trim());
}
}
/**
* Will return a key of NULL for any units which we do not have art for.
*/
public static Map<PlayerID, List<UnitType>> getAllPlayerUnitsWithImages(final GameData data,
final IUIContext uiContext, final boolean forceIncludeNeutralPlayer) {
final LinkedHashMap<PlayerID, List<UnitType>> rVal = new LinkedHashMap<>();
data.acquireReadLock();
try {
for (final PlayerID p : data.getPlayerList().getPlayers()) {
rVal.put(p, getPlayerUnitsWithImages(p, data, uiContext));
}
final HashSet<UnitType> unitsSoFar = new HashSet<>();
for (final List<UnitType> l : rVal.values()) {
unitsSoFar.addAll(l);
}
final Set<UnitType> all = data.getUnitTypeList().getAllUnitTypes();
all.removeAll(unitsSoFar);
if (forceIncludeNeutralPlayer || !all.isEmpty()) {
rVal.put(PlayerID.NULL_PLAYERID, getPlayerUnitsWithImages(PlayerID.NULL_PLAYERID, data, uiContext));
unitsSoFar.addAll(rVal.get(PlayerID.NULL_PLAYERID));
all.removeAll(unitsSoFar);
if (!all.isEmpty()) {
rVal.put(null, new ArrayList<>(all));
}
}
} finally {
data.releaseReadLock();
}
return rVal;
}
private static List<UnitType> getPlayerUnitsWithImages(final PlayerID player, final GameData data,
final IUIContext uiContext) {
final ArrayList<UnitType> rVal = new ArrayList<>();
data.acquireReadLock();
try {
// add first based on current production ability
if (player.getProductionFrontier() != null) {
for (final ProductionRule productionRule : player.getProductionFrontier()) {
for (final Entry<NamedAttachable, Integer> entry : productionRule.getResults().entrySet()) {
if (UnitType.class.isAssignableFrom(entry.getKey().getClass())) {
final UnitType ut = (UnitType) entry.getKey();
if (!rVal.contains(ut)) {
rVal.add(ut);
}
}
}
}
}
// this next part is purely to allow people to "add" neutral (null player) units to territories.
// This is because the null player does not have a production frontier, and we also do not know what units we have
// art for, so only
// use the units on a map.
for (final Territory t : data.getMap()) {
for (final Unit u : t.getUnits()) {
if (u.getOwner().equals(player)) {
final UnitType ut = u.getType();
if (!rVal.contains(ut)) {
rVal.add(ut);
}
}
}
}
// now check if we have the art for anything that is left
for (final UnitType ut : data.getUnitTypeList().getAllUnitTypes()) {
if (!rVal.contains(ut)) {
try {
final UnitImageFactory imageFactory = uiContext.getUnitImageFactory();
if (imageFactory != null) {
final Optional<Image> unitImage = imageFactory.getImage(ut, player, data, false, false);
if (unitImage.isPresent()) {
if (!rVal.contains(ut)) {
rVal.add(ut);
}
}
}
} catch (final Exception e) {
// TODO: does this cause excessive logging noise, or is the message useful?
ClientLogger.logQuietly("Quietly ignoring an exception while drawing unit type: " + ut + ", ", e);
}
}
}
} finally {
data.releaseReadLock();
}
return rVal;
}
}