package tools.map.xml.creator;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import games.strategy.ui.SwingAction;
import games.strategy.ui.Util;
import games.strategy.util.AlphanumComparator;
import tools.map.making.ConnectionFinder;
public class TerritoryConnectionsPanel extends ImageScrollPanePanel {
private Optional<String> selectedTerritory = Optional.empty();
private TerritoryConnectionsPanel() {}
public static void layout(final MapXmlCreator mapXmlCreator) {
setMapXmlCreator(mapXmlCreator);
final TerritoryConnectionsPanel panel = new TerritoryConnectionsPanel();
panel.layout(mapXmlCreator.getStepActionPanel());
mapXmlCreator.setAutoFillAction(SwingAction.of(e -> {
panel.paintPreparation(null);
panel.repaint();
}));
}
@Override
protected void paintCenterSpecifics(final Graphics g, final String centerName, final FontMetrics fontMetrics,
final Point item, final int textStartX) {
if (centerName.equals(selectedTerritory.orElse(""))) {
final Rectangle2D stringBounds = fontMetrics.getStringBounds(centerName, g);
g.setColor(Color.yellow);
final int xRectPadding = 2;
final int yDiffCenterToRectTop = -6;
final int yDiffCenterToStringBottom = 5;
g.fillRect(Math.max(0, textStartX - xRectPadding), Math.max(0, item.y + yDiffCenterToRectTop),
(int) stringBounds.getWidth() + 2 * xRectPadding,
(int) stringBounds.getHeight());
g.setColor(Color.red);
g.drawString(centerName, Math.max(0, textStartX), item.y + yDiffCenterToStringBottom);
}
g.setColor(Color.red);
}
@Override
protected void paintPreparation(final Map<String, Point> centers) {
if (centers != null && !MapXmlHelper.getTerritoryConnectionsMap().isEmpty()) {
return;
}
final Map<String, List<Area>> territoryAreas = getTerritoryAreasFromPolygons();
final int lineThickness = showInputDialogForPositiveIntegerInput(
"Enter the width of territory border lines on your map? \r(eg: 1, or 2, etc.)", "1");
if (lineThickness == 0) {
return;
}
int scalePixels = 8;
double minOverlap = 32;
scalePixels = lineThickness * 4;
minOverlap = scalePixels * 4;
if (JOptionPane.showConfirmDialog(null,
"Scale set to " + scalePixels + " pixels larger, and minimum overlap set to " + minOverlap + " pixels. \r"
+ "Do you wish to continue with this? \rSelect Yes to continue, Select No to override and change the size.",
"Scale and Overlap Size", JOptionPane.YES_NO_OPTION) == 1) {
scalePixels = showInputDialogForPositiveIntegerInput(
"Enter the number of pixels larger each territory should become? \r"
+ "(Normally 4x bigger than the border line width. eg: 4, or 8, etc)",
"4");
if (scalePixels == 0) {
return;
}
minOverlap = showInputDialogForPositiveIntegerInput(
"Enter the minimum number of overlapping pixels for a connection? \r"
+ "(Normally 16x bigger than the border line width. eg: 16, or 32, etc.)",
"16");
if (minOverlap == 0) {
return;
}
}
setTerritoryConnections(territoryAreas, scalePixels, minOverlap);
}
private void setTerritoryConnections(final Map<String, List<Area>> territoryAreas, final int scalePixels,
final double minOverlap) {
MapXmlHelper.clearTerritoryConnections();
Logger.getLogger(MapXmlCreator.MAP_XML_CREATOR_LOGGER_NAME).log(Level.FINE,
"Now scanning for connections ... ");
// sort so that they are in alphabetic order (makes xml's prettier and easier to update in future)
final List<String> allTerritories =
polygons == null ? new ArrayList<>() : new ArrayList<>(polygons.keySet());
Collections.sort(allTerritories, new AlphanumComparator());
final List<String> allAreas = new ArrayList<>(territoryAreas.keySet());
Collections.sort(allAreas, new AlphanumComparator());
for (final String territory : allTerritories) {
final Set<String> thisTerritoryConnections = Sets.newLinkedHashSet();
final List<Polygon> currentPolygons = polygons.get(territory);
for (final Polygon currentPolygon : currentPolygons) {
final Shape scaledShape = ConnectionFinder.scale(currentPolygon, scalePixels);
for (final String otherTerritory : allAreas) {
if (otherTerritory.equals(territory)) {
continue;
}
if (thisTerritoryConnections.contains(otherTerritory)) {
continue;
}
if (MapXmlHelper.getTerritoryConnectionsMap().get(otherTerritory) != null
&& MapXmlHelper.getTerritoryConnectionsMap().get(otherTerritory).contains(territory)) {
continue;
}
for (final Area otherArea : territoryAreas.get(otherTerritory)) {
final Area testArea = new Area(scaledShape);
testArea.intersect(otherArea);
if (!testArea.isEmpty() && ConnectionFinder.sizeOfArea(testArea) > minOverlap) {
thisTerritoryConnections.add(otherTerritory);
}
}
}
MapXmlHelper.putTerritoryConnections(territory, thisTerritoryConnections);
}
}
Logger.getLogger(MapXmlCreator.MAP_XML_CREATOR_LOGGER_NAME).log(Level.FINE,
"finished scanning");
}
private Map<String, List<Area>> getTerritoryAreasFromPolygons() {
final Map<String, List<Area>> territoryAreas = Maps.newHashMap();
for (final String territoryName : polygons.keySet()) {
final List<Polygon> listOfPolygons = polygons.get(territoryName);
final List<Area> listOfAreas = new ArrayList<>();
for (final Polygon p : listOfPolygons) {
listOfAreas.add(new Area(p));
}
territoryAreas.put(territoryName, listOfAreas);
}
return territoryAreas;
}
/**
* Forces the user to either enter nothing or a positive integer.
*
* @return input value or 0 if nothing has been entered
*/
public int showInputDialogForPositiveIntegerInput(final String message, final String suggestedInput) {
String lineWidth = suggestedInput;
while (1 > 0) {
lineWidth = JOptionPane.showInputDialog(null,
message, lineWidth);
if (lineWidth == null) {
return 0;
}
try {
return Integer.parseInt(lineWidth);
} catch (final NumberFormatException ex) {
JOptionPane.showMessageDialog(null, "'" + lineWidth + "' is not a valid positive integer value.",
"Invalid Input",
JOptionPane.ERROR_MESSAGE);
// do-loop again
}
}
}
@Override
protected void paintOwnSpecifics(final Graphics g, final Map<String, Point> centers) {
g.setColor(Color.GREEN);
for (final Entry<String, Set<String>> territoryConnection : MapXmlHelper.getTerritoryConnectionsMap()
.entrySet()) {
final Point center1 = centers.get(territoryConnection.getKey());
for (final String territory2 : territoryConnection.getValue()) {
final Point center2 = centers.get(territory2);
g.drawLine(center1.x, center1.y, center2.x, center2.y);
}
}
}
@Override
protected void mouseClickedOnImage(final Map<String, Point> centers, final MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
if (selectedTerritory.isPresent()) {
selectedTerritory = Optional.empty();
repaint();
}
return;
}
final Point point = e.getPoint();
final Optional<String> territoryName = Util.findTerritoryName(point, polygons);
if (!territoryName.isPresent()) {
return;
}
if (needToBeRepainted(territoryName.get())) {
repaint();
}
}
private boolean needToBeRepainted(final String territoryName) {
boolean repaint = false;
if (!selectedTerritory.isPresent() || selectedTerritory.orElse("").equals(territoryName)) {
selectedTerritory = Optional.of(territoryName);
repaint = true;
} else {
Collection<String> firstTerritoryConnections;
String secondterritory;
if (territoryName.compareTo(selectedTerritory.get()) < 0) {
firstTerritoryConnections = MapXmlHelper.getTerritoryConnectionsMap().get(territoryName);
secondterritory = selectedTerritory.get();
} else {
firstTerritoryConnections = MapXmlHelper.getTerritoryConnectionsMap().get(selectedTerritory.get());
secondterritory = territoryName;
}
if (firstTerritoryConnections.contains(secondterritory)) {
firstTerritoryConnections.remove(secondterritory);
} else {
firstTerritoryConnections.add(secondterritory);
}
selectedTerritory = Optional.empty();
repaint = true;
}
return repaint;
}
}