package rescuecore2.standard.view;
import rescuecore2.messages.Command;
import rescuecore2.misc.Pair;
import rescuecore2.misc.gui.DrawingTools;
import rescuecore2.misc.gui.ScreenTransform;
import rescuecore2.view.RenderedObject;
import rescuecore2.view.Icons;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.messages.AKMove;
import rescuecore2.standard.messages.AKExtinguish;
import rescuecore2.standard.messages.AKClear;
import rescuecore2.standard.messages.AKRescue;
import rescuecore2.standard.messages.AKLoad;
import rescuecore2.standard.messages.AKUnload;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.event.ActionEvent;
import javax.swing.Action;
import javax.swing.AbstractAction;
import javax.swing.JMenuItem;
/**
A layer for viewing commands.
*/
public class CommandLayer extends StandardViewLayer {
private static final int SIZE = 15;
private static final Color CLEAR_COLOUR = new Color(0, 0, 255, 128);
private static final Color RESCUE_COLOUR = new Color(255, 255, 255, 128);
private static final Color LOAD_COLOUR = new Color(255, 255, 255, 128);
private static final Color UNLOAD_COLOUR = new Color(255, 255, 255, 128);
private static final double ARROW_ANGLE = Math.toRadians(135);
private static final double ARROW_LENGTH = 5;
private Graphics2D g;
private ScreenTransform t;
private Collection<Command> commands;
private boolean renderMove;
private boolean renderExtinguish;
private boolean renderClear;
private boolean renderLoad;
private boolean renderUnload;
private boolean renderRescue;
private RenderMoveAction renderMoveAction;
private RenderExtinguishAction renderExtinguishAction;
private RenderClearAction renderClearAction;
private RenderRescueAction renderRescueAction;
private RenderLoadAction renderLoadAction;
private RenderUnloadAction renderUnloadAction;
/**
Construct a new CommandLayer.
*/
public CommandLayer() {
commands = new ArrayList<Command>();
renderMove = true;
renderExtinguish = true;
renderClear = true;
renderLoad = true;
renderUnload = true;
renderRescue = true;
renderMoveAction = new RenderMoveAction();
renderExtinguishAction = new RenderExtinguishAction();
renderClearAction = new RenderClearAction();
renderRescueAction = new RenderRescueAction();
renderLoadAction = new RenderLoadAction();
renderUnloadAction = new RenderUnloadAction();
}
/**
Set whether to render Move commands.
@param render True if move commands should be rendered, false otherwise.
*/
public void setRenderMove(boolean render) {
renderMove = render;
renderMoveAction.update();
}
/**
Set whether to render Extinguish commands.
@param render True if extinguish commands should be rendered, false otherwise.
*/
public void setRenderExtinguish(boolean render) {
renderExtinguish = render;
renderExtinguishAction.update();
}
/**
Set whether to render Clear commands.
@param render True if clear commands should be rendered, false otherwise.
*/
public void setRenderClear(boolean render) {
renderClear = render;
renderClearAction.update();
}
/**
Set whether to render Load commands.
@param render True if load commands should be rendered, false otherwise.
*/
public void setRenderLoad(boolean render) {
renderLoad = render;
renderLoadAction.update();
}
/**
Set whether to render Unload commands.
@param render True if unload commands should be rendered, false otherwise.
*/
public void setRenderUnload(boolean render) {
renderUnload = render;
renderUnloadAction.update();
}
/**
Set whether to render Rescue commands.
@param render True if rescue commands should be rendered, false otherwise.
*/
public void setRenderRescue(boolean render) {
renderRescue = render;
renderRescueAction.update();
}
@Override
public List<JMenuItem> getPopupMenuItems() {
List<JMenuItem> result = new ArrayList<JMenuItem>();
result.add(new JMenuItem(renderMoveAction));
result.add(new JMenuItem(renderClearAction));
result.add(new JMenuItem(renderExtinguishAction));
result.add(new JMenuItem(renderRescueAction));
result.add(new JMenuItem(renderLoadAction));
result.add(new JMenuItem(renderUnloadAction));
return result;
}
@Override
public String getName() {
return "Commands";
}
@Override
public Rectangle2D view(Object... objects) {
synchronized (commands) {
commands.clear();
return super.view(objects);
}
}
@Override
protected void viewObject(Object o) {
super.viewObject(o);
if (o instanceof Command) {
commands.add((Command)o);
}
}
@Override
public Collection<RenderedObject> render(Graphics2D graphics, ScreenTransform transform, int width, int height) {
synchronized (commands) {
Collection<RenderedObject> result = new ArrayList<RenderedObject>();
g = graphics;
t = transform;
for (Command next : commands) {
if (renderMove && next instanceof AKMove) {
renderMove((AKMove)next);
}
if (renderExtinguish && next instanceof AKExtinguish) {
renderExtinguish((AKExtinguish)next);
}
if (renderClear && next instanceof AKClear) {
renderClear((AKClear)next);
}
if (renderRescue && next instanceof AKRescue) {
renderRescue((AKRescue)next);
}
if (renderLoad && next instanceof AKLoad) {
renderLoad((AKLoad)next);
}
if (renderUnload && next instanceof AKUnload) {
renderUnload((AKUnload)next);
}
}
return result;
}
}
private void renderMove(AKMove move) {
g.setColor(Color.BLACK);
List<EntityID> path = move.getPath();
if (path.isEmpty()) {
return;
}
Iterator<EntityID> it = path.iterator();
StandardEntity first = world.getEntity(it.next());
Pair<Integer, Integer> firstLocation = first.getLocation(world);
int startX = t.xToScreen(firstLocation.first());
int startY = t.yToScreen(firstLocation.second());
while (it.hasNext()) {
StandardEntity next = world.getEntity(it.next());
Pair<Integer, Integer> nextLocation = next.getLocation(world);
int nextX = t.xToScreen(nextLocation.first());
int nextY = t.yToScreen(nextLocation.second());
g.drawLine(startX, startY, nextX, nextY);
// Draw an arrow partway along the length
DrawingTools.drawArrowHeads(startX, startY, nextX, nextY, g);
startX = nextX;
startY = nextY;
}
}
private void renderExtinguish(AKExtinguish ex) {
StandardEntity fb = world.getEntity(ex.getAgentID());
StandardEntity target = world.getEntity(ex.getTarget());
Pair<Integer, Integer> fbLocation = fb.getLocation(world);
Pair<Integer, Integer> targetLocation = target.getLocation(world);
int fbX = t.xToScreen(fbLocation.first());
int fbY = t.yToScreen(fbLocation.second());
int bX = t.xToScreen(targetLocation.first());
int bY = t.yToScreen(targetLocation.second());
g.setColor(Color.BLUE);
g.drawLine(fbX, fbY, bX, bY);
}
private void renderClear(AKClear clear) {
renderHumanAction(world.getEntity(clear.getAgentID()), CLEAR_COLOUR, null);
}
private void renderRescue(AKRescue rescue) {
renderHumanAction(world.getEntity(rescue.getAgentID()), RESCUE_COLOUR, null);
}
private void renderLoad(AKLoad load) {
renderHumanAction(world.getEntity(load.getAgentID()), LOAD_COLOUR, "L");
}
private void renderUnload(AKUnload unload) {
renderHumanAction(world.getEntity(unload.getAgentID()), UNLOAD_COLOUR, "U");
}
private void renderHumanAction(StandardEntity entity, Color colour, String s) {
Pair<Integer, Integer> location = entity.getLocation(world);
int x = t.xToScreen(location.first()) - SIZE / 2;
int y = t.yToScreen(location.second()) - SIZE / 2;
Shape shape = new Ellipse2D.Double(x, y, SIZE, SIZE);
g.setColor(colour);
g.fill(shape);
if (s != null) {
g.setColor(Color.BLACK);
FontMetrics metrics = g.getFontMetrics();
int width = metrics.stringWidth(s);
int height = metrics.getHeight();
x = t.xToScreen(location.first());
y = t.yToScreen(location.second());
g.drawString(s, x - (width / 2), y + (height / 2));
}
}
private final class RenderMoveAction extends AbstractAction {
public RenderMoveAction() {
super("Show move commands");
update();
}
@Override
public void actionPerformed(ActionEvent e) {
setRenderMove(!renderMove);
component.repaint();
}
void update() {
putValue(Action.SELECTED_KEY, Boolean.valueOf(renderMove));
putValue(Action.SMALL_ICON, renderMove ? Icons.TICK : Icons.CROSS);
}
}
private final class RenderClearAction extends AbstractAction {
public RenderClearAction() {
super("Show clear commands");
update();
}
@Override
public void actionPerformed(ActionEvent e) {
setRenderClear(!renderClear);
component.repaint();
}
void update() {
putValue(Action.SELECTED_KEY, Boolean.valueOf(renderClear));
putValue(Action.SMALL_ICON, renderClear ? Icons.TICK : Icons.CROSS);
}
}
private final class RenderExtinguishAction extends AbstractAction {
public RenderExtinguishAction() {
super("Show extinguish commands");
update();
}
@Override
public void actionPerformed(ActionEvent e) {
setRenderExtinguish(!renderExtinguish);
component.repaint();
}
void update() {
putValue(Action.SELECTED_KEY, Boolean.valueOf(renderExtinguish));
putValue(Action.SMALL_ICON, renderExtinguish ? Icons.TICK : Icons.CROSS);
}
}
private final class RenderRescueAction extends AbstractAction {
public RenderRescueAction() {
super("Show rescue commands");
update();
}
@Override
public void actionPerformed(ActionEvent e) {
setRenderRescue(!renderRescue);
component.repaint();
}
void update() {
putValue(Action.SELECTED_KEY, Boolean.valueOf(renderRescue));
putValue(Action.SMALL_ICON, renderRescue ? Icons.TICK : Icons.CROSS);
}
}
private final class RenderLoadAction extends AbstractAction {
public RenderLoadAction() {
super("Show load commands");
update();
}
@Override
public void actionPerformed(ActionEvent e) {
setRenderLoad(!renderLoad);
component.repaint();
}
void update() {
putValue(Action.SELECTED_KEY, Boolean.valueOf(renderLoad));
putValue(Action.SMALL_ICON, renderLoad ? Icons.TICK : Icons.CROSS);
}
}
private final class RenderUnloadAction extends AbstractAction {
public RenderUnloadAction() {
super("Show unload commands");
update();
}
@Override
public void actionPerformed(ActionEvent e) {
setRenderUnload(!renderUnload);
component.repaint();
}
void update() {
putValue(Action.SELECTED_KEY, Boolean.valueOf(renderUnload));
putValue(Action.SMALL_ICON, renderUnload ? Icons.TICK : Icons.CROSS);
}
}
}