/**
*
*/
package iamrescue.routing.costs;
import iamrescue.agent.ISimulationTimer;
import iamrescue.agent.ITimeStepListener;
import iamrescue.execution.command.IPath;
import iamrescue.util.PositionXY;
import java.util.Map;
import java.util.Set;
import javolution.util.FastMap;
import javolution.util.FastSet;
import rescuecore2.standard.entities.Area;
import rescuecore2.standard.entities.StandardWorldModel;
import rescuecore2.worldmodel.EntityID;
/**
* @author Sebastian
*
*/
public class BlockableRoutingCostFunction implements IRoutingCostFunction,
ITimeStepListener {
private Map<TemporaryBlockKey, BlockInfo> blocks = new FastMap<TemporaryBlockKey, BlockInfo>();
private Map<Integer, Set<TemporaryBlockKey>> expiryTimes = new FastMap<Integer, Set<TemporaryBlockKey>>();
private IRoutingCostFunction baseFunction;
private ISimulationTimer timer;
private IRoutingCostChangeListener listener;
public BlockableRoutingCostFunction(IRoutingCostFunction baseFunction,
ISimulationTimer timer, IRoutingCostChangeListener listener) {
this.timer = timer;
this.baseFunction = baseFunction;
this.listener = listener;
timer.addTimeStepListener(this);
}
public BlockableRoutingCostFunction(IRoutingCostFunction baseFunction,
ISimulationTimer timer) {
this(baseFunction, timer, null);
}
/**
* @param listener
* the listener to set
*/
public void setListener(IRoutingCostChangeListener listener) {
this.listener = listener;
}
@Override
public double getCost(IPath path, StandardWorldModel worldModel) {
return baseFunction.getCost(path, worldModel);
}
@Override
public double getTravelCost(Area area, PositionXY from, PositionXY to) {
// Check if there is a block
TemporaryBlockKey key = new TemporaryBlockKey(area, to);
BlockInfo info = blocks.get(key);
if (info != null) {
boolean remove = false;
// Check if blocks have changed
if (info.validUntil < timer.getTime()) {
remove = true;
} else {
if (!area.isBlockadesDefined()) {
if (info.blockades.size() > 0) {
remove = true;
}
} else if (!area.getBlockades().containsAll(info.blockades)) {
remove = true;
}
}
if (remove) {
blocks.remove(key);
listener.routingCostChanged(area);
} else {
return info.multiplier
* baseFunction.getTravelCost(area, from, to);
}
}
return baseFunction.getTravelCost(area, from, to);
}
public void addTemporaryBlock(Area area, PositionXY position,
int validUntil, double multiplier) {
if (validUntil >= timer.getTime()) {
TemporaryBlockKey key = new TemporaryBlockKey(area, position);
BlockInfo existingInfo = blocks.get(key);
boolean addThis = true;
if (existingInfo != null) {
if (existingInfo.validUntil < validUntil) {
// Remove old expiry time
Set<TemporaryBlockKey> set = expiryTimes
.get(existingInfo.validUntil);
if (set.size() == 1) {
expiryTimes.remove(existingInfo.validUntil);
} else {
set.remove(key);
}
} else {
// Ignore this
addThis = false;
}
}
if (addThis) {
Set<EntityID> blockades = new FastSet<EntityID>();
if (area.isBlockadesDefined()) {
blockades.addAll(area.getBlockades());
}
blocks.put(key,
new BlockInfo(validUntil, blockades, multiplier));
Set<TemporaryBlockKey> set = expiryTimes.get(validUntil);
if (set == null) {
set = new FastSet<TemporaryBlockKey>();
expiryTimes.put(validUntil, set);
}
set.add(key);
}
}
}
@Override
public void notifyTimeStepStarted(int timeStep) {
Set<TemporaryBlockKey> keys = expiryTimes.remove(timeStep);
if (keys != null) {
for (TemporaryBlockKey temporaryBlockKey : keys) {
Area area = temporaryBlockKey.area;
blocks.remove(temporaryBlockKey);
listener.routingCostChanged(area);
}
}
}
private static class BlockInfo {
private int validUntil;
private Set<EntityID> blockades;
private double multiplier;
public BlockInfo(int validUntil, Set<EntityID> blockades,
double multiplier) {
super();
this.validUntil = validUntil;
this.blockades = blockades;
this.multiplier = multiplier;
}
}
private static class TemporaryBlockKey {
private Area area;
private PositionXY position;
public TemporaryBlockKey(Area area, PositionXY position) {
this.area = area;
this.position = position;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((area == null) ? 0 : area.hashCode());
result = prime * result
+ ((position == null) ? 0 : position.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TemporaryBlockKey other = (TemporaryBlockKey) obj;
if (area == null) {
if (other.area != null)
return false;
} else if (!area.equals(other.area))
return false;
if (position == null) {
if (other.position != null)
return false;
} else if (!position.equals(other.position))
return false;
return true;
}
}
}