package rescuecore2.standard.view;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.event.ActionEvent;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import javax.swing.JMenuItem;
import javax.swing.Action;
import javax.swing.AbstractAction;
import java.util.Comparator;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.List;
import java.util.ArrayList;
import java.net.URL;
import rescuecore2.misc.Pair;
import rescuecore2.misc.gui.ScreenTransform;
import rescuecore2.config.Config;
import rescuecore2.view.Icons;
import rescuecore2.log.Logger;
import rescuecore2.standard.entities.Human;
import rescuecore2.standard.entities.AmbulanceTeam;
import rescuecore2.standard.entities.Civilian;
import rescuecore2.standard.entities.StandardEntityURN;
/**
A view layer that renders humans.
*/
public class HumanLayer extends StandardEntityViewLayer<Human> {
private static final int SIZE = 10;//the size of agents shown in GUI bing
private static final int HP_MAX = 10000;
private static final int HP_INJURED = 7500;
private static final int HP_CRITICAL = 1000;
private static final String ICON_SIZE_KEY = "view.standard.human.icons.size";
private static final String USE_ICONS_KEY = "view.standard.human.icons.use";
private static final int DEFAULT_ICON_SIZE = 32;
private static final HumanSorter HUMAN_SORTER = new HumanSorter();
private static final Color CIVILIAN_COLOUR = Color.GREEN;
private static final Color FIRE_BRIGADE_COLOUR = Color.RED;
private static final Color POLICE_FORCE_COLOUR = Color.BLUE;
private static final Color AMBULANCE_TEAM_COLOUR = Color.WHITE;
private static final Color DEAD_COLOUR = Color.BLACK;
private int iconSize;
private Map<String, Map<State, Icon>> icons;
private boolean useIcons;
private Action useIconsAction;
/**
Construct a human view layer.
*/
public HumanLayer() {
super(Human.class);
iconSize = DEFAULT_ICON_SIZE;
}
@Override
public void initialise(Config config) {
iconSize = config.getIntValue(ICON_SIZE_KEY, DEFAULT_ICON_SIZE);
icons = new HashMap<String, Map<State, Icon>>();
useIcons = config.getBooleanValue(USE_ICONS_KEY, false);
icons.put(StandardEntityURN.FIRE_BRIGADE.toString(), generateIconMap("FireBrigade"));
icons.put(StandardEntityURN.AMBULANCE_TEAM.toString(), generateIconMap("AmbulanceTeam"));
icons.put(StandardEntityURN.POLICE_FORCE.toString(), generateIconMap("PoliceForce"));
icons.put(StandardEntityURN.CIVILIAN.toString() + "-Male", generateIconMap("Civilian-Male"));
icons.put(StandardEntityURN.CIVILIAN.toString() + "-Female", generateIconMap("Civilian-Female"));
useIconsAction = new UseIconsAction();
}
@Override
public String getName() {
return "Humans";
}
@Override
public Shape render(Human h, Graphics2D g, ScreenTransform t) {
// Don't draw humans in ambulances
if (h.isPositionDefined() && (world.getEntity(h.getPosition()) instanceof AmbulanceTeam)) {
return null;
}
Pair<Integer, Integer> location = getLocation(h);
if (location == null) {
return null;
}
int x = t.xToScreen(location.first());
int y = t.yToScreen(location.second());
Shape shape;
Icon icon = useIcons ? getIcon(h) : null;
if (icon == null) {
shape = new Ellipse2D.Double(x - SIZE / 2, y - SIZE / 2, SIZE, SIZE);
g.setColor(adjustColour(getColour(h), h.getHP()));
g.fill(shape);
}
else {
x -= icon.getIconWidth() / 2;
y -= icon.getIconHeight() / 2;
shape = new Rectangle2D.Double(x, y, icon.getIconWidth(), icon.getIconHeight());
icon.paintIcon(null, g, x, y);
}
return shape;
}
@Override
public List<JMenuItem> getPopupMenuItems() {
List<JMenuItem> result = new ArrayList<JMenuItem>();
result.add(new JMenuItem(useIconsAction));
return result;
}
@Override
protected void postView() {
Collections.sort(entities, HUMAN_SORTER);
}
/**
Get the location of a human.
@param h The human to look up.
@return The location of the human.
*/
protected Pair<Integer, Integer> getLocation(Human h) {
return h.getLocation(world);
}
private Map<State, Icon> generateIconMap(String type) {
Map<State, Icon> result = new EnumMap<State, Icon>(State.class);
for (State state : State.values()) {
String resourceName = "rescuecore2/standard/view/" + type + "-" + state.toString() + "-" + iconSize + "x" + iconSize + ".png";
URL resource = HumanLayer.class.getClassLoader().getResource(resourceName);
if (resource == null) {
Logger.warn("Couldn't find resource: " + resourceName);
}
else {
result.put(state, new ImageIcon(resource));
}
}
return result;
}
private Color getColour(Human h) {
switch (h.getStandardURN()) {
case CIVILIAN:
return CIVILIAN_COLOUR;
case FIRE_BRIGADE:
return FIRE_BRIGADE_COLOUR;
case AMBULANCE_TEAM:
return AMBULANCE_TEAM_COLOUR;
case POLICE_FORCE:
return POLICE_FORCE_COLOUR;
default:
throw new IllegalArgumentException("Don't know how to draw humans of type " + h.getStandardURN());
}
}
private Color adjustColour(Color c, int hp) {
if (hp == 0) {
return DEAD_COLOUR;
}
if (hp < HP_CRITICAL) {
c = c.darker();
}
if (hp < HP_INJURED) {
c = c.darker();
}
if (hp < HP_MAX) {
c = c.darker();
}
return c;
}
private Icon getIcon(Human h) {
State state = getState(h);
Map<State, Icon> iconMap = null;
switch (h.getStandardURN()) {
case CIVILIAN:
boolean male = h.getID().getValue() % 2 == 0;
if (male) {
iconMap = icons.get(StandardEntityURN.CIVILIAN.toString() + "-Male");
}
else {
iconMap = icons.get(StandardEntityURN.CIVILIAN.toString() + "-Female");
}
break;
default:
iconMap = icons.get(h.getStandardURN().toString());
}
if (iconMap == null) {
return null;
}
return iconMap.get(state);
}
private State getState(Human h) {
int hp = h.getHP();
if (hp <= 0) {
return State.DEAD;
}
if (hp <= HP_CRITICAL) {
return State.CRITICAL;
}
if (hp <= HP_INJURED) {
return State.INJURED;
}
return State.HEALTHY;
}
private enum State {
HEALTHY {
@Override
public String toString() {
return "Healthy";
}
},
INJURED {
@Override
public String toString() {
return "Injured";
}
},
CRITICAL {
@Override
public String toString() {
return "Critical";
}
},
DEAD {
@Override
public String toString() {
return "Dead";
}
};
}
private static final class HumanSorter implements Comparator<Human>, java.io.Serializable {
@Override
public int compare(Human h1, Human h2) {
if (h1 instanceof Civilian && !(h2 instanceof Civilian)) {
return -1;
}
if (h2 instanceof Civilian && !(h1 instanceof Civilian)) {
return 1;
}
return h1.getID().getValue() - h2.getID().getValue();
}
}
private final class UseIconsAction extends AbstractAction {
public UseIconsAction() {
super("Use icons");
putValue(Action.SELECTED_KEY, Boolean.valueOf(useIcons));
putValue(Action.SMALL_ICON, useIcons ? Icons.TICK : Icons.CROSS);
}
@Override
public void actionPerformed(ActionEvent e) {
useIcons = !useIcons;
putValue(Action.SELECTED_KEY, Boolean.valueOf(useIcons));
putValue(Action.SMALL_ICON, useIcons ? Icons.TICK : Icons.CROSS);
component.repaint();
}
}
}