package nl.tudelft.bw4t.map.renderer;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.HashSet;
import java.util.Set;
import nl.tudelft.bw4t.map.MapFormatException;
import nl.tudelft.bw4t.map.NewMap;
import nl.tudelft.bw4t.map.Path;
import nl.tudelft.bw4t.map.Point;
import nl.tudelft.bw4t.map.Zone;
import nl.tudelft.bw4t.map.Zone.Type;
/**
* An abstract {@link MapController} implementation, taking over as much
* functionality as possible. Without using client or server-specific code.
*
* This class has to be thread safe because ClientMapController has to be. But
* this does not mean that the objects that are returned are thread safe. Please
* consult thread safety of the relevant objects as well.
*/
public abstract class AbstractMapController extends MouseAdapter implements
MapController, Runnable {
/**
* The map to be rendered.
*/
private NewMap map;
/**
* Various rendering settings.
*/
private MapRenderSettings renderSettings;
/**
* True while the thread to update the {@link MapRendererInterface} is
* running.
*/
private boolean running = false;
private boolean starting = false;
/**
* The set of all connected {@link MapRendererInterface}s.
*/
private final Set<MapRendererInterface> renderers = new HashSet<>();
private boolean mouseOver = false;
/**
* Creates Default {@link MapRenderSettings}, sets the map and starts the
* updater.
*
* @param theMap
* the map to be used
*/
public AbstractMapController(NewMap theMap) {
renderSettings = new MapRenderSettings();
this.setMap(theMap);
setRunning(true);
}
/**
* @return the map
*/
public NewMap getMap() {
return map;
}
/**
* Set the map and update the world dimensions in the renderer.
*
* @param themap
* the map to set. Should not be null.
*/
public void setMap(NewMap themap) {
if (themap == null) {
throw new NullPointerException("given map is null");
}
this.map = themap;
Point size = map.getArea();
renderSettings.setWorldDimensions((int) size.getX(), (int) size.getY());
}
/**
* @return the renderSettings
*/
@Override
public MapRenderSettings getRenderSettings() {
return renderSettings;
}
/**
* @param theRenderSettings
* the renderSettings to set
*/
public void setRenderSettings(MapRenderSettings theRenderSettings) {
this.renderSettings = theRenderSettings;
}
/**
* Is the update thread running?
*
* @return true iff the thread is running.
*/
public boolean isRunning() {
return running;
}
/**
* Set the update thread running?
*
* @param run
* the value to be set
*/
public final void setRunning(boolean run) {
if (!run) {
setForceRunning(false);
starting = false;
} else if (!running && !starting) {
startupUpdateThread();
}
}
protected boolean isStarting() {
return starting;
}
/**
* Sets the actual variable doesn't start the thread.
*
* @param run
* the value to be set
*/
protected final void setForceRunning(boolean run) {
running = run;
if (run) {
starting = false;
}
}
/**
* @return the renderers
*/
public Set<MapRendererInterface> getRenderers() {
return renderers;
}
/**
* Start the update thread.(will fail if it's already running)
*/
private void startupUpdateThread() {
starting = true;
Thread thread = new Thread(new Updater(this), "Updater->"
+ Integer.toHexString(this.hashCode()));
thread.start();
}
@Override
public void addRenderer(MapRendererInterface mri) {
mri.addMouseListener(this);
mri.addMouseWheelListener(this);
getRenderers().add(mri);
}
@Override
public void removeRenderer(MapRendererInterface mri) {
mri.removeMouseListener(this);
mri.removeMouseWheelListener(this);
getRenderers().remove(mri);
}
@Override
public Set<Zone> getZones() {
return new HashSet<>(map.getZones());
}
@Override
public Set<Zone> getRooms() {
Set<Zone> rooms = new HashSet<>();
for (Zone zone : map.getZones()) {
if (zone.getType() == Type.ROOM) {
rooms.add(zone);
}
}
return rooms;
}
@Override
public Set<Zone> getChargingZones() {
Set<Zone> chargingzones = new HashSet<>();
for (Zone zone : map.getZones()) {
if (zone.getType() == Type.CHARGINGZONE) {
chargingzones.add(zone);
}
}
return chargingzones;
}
@Override
public Set<Zone> getBlockades() {
Set<Zone> blockades = new HashSet<>();
for (Zone zone : map.getZones()) {
if (zone.getType() == Type.BLOCKADE) {
blockades.add(zone);
}
}
return blockades;
}
@Override
public Zone getDropZone() {
for (Zone zone : map.getZones()) {
if (zone.getName().equals(Zone.DROP_ZONE_NAME)) {
return zone;
}
}
throw new MapFormatException("The map does not include a dropzone!");
}
/**
* Called every {@link MapRenderSettings#getUpdateDelay()}, to update the
* renderers. Probably not thread safe.
*/
@Override
public void run() {
for (MapRendererInterface mri : getRenderers()) {
updateRenderer(mri);
}
}
/**
* Called every {@link MapRenderSettings#getUpdateDelay()} for every
* associated {@link MapRendererInterface}.
*
* @param mri
* the current renderer
*/
protected abstract void updateRenderer(MapRendererInterface mri);
@Override
public Set<Path> getPaths() {
return new HashSet<>(0);
}
@Override
public void mouseEntered(MouseEvent e) {
this.mouseOver = true;
}
@Override
public void mouseExited(MouseEvent e) {
this.mouseOver = false;
}
/*
* (non-Javadoc)
*
* @see
* java.awt.event.MouseAdapter#mouseWheelMoved(java.awt.event.MouseWheelEvent
* )
*/
@Override
public void mouseWheelMoved(MouseWheelEvent mwe) {
if (mouseOver && mwe.isControlDown()) {
MapRenderSettings settings = this.getRenderSettings();
if (mwe.getUnitsToScroll() >= 0) {
settings.setScale(settings.getScale() + 0.1);
} else {
settings.setScale(settings.getScale() - 0.1);
}
mwe.getComponent().revalidate();
}
}
}