package games.strategy.triplea.ui.screen.drawable;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.List;
import java.util.Optional;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.Territory;
import games.strategy.triplea.attachments.TerritoryAttachment;
import games.strategy.triplea.formatter.MyFormatter;
import games.strategy.triplea.image.MapImage;
import games.strategy.triplea.ui.IUIContext;
import games.strategy.triplea.ui.mapdata.MapData;
public class TerritoryNameDrawable implements IDrawable {
private final String m_territoryName;
private final IUIContext m_uiContext;
public TerritoryNameDrawable(final String territoryName, final IUIContext context) {
this.m_territoryName = territoryName;
this.m_uiContext = context;
}
@Override
public void draw(final Rectangle bounds, final GameData data, final Graphics2D graphics, final MapData mapData,
final AffineTransform unscaled, final AffineTransform scaled) {
final Territory territory = data.getMap().getTerritory(m_territoryName);
final TerritoryAttachment ta = TerritoryAttachment.get(territory);
final boolean drawFromTopLeft = mapData.drawNamesFromTopLeft();
final boolean showSeaNames = mapData.drawSeaZoneNames();
final boolean showComments = mapData.drawComments();
boolean drawComments = false;
String commentText = null;
if (territory.isWater()) {
// this is for special comments, like convoy zones, etc.
if (ta != null && showComments) {
if (ta.getConvoyRoute() && ta.getProduction() > 0 && ta.getOriginalOwner() != null) {
drawComments = true;
if (ta.getConvoyAttached().isEmpty()) {
commentText = MyFormatter
.defaultNamedToTextList(TerritoryAttachment.getWhatTerritoriesThisIsUsedInConvoysFor(territory, data))
+ " " + ta.getOriginalOwner().getName() + " Blockade Route";
} else {
commentText = MyFormatter.defaultNamedToTextList(ta.getConvoyAttached()) + " "
+ ta.getOriginalOwner().getName() + " Convoy Route";
}
} else if (ta.getConvoyRoute()) {
drawComments = true;
if (ta.getConvoyAttached().isEmpty()) {
commentText = MyFormatter.defaultNamedToTextList(
TerritoryAttachment.getWhatTerritoriesThisIsUsedInConvoysFor(territory, data)) + " Blockade Route";
} else {
commentText = MyFormatter.defaultNamedToTextList(ta.getConvoyAttached()) + " Convoy Route";
}
} else if (ta.getProduction() > 0 && ta.getOriginalOwner() != null) {
drawComments = true;
final PlayerID originalOwner = ta.getOriginalOwner();
commentText = originalOwner.getName() + " Convoy Center";
}
}
if (!drawComments && !showSeaNames) {
return;
}
}
graphics.setFont(MapImage.getPropertyMapFont());
graphics.setColor(MapImage.getPropertyTerritoryNameAndPUAndCommentcolor());
final FontMetrics fm = graphics.getFontMetrics();
// if we specify a placement point, use it otherwise try to center it
int x;
int y;
final Optional<Point> namePlace = mapData.getNamePlacementPoint(territory);
if (namePlace.isPresent()) {
x = namePlace.get().x;
y = namePlace.get().y;
} else {
final Rectangle territoryBounds = getBestTerritoryNameRect(mapData, territory, fm);
x = territoryBounds.x + (int) territoryBounds.getWidth() / 2 - fm.stringWidth(territory.getName()) / 2;
y = territoryBounds.y + (int) territoryBounds.getHeight() / 2 + fm.getAscent() / 2;
}
// draw comments above names
if (showComments && drawComments && commentText != null) {
final Optional<Point> place = mapData.getCommentMarkerLocation(territory);
if (place.isPresent()) {
draw(bounds, graphics, place.get().x, place.get().y, null, commentText, drawFromTopLeft);
} else {
draw(bounds, graphics, x, y - fm.getHeight(), null, commentText, drawFromTopLeft);
}
}
// draw territory names
if (mapData.drawTerritoryNames() && mapData.shouldDrawTerritoryName(m_territoryName)) {
if (!territory.isWater() || showSeaNames) {
final Image nameImage = mapData.getTerritoryNameImages().get(territory.getName());
draw(bounds, graphics, x, y, nameImage, territory.getName(), drawFromTopLeft);
}
}
// draw the PUs.
if (ta != null && ta.getProduction() > 0 && mapData.drawResources()) {
final Image img = m_uiContext.getPUImageFactory().getPUImage(ta.getProduction());
final String prod = Integer.valueOf(ta.getProduction()).toString();
final Optional<Point> place = mapData.getPUPlacementPoint(territory);
// if pu_place.txt is specified draw there
if (place.isPresent()) {
draw(bounds, graphics, place.get().x, place.get().y, img, prod, drawFromTopLeft);
} else {
// otherwise, draw under the territory name
draw(bounds, graphics, x + ((fm.stringWidth(m_territoryName)) >> 1) - ((fm.stringWidth(prod)) >> 1),
y + fm.getLeading() + fm.getAscent(), img, prod, drawFromTopLeft);
}
}
}
/**
* Find the best rectangle inside the territory to place the name in. Finds the rectangle
* that can fit the name, that is the closest to the vertical center, and has a large width at
* that location. If there isn't any rectangles that can fit the name then default back to the
* bounding rectangle.
*/
private Rectangle getBestTerritoryNameRect(final MapData mapData, final Territory territory,
final FontMetrics fontMetrics) {
// Find bounding rectangle and parameters for creating a grid (20 x 20) across the territory
final Rectangle territoryBounds = mapData.getBoundingRect(territory);
Rectangle result = territoryBounds;
final int maxX = territoryBounds.x + territoryBounds.width;
final int maxY = territoryBounds.y + territoryBounds.height;
final int centerY = territoryBounds.y + territoryBounds.height / 2;
final int incrementX = (int) Math.ceil(territoryBounds.width / 20.0);
final int incrementY = (int) Math.ceil(territoryBounds.height / 20.0);
final int nameWidth = fontMetrics.stringWidth(territory.getName());
final int nameHeight = fontMetrics.getAscent();
int maxScore = 0;
// Loop through the grid moving the starting point and determining max width at that point
for (int x = territoryBounds.x; x < maxX - nameWidth; x += incrementX) {
for (int y = territoryBounds.y; y < maxY - nameHeight; y += incrementY) {
for (int endX = maxX; endX > x; endX -= incrementX) {
final Rectangle rectangle = new Rectangle(x, y, endX - x, nameHeight);
// Ranges from 0 when at very top or bottom of territory to height/2 when at vertical center
final int verticalDistanceFromEdge = territoryBounds.height / 2 - Math.abs(centerY - nameHeight - y);
// Score rectangle based on how close to vertical center and territory width at location
final int score = verticalDistanceFromEdge * rectangle.width;
if (rectangle.width > nameWidth && score > maxScore) {
// Check to make sure rectangle is contained in the territory
if (isRectangleContainedInTerritory(rectangle, territory, mapData)) {
maxScore = score;
result = rectangle;
break;
}
}
}
}
}
return result;
}
private boolean isRectangleContainedInTerritory(final Rectangle rectangle, final Territory territory,
final MapData mapData) {
final List<Polygon> polygons = mapData.getPolygons(territory.getName());
for (final Polygon polygon : polygons) {
if (polygon.contains(rectangle)) {
return true;
}
}
return false;
}
private void draw(final Rectangle bounds, final Graphics2D graphics, final int x, final int y, final Image img,
final String prod, final boolean drawFromTopLeft) {
int yNormal = y;
if (img == null) {
if (graphics.getFont().getSize() <= 0) {
return;
}
if (drawFromTopLeft) {
final FontMetrics fm = graphics.getFontMetrics();
yNormal += fm.getHeight();
}
graphics.drawString(prod, x - bounds.x, yNormal - bounds.y);
} else {
// we want to be consistent
// drawString takes y as the base line position
// drawImage takes x as the top right corner
if (!drawFromTopLeft) {
yNormal -= img.getHeight(null);
}
graphics.drawImage(img, x - bounds.x, yNormal - bounds.y, null);
}
}
@Override
public int getLevel() {
return TERRITORY_TEXT_LEVEL;
}
}