public final class Renderer{

	/** The only instance of this class (singleton). */
	private static final Renderer INSTANCE = new Renderer();

	/**
	 * Gets the single instance of this renderer.
	 * 
	 * @return single instance of this renderer
	 */
	public static Renderer getInstance(){
		return INSTANCE;
	}
	
	/** The size of one single vehicle (2,5m). */
	private static final int VEHICLE_SIZE = 250;
	
	/** A global formatter to get locale-specific grouping separators from numeric values */
	private static final DecimalFormat FORMATTER = new DecimalFormat(",###"); //$NON-NLS-1$
		
	/** A reference to the singleton instance of the {@link vanetsim.map.Map} because we need this quite often and don't want to rely on compiler inlining. */
	private final Map map_ = Map.getInstance();
	
	/** The font used for displaying the current time. */
	private final Font timeFont_ = new Font("Default", Font.PLAIN, 11); //$NON-NLS-1$
	
	/** The font used for displaying the silent period status. */
	private final Font silentPeriodFont_ = new Font("Default", Font.BOLD, 20); //$NON-NLS-1$
	
	/** The font size for displaying the vehicle ID. */
	private final int vehicleIDFontSize_ = 150;
	
	/** The font used for displaying the vehicle ID. */
	private final Font vehicleIDFont_ = new Font("SansSerif", Font.PLAIN, vehicleIDFontSize_); //$NON-NLS-1$
	
	/** A cached <code>BasicStroke</code> used as a (black) background for streets with 2 lanes total. */
	private final BasicStroke lineBackground_ = new BasicStroke((Map.LANE_WIDTH + 45)*2,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER);

	/** A cached <code>BasicStroke</code> used for painting streets with 2 lanes total. */
	private final BasicStroke lineMain_ = new BasicStroke(Map.LANE_WIDTH*2,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER);

	/** The <code>AffineTransform</code> which handles zooming and panning transparently for us. */
	private final AffineTransform transform_ = new AffineTransform();
	
	/** Used as source to transform the blockingImage into current coordinate space. */
	private final Point2D blockingImageTransformSource_ = new Point2D.Double(0,0);;

	/** To get the result of the transformation of the blockingImage. */
	private final Point2D blockingImageTransformDestination_ = new Point2D.Double(0,0);
	
	/** If a simulation thread is currently running. */
	private boolean simulationRunning_ = false;

	/** Used to display how long the relative time in milliseconds since the simulation started. */
	private int timePassed_ = 0;

	/** A boolean indicating if a new full render (=rendering of static objects) is needed. */
	private boolean scheduleFullRender_ = false;

	/** <code>true</code> if the current painting was forced by the simulation master, else <code>false</code> (by AWT/Swing or so). */
	private boolean doPaintInitializedBySimulation_ = false;

	/** The {@link DrawingArea} for the simulation. */
	private DrawingArea drawArea_;

	/** The width of the {@link DrawingArea}. */
	private int drawWidth_ = 0;

	/** The height of the {@link DrawingArea}. */
	private int drawHeight_ = 0;	

	/** The x coordinate of the center of the currently drawn map part. */
	private double middleX_ = 0;

	/** The y coordinate of the center of the currently drawn map part. */
	private double middleY_ = 0;	

	/** The minimum x coordinate to be painted. */
	private int mapMinX_ = 0;

	/** The minimum y coordinate to be painted. */
	private int mapMinY_ = 0;

	/** The maximum x coordinate to be painted. */
	private int mapMaxX_ = 0;

	/** The maximum y coordinate to be painted. */
	private int mapMaxY_ = 0;
	
	/** If the current rendered view displays over the borders of the map. Used to detect when efficient panning can NOT be used. */
	private boolean currentOverdrawn_ = true;
	
	/** If the last rendered view displays over the borders of the map. Used to detect when efficient panning can NOT be used. */
	private boolean lastOverdrawn_ = true;

	/** The current zooming factor. */
	private double zoom_ = Math.exp(1/100.0)/1000;

	/** The amount of pans in x direction (negative if moved down) since the last paint of the static objects. */
	private int panCountX_ = 0;

	/** The amount of pans in y direction (negative if moved down) since the last paint of the static objects. */
	private int panCountY_ = 0;

	/** The x-axis index of the leftmost region which is currently considered to be rendered. */
	private int regionMinX_ = 0;

	/** The y-axis index of the leftmost region which is currently considered to be rendered. */
	private int regionMinY_ = 0;

	/** The x-axis index of the rightmost region which is currently considered to be rendered. */
	private int regionMaxX_ = 0;

	/** The y-axis index of the rightmost region which is currently considered to be rendered. */
	private int regionMaxY_ = 0;

	/** A temporary <code>AffineTransform</code>. Prevents some unnecessary garbage collection. */
	private AffineTransform tmpAffine_ = new AffineTransform();

	/** The maximum translation in x direction we got from a <code>Graphics2D</code> object (used because this value sometimes is wrong when using substance themes). */
	private double maxTranslationX_ = 0;

	/** The maximum translation in y direction we got from a <code>Graphics2D</code> object (used because this value sometimes is wrong when using substance themes). */
	private double maxTranslationY_ = 0;

	/** A street which is to be drawn marked (selected by user). */
	private Street markedStreet_ = null;	

	/** A vehicle which is to be drawn marked (selected by user). */
	private Vehicle markedVehicle_ = null;
	
	/** A vehicle which is to be drawn as attacker (selected by user). */
	private Vehicle attackerVehicle_ = null;
	
	/** A vehicle which is to be drawn as attacked vehicle (selected by user). */
	private Vehicle attackedVehicle_ = null;
	
	/** If circles shall be displayed to indicate communication distances. */
	private boolean highlightCommunication_ = false;

	/** If all nodes shall be highlighted. */
	private boolean highlightAllNodes_ = false;
	
	/** If mix zones shall be hided. */
	private boolean hideMixZones_ = false;
	
	/** If vehicle IDs shall be displayed. */
	private boolean displayVehicleIDs_ = false;

	/** <code>true</code> if all blockings shall be showed, else <code>false</code>. */
	private boolean showAllBlockings_;
	
	/** If the monitored beacon zone shall be indicated or not */
	private boolean showBeaconMonitorZone_ = false;
	
	/** If vehicles should be displayed (used in vehicle edit modes) */
	private boolean showVehicles_ = false;
	
	/** If mix zones should be displayed (used in mix zone edit mode)*/
	private boolean showMixZones_ = false;
	
	/** If RSUs should be displayed (used in RSU edit mode)*/
	private boolean showRSUs_ = false;
	
	/** If attacker and attacked vehicle should be displayed*/
	private boolean showAttackers_ = false;
	
	/** If a mix zone should be added at each street corner */
	private boolean autoAddMixZones_ = false;
	
	/** The minimum x coordinate which is checked during beacon monitoring. */
	private int beaconMonitorMinX_ = -1;
	
	/** The maximum x coordinate which is checked during beacon monitoring. */
	private int beaconMonitorMaxX_ = -1;
	
	/** The minimum y coordinate which is checked during beacon monitoring. */
	private int beaconMonitorMinY_ = -1;
	
	/** The maximum y coordinate which is checked during beacon monitoring. */
	private int beaconMonitorMaxY_ = -1;
	
	/** The RSU color */
	private Color rsuColor = new Color(0,100,0);
	
	/** The ARSU color */
	private Color arsuColor = new Color(100,0,0);

	/** A <code>CyclicBarrier</code> to signal the SimulationMaster that we are ready with rendering the dynamic objects. */
	private CyclicBarrier barrierForSimulationMaster_;

	/** An image to indicate that there's a blocking. */
	private BufferedImage blockingImage_;
	
	/** An image to indicate that there's a slippery road. :) */
	private BufferedImage slipperyImage_;
	
	/** A scaled instance of the <code>blockingImage_</code>. Is updated on every zoom change. */
	private BufferedImage scaledBlockingImage_;
	
	/** A scaled instance of the <code>slipperyImage_</code>. Is updated on every zoom change. */
	private BufferedImage scaledSlipperyImage_;
	
	/** flag for console start*/
	private boolean consoleStart = false;

	/** the marked junction*/
	private Junction markedJunction_ = null;

	/** arraylist to show if a beacon was guessed correctly after mix. Is not used while simulation is running, so performance doesn't have to be that good*/
	private ArrayList<String> locationInformationMix_ = null;
	
	/** arraylist to show if a beacon was guessed correctly after silent period. Is not used while simulation is running, so performance doesn't have to be that good*/
	private ArrayList<String> locationInformationSilentPeriod_ = null;
	
	/** counts passed mix-zones. Used to show location information */
	private int mixZoneAmount = 0;
	
	/** flag to activate the debug mode. In this mode more information is shown to the user */
	private boolean debugMode = false;
	
	/** flag to enable schools, police, ... on map (this are not event spots, but can be imported) */
	private boolean showAmenities = false;
	
	/** the selected event grid */
	private int[][] grid = null;
	
	/** the min value of the grid */
	private int minGridValue = 0;
	
	/** the max value of the grid */
	private int maxGridValue = 0;
	
	/** the grid size */
	private int gridSize_ = 0;
	
	/** flag to show the penalty connections */
	private boolean showPenaltyConnections_ = false;
	
	/** flag to show known vehicles connections */
	private boolean showKnownVehiclesConnections_ = false;
	
	/** the x values from the centroids */
	private ArrayList<Double> centroidsX = null;
	
	/** the y values from the centroids */
	private ArrayList<Double> centroidsY = null;
	
	/** a ArrayList of the clusters */
	private ArrayList<Cluster> clusters = new ArrayList<Cluster>();
	
	/** a cluster that should be shown */
	private Cluster displayCluster_ = null;
	
	/** a flag to toggle between cluster display modes */
	private boolean showAllClusters = false;
	
	/** The font used for displaying the cluster ID. */
	private final Font clusterIDFont_ = new Font("SansSerif", Font.PLAIN, 1000); //$NON-NLS-1$
	
	/**
	 * Private constructor in order to disable instancing. Creates the blocking images.
	 */
	private Renderer(){
		try{
			//We're not directly using the BufferedImage returned by ImageIO as this might not be suitable for the graphics environment and thus slow. The small overhead here does not hurt
			//as it's just done on program startup
			BufferedImage tmpImage;
			URL url = ClassLoader.getSystemResource("vanetsim/images/blocking.png"); //$NON-NLS-1$
			tmpImage = ImageIO.read(url);
			blockingImage_ = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(tmpImage.getWidth(), tmpImage.getHeight(), 3);
			blockingImage_.getGraphics().drawImage(tmpImage, 0, 0, null);
			
			url = ClassLoader.getSystemResource("vanetsim/images/slippery_road.png"); 
			tmpImage = ImageIO.read(url);
			slipperyImage_ = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(tmpImage.getWidth(), tmpImage.getHeight(), 3);
			slipperyImage_.getGraphics().drawImage(tmpImage, 0, 0, null);
		} catch(Exception e){
			ErrorLog.log(Messages.getString("Renderer.noBlockingImage"), 7, Renderer.class.getName(), "Constructor", e);  //$NON-NLS-1$//$NON-NLS-2$
			//create just empty transparent images.
			blockingImage_ = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(1, 1, 3);
			slipperyImage_ = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(1, 1, 3);
		}
	}

	/**
	 * This function renders all non-static objects on the supplied <code>Graphics2D</code> object.
	 * 
	 * @param g2d	the <code>Graphics2D</code> object on which rendering takes place
	 */
	public void drawMovingObjects(Graphics2D g2d){
		Region[][] regions = map_.getRegions();
		if(regions != null && map_.getReadyState() && (!simulationRunning_ || doPaintInitializedBySimulation_)){
			int i, j, k, size;
			Vehicle vehicle;
			Vehicle[] vehicles;

			// A small fix because the substance theme engine sometimes causes unwanted shifts
			AffineTransform g2dAffine = g2d.getTransform();	//cache to save some calls
			if(g2dAffine.getTranslateX() > maxTranslationX_) maxTranslationX_ = g2dAffine.getTranslateX();
			if(g2dAffine.getTranslateY() > maxTranslationY_) maxTranslationY_ = g2dAffine.getTranslateY();		
			if(g2dAffine.getTranslateX() < 0 || g2dAffine.getTranslateY() < 0) tmpAffine_ = (AffineTransform)g2dAffine.clone();
			else if(g2dAffine.getTranslateX() < maxTranslationX_ || g2dAffine.getTranslateY() < maxTranslationY_) tmpAffine_.setToIdentity();
			else tmpAffine_.setToTranslation(maxTranslationX_, maxTranslationY_);
			tmpAffine_.concatenate(transform_);
			AffineTransform tmpAffine2 = g2d.getTransform();
			g2d.setTransform(tmpAffine_);
			
			if(showBeaconMonitorZone_){
				g2d.setColor(Color.orange);
				g2d.drawRect(beaconMonitorMinX_, beaconMonitorMinY_, beaconMonitorMaxX_ - beaconMonitorMinX_, beaconMonitorMaxY_ - beaconMonitorMinY_);
			}
			
			// draw street marked by user
			if(markedStreet_!=null){
				g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
				int totalLanes;
				if(markedStreet_.isOneway()) totalLanes = markedStreet_.getLanesCount();
				else totalLanes = 2 * markedStreet_.getLanesCount();

				int x0 = markedStreet_.getStartNode().getX();
				int y0 = markedStreet_.getStartNode().getY();
				int x1 = markedStreet_.getEndNode().getX();
				int y1 = markedStreet_.getEndNode().getY();

				// paint the line with a black background
				g2d.setStroke(new BasicStroke(Map.LANE_WIDTH*totalLanes + 45,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER));
				g2d.setPaint(Color.BLACK);
				g2d.drawLine(x0, y0, x1, y1);

				// paint arrows to indicate direction. See http://lifshitz.ucdavis.edu/~dmartin/teach_java/slope/arrows.html (link last visited: 04.09.2008)
				int deltaX = x1 - x0;
				int deltaY = y1 - y0;
				double frac = 0.2;
				if(markedStreet_.getLength() < 1000 && markedStreet_.isOneway())frac = 0.9;	//paint short streets which are oneway with longer arrow. There's no solution implemented for short twoway-streets...

				g2d.drawLine(x0 + (int)((1-frac)*deltaX + frac*deltaY),y0 + (int)((1-frac)*deltaY - frac*deltaX),x1, y1);
				g2d.drawLine(x0 + (int)((1-frac)*deltaX - frac*deltaY),y0 + (int)((1-frac)*deltaY + frac*deltaX),x1, y1);
				if(!markedStreet_.isOneway()){
					deltaX = -deltaX;
					deltaY = -deltaY;
					g2d.drawLine(x1 + (int)((1-frac)*deltaX + frac*deltaY),y1 + (int)((1-frac)*deltaY - frac*deltaX),x0, y0);
					g2d.drawLine(x1 + (int)((1-frac)*deltaX - frac*deltaY),y1 + (int)((1-frac)*deltaY + frac*deltaX),x0, y0);
					deltaX = -deltaX;
					deltaY = -deltaY;
				}

				// paint the whole arrow again but this time with the fill color
				g2d.setStroke(new BasicStroke(Map.LANE_WIDTH*totalLanes,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER));
				g2d.setPaint(Color.CYAN);
				g2d.drawLine(x0, y0, x1, y1);

				g2d.drawLine(x0 + (int)((1-frac)*deltaX + frac*deltaY),y0 + (int)((1-frac)*deltaY - frac*deltaX),x1, y1);
				g2d.drawLine(x0 + (int)((1-frac)*deltaX - frac*deltaY),y0 + (int)((1-frac)*deltaY + frac*deltaX),x1, y1);
				if(!markedStreet_.isOneway()){
					deltaX = -deltaX;
					deltaY = -deltaY;
					g2d.drawLine(x1 + (int)((1-frac)*deltaX + frac*deltaY),y1 + (int)((1-frac)*deltaY - frac*deltaX),x0, y0);
					g2d.drawLine(x1 + (int)((1-frac)*deltaX - frac*deltaY),y1 + (int)((1-frac)*deltaY + frac*deltaX),x0, y0);
				}
			}
			
			g2d.setTransform(tmpAffine2);

			//draw blocking events
			ArrayList<StartBlocking> blockings;
			if(showAllBlockings_) blockings = EventList.getInstance().getAllBlockingsArrayList();
			else blockings = EventList.getInstance().getCurrentBlockingsArrayList();
			size = blockings.size();
			if(size > 0){
				k=scaledBlockingImage_.getHeight()/2;
				int l = scaledBlockingImage_.getHeight()/2;
				for(i = 0; i < size; ++i){
					blockingImageTransformSource_.setLocation(blockings.get(i).getX()-k/zoom_, blockings.get(i).getY()-l/zoom_);
					transform_.transform(blockingImageTransformSource_, blockingImageTransformDestination_);
					if(blockings.get(i).getPenaltyType_().equals("HUANG_RHCN")){
						g2d.drawImage(scaledSlipperyImage_, (int)Math.round(blockingImageTransformDestination_.getX()), (int)Math.round(blockingImageTransformDestination_.getY()), null, drawArea_);
					}
					else{
						g2d.drawImage(scaledBlockingImage_, (int)Math.round(blockingImageTransformDestination_.getX()), (int)Math.round(blockingImageTransformDestination_.getY()), null, drawArea_);
					}
				}
			}

			g2d.setTransform(tmpAffine_);

			//Traffic lights
			Street[] streets;
			Street street;
			for(i = regionMinX_; i <= regionMaxX_; ++i){
				for(j = regionMinY_; j <= regionMaxY_; ++j){
					streets = regions[i][j].getStreets();
					for(k = 0; k < streets.length; ++k){
						
						street = streets[k];
						
						
						if(street.getEndNodeTrafficLightState() != -1) {
							if(markedJunction_ != null && markedJunction_.getNode().equals(street.getEndNode())){
								g2d.setPaint(Color.orange);
								g2d.fillOval(street.getTrafficLightEndX_()-(Map.LANE_WIDTH+45), street.getTrafficLightEndY_()-(Map.LANE_WIDTH+45),(Map.LANE_WIDTH+45)*2,(Map.LANE_WIDTH+45)*2);		
							}
							if(street.getEndNodeTrafficLightState() == 0) g2d.setPaint(Color.green);
							else if (street.getEndNodeTrafficLightState() == 1 || street.getEndNodeTrafficLightState() == 7) g2d.setPaint(Color.yellow);
							else g2d.setPaint(Color.red);
							
							g2d.fillOval(street.getTrafficLightEndX_() - (Map.LANE_WIDTH), street.getTrafficLightEndY_() - (Map.LANE_WIDTH),(Map.LANE_WIDTH)*2,(Map.LANE_WIDTH)*2);
						}
						if(street.getStartNodeTrafficLightState() != -1){
							if(markedJunction_ != null && markedJunction_.getNode().equals(street.getStartNode())){
								g2d.setPaint(Color.orange);
								g2d.fillOval(street.getTrafficLightStartX_()-(Map.LANE_WIDTH+45), street.getTrafficLightStartY_()-(Map.LANE_WIDTH+45),(Map.LANE_WIDTH+45)*2,(Map.LANE_WIDTH+45)*2);		
							}
							if(street.getStartNodeTrafficLightState() == 0) g2d.setPaint(Color.green);
							else if (street.getStartNodeTrafficLightState() == 1 || street.getStartNodeTrafficLightState() == 7) g2d.setPaint(Color.yellow);
							else g2d.setPaint(Color.red);
							
							g2d.fillOval(street.getTrafficLightStartX_() - (Map.LANE_WIDTH), street.getTrafficLightStartY_() - (Map.LANE_WIDTH),(Map.LANE_WIDTH)*2,(Map.LANE_WIDTH)*2);
						}
					}
				}
			}
		
			
			
			//only if zoom is near enough
			if(zoom_ > 0.0018){
				if(displayVehicleIDs_) g2d.setFont(vehicleIDFont_);
				// draw all visible vehicles
				g2d.setStroke(new BasicStroke(20,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
				g2d.setPaint(Color.black);
				g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
				try{
					for(i = regionMinX_; i <= regionMaxX_; ++i){
						for(j = regionMinY_; j <= regionMaxY_; ++j){
							vehicles = regions[i][j].getVehicleArray();
							size = vehicles.length;
							for(k = 0; k < size; ++k){
								vehicle = vehicles[k];
								g2d.setPaint(vehicle.getColor());
								if(vehicle.isInSlow())g2d.setPaint(Color.pink);
								if((isShowVehicles() || vehicle.isActive()) && vehicle.getX() >= mapMinX_ && vehicle.getX() <= mapMaxX_ && vehicle.getY() >= mapMinY_ && vehicle.getY() <= mapMaxY_){		//only paint when necessary and within paint area
									if(highlightCommunication_){
										if(vehicle.isWiFiEnabled() && (!vehicle.isInMixZone() || Vehicle.getMixZonesFallbackEnabled())){
											g2d.setPaint(Color.blue);
											if(vehicle != markedVehicle_) g2d.drawOval(vehicle.getX()-vehicle.getMaxCommDistance(), vehicle.getY()-vehicle.getMaxCommDistance(),vehicle.getMaxCommDistance()*2,vehicle.getMaxCommDistance()*2);
										} else g2d.setPaint(Color.black);
									}
									g2d.fillOval(vehicle.getX()-VEHICLE_SIZE/2, vehicle.getY()-VEHICLE_SIZE/2,VEHICLE_SIZE,VEHICLE_SIZE);
								}
							}
						}
					}
				} catch (Exception e){}
				if(displayVehicleIDs_){
					g2d.setPaint(new Color(153, 102, 100));
					try{
						for(i = regionMinX_; i <= regionMaxX_; ++i){
							for(j = regionMinY_; j <= regionMaxY_; ++j){
								vehicles = regions[i][j].getVehicleArray();
								size = vehicles.length;
								for(k = 0; k < size; ++k){
									vehicle = vehicles[k];
									if(vehicle.isActive() && vehicle.getX() >= mapMinX_ && vehicle.getX() <= mapMaxX_ && vehicle.getY() >= mapMinY_ && vehicle.getY() <= mapMaxY_){		//only paint when necessary and within paint area
										g2d.drawString(vehicle.getHexID(), vehicle.getX() - vehicleIDFontSize_ *4, vehicle.getY() - VEHICLE_SIZE/2);
									}
								}
							}
						}
					} catch (Exception e){}
				}


				//visualize known vehicles connections
				if(showKnownVehiclesConnections_){
					g2d.setPaint(new Color(153, 102, 100));
					try{
						for(i = regionMinX_; i <= regionMaxX_; ++i){
							for(j = regionMinY_; j <= regionMaxY_; ++j){
								vehicles = regions[i][j].getVehicleArray();
								size = vehicles.length;
								for(k = 0; k < size; ++k){
									vehicle = vehicles[k];
									if(vehicle.isActive() && vehicle.getX() >= mapMinX_ && vehicle.getX() <= mapMaxX_ && vehicle.getY() >= mapMinY_ && vehicle.getY() <= mapMaxY_){		//only paint when necessary and within paint area
										
										
										KnownVehicle[] heads = vehicle.getKnownVehiclesList().getFirstKnownVehicle();
										KnownVehicle next;
										
										//traverse all vehicle which sent beacons
										for(int l = 0; l < heads.length; ++l){
											next = heads[l];								
											while(next != null){
												//Find the attacker data
												g2d.drawLine(vehicle.getX(), vehicle.getY(),next.getX(), next.getY());
												next = next.getNext();
											}
										}
										

									}
								}
							}
						}
					} catch (Exception e){e.printStackTrace();}
				}
				
				// visualize penalty connections
				if(showPenaltyConnections_){
					
					try{
						for(i = regionMinX_; i <= regionMaxX_; ++i){
							for(j = regionMinY_; j <= regionMaxY_; ++j){
								vehicles = regions[i][j].getVehicleArray();
								size = vehicles.length;
								for(k = 0; k < size; ++k){
									vehicle = vehicles[k];
									if(vehicle.isActive() && vehicle.getX() >= mapMinX_ && vehicle.getX() <= mapMaxX_ && vehicle.getY() >= mapMinY_ && vehicle.getY() <= mapMaxY_){		//only paint when necessary and within paint area
										KnownPenalties tmp = vehicle.getKnownPenalties();
										
										if(tmp != null){
											for(int p = 0; p < tmp.getSize(); p++){
												if(tmp.getPenaltyType_()[p].equals("EVA_EMERGENCY_ID")) g2d.setPaint(Color.red);
												else if(tmp.getPenaltyType_()[p].equals("HUANG_PCN")) g2d.setPaint(Color.black);
												else if(tmp.getPenaltyType_()[p].equals("PCN_FORWARD")) g2d.setPaint(Color.gray);
												else if(tmp.getPenaltyType_()[p].equals("HUANG_RHCN")) g2d.setPaint(Color.green);
												else if(tmp.getPenaltyType_()[p].equals("HUANG_EVA_FORWARD")) g2d.setPaint(Color.pink);
												else if(tmp.getPenaltyType_()[p].equals("HUANG_EEBL")) g2d.setPaint(Color.orange);
												
												
												
												if(tmp.getPenaltySourceVehicle_() != null && tmp.getPenaltySourceVehicle_()[p] != null)g2d.drawLine(vehicle.getX(), vehicle.getY(), tmp.getPenaltySourceVehicle_()[p].getX(), tmp.getPenaltySourceVehicle_()[p].getY());
											}
										}
										
									}
								}
							}
						}
					} catch (Exception e){e.printStackTrace();}
				}
				// draw vehicle marked by user
				if(markedVehicle_ != null){
					g2d.setPaint(Color.RED);
					g2d.fillOval(markedVehicle_.getX()-VEHICLE_SIZE/2+35, markedVehicle_.getY()-VEHICLE_SIZE/2+35,VEHICLE_SIZE-70,VEHICLE_SIZE-70);
					if(markedVehicle_.isWiFiEnabled() && (!markedVehicle_.isInMixZone() || Vehicle.getMixZonesFallbackEnabled())) g2d.drawOval(markedVehicle_.getX()-markedVehicle_.getMaxCommDistance(), markedVehicle_.getY()-markedVehicle_.getMaxCommDistance(),markedVehicle_.getMaxCommDistance()*2,markedVehicle_.getMaxCommDistance()*2);
					WayPoint nextDestination = markedVehicle_.getDestinations().peekFirst();
					if(nextDestination != null){
						g2d.drawLine(markedVehicle_.getX(), markedVehicle_.getY(), nextDestination.getX(), nextDestination.getY());
						g2d.fillOval(nextDestination.getX()-VEHICLE_SIZE, nextDestination.getY()-VEHICLE_SIZE,VEHICLE_SIZE*2,VEHICLE_SIZE*2);
						Street[] routestreets = markedVehicle_.getRouteStreets();
						if(routestreets.length > 1){
							g2d.setPaint(Color.blue);
							if(markedVehicle_.getCurDirection()) g2d.drawLine(markedVehicle_.getX(), markedVehicle_.getY(), markedVehicle_.getCurStreet().getEndNode().getX(), markedVehicle_.getCurStreet().getEndNode().getY());
							else g2d.drawLine(markedVehicle_.getX(), markedVehicle_.getY(), markedVehicle_.getCurStreet().getStartNode().getX(), markedVehicle_.getCurStreet().getStartNode().getY());
							for(i = markedVehicle_.getRoutePosition()+1; i < routestreets.length-1; ++i){
								g2d.drawLine(routestreets[i].getStartNode().getX(), routestreets[i].getStartNode().getY(), routestreets[i].getEndNode().getX(), routestreets[i].getEndNode().getY());
							}
							if(!markedVehicle_.getRouteDirections()[routestreets.length-1]){
								g2d.drawLine(nextDestination.getX(), nextDestination.getY(), routestreets[routestreets.length-1].getEndNode().getX(), routestreets[routestreets.length-1].getEndNode().getY());
							} else {
								g2d.drawLine(nextDestination.getX(), nextDestination.getY(), routestreets[routestreets.length-1].getStartNode().getX(), routestreets[routestreets.length-1].getStartNode().getY());
							}
						}
					}
				
					//added to display more than 2 Waypoints for one vehicle (only used in vehicle edit mode, so no need to improve the performance)
					if(isShowVehicles()){
						
						ArrayDeque<WayPoint> tmpDestinations = markedVehicle_.getDestinations();
						
							WayPoint oldDestination = null;
							for(WayPoint destination : tmpDestinations){	
								if(oldDestination != null){
									g2d.setPaint(Color.blue);
									g2d.fillOval(oldDestination.getX()-VEHICLE_SIZE, oldDestination.getY()-VEHICLE_SIZE,VEHICLE_SIZE*2,VEHICLE_SIZE*2);
									g2d.setPaint(Color.red);
									g2d.drawLine(oldDestination.getX(), oldDestination.getY(), destination.getX(), destination.getY());
								}
							
								g2d.setPaint(Color.red);
								g2d.fillOval(destination.getX()-VEHICLE_SIZE, destination.getY()-VEHICLE_SIZE,VEHICLE_SIZE*2,VEHICLE_SIZE*2);
								
								oldDestination = destination;
							}
						}
				}
				if(showAttackers_ || simulationRunning_){
					// draw attacker vehicle
					if(attackerVehicle_ != null){
						g2d.setPaint(Color.LIGHT_GRAY);
						g2d.fillOval(attackerVehicle_.getX()-VEHICLE_SIZE/2+35, attackerVehicle_.getY()-VEHICLE_SIZE/2+35,VEHICLE_SIZE-70,VEHICLE_SIZE-70);
					}
					// draw attacked vehicle
					if(attackedVehicle_ != null){
						g2d.setPaint(Color.GREEN);
						g2d.fillOval(attackedVehicle_.getX()-VEHICLE_SIZE/2+35, attackedVehicle_.getY()-VEHICLE_SIZE/2+35,VEHICLE_SIZE-70,VEHICLE_SIZE-70);
					}
				}
			}

			// draw all nodes coloured to aid editing (only when editing streets and near enough)
			if(highlightAllNodes_ && zoom_ > 0.0012){
				//g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
				Node[] nodes;
				Node node;
				for(i = regionMinX_; i <= regionMaxX_; ++i){
					for(j = regionMinY_; j <= regionMaxY_; ++j){
						nodes = regions[i][j].getNodes();
						for(k = 0; k < nodes.length; ++k){
							node = nodes[k];
							g2d.setPaint(Color.black);
							g2d.fillOval(node.getX()-(Map.LANE_WIDTH+45), node.getY()-(Map.LANE_WIDTH+45),(Map.LANE_WIDTH+45)*2,(Map.LANE_WIDTH+45)*2);						
							g2d.setPaint(Color.pink);
							//marked junction
							if(markedJunction_ != null && markedJunction_.getNode().equals(node))g2d.setPaint(Color.red);
							g2d.fillOval(node.getX()-Map.LANE_WIDTH, node.getY()-Map.LANE_WIDTH,Map.LANE_WIDTH*2,Map.LANE_WIDTH*2);
						}
					}
				}
			}

			
			// apply the fix for substance again for others which draw after us
			if(g2dAffine.getTranslateX() < 0 || g2dAffine.getTranslateY() < 0) tmpAffine_ = g2dAffine;
			else if(g2dAffine.getTranslateX() < maxTranslationX_ || g2dAffine.getTranslateY() < maxTranslationY_) tmpAffine_.setToIdentity();
			else tmpAffine_.setToTranslation(maxTranslationX_, maxTranslationY_);
			g2d.setTransform(tmpAffine_);

			// display time
			g2d.setPaint(Color.black);
			g2d.setFont(timeFont_);
			g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
			g2d.drawString(FORMATTER.format(timePassed_) + " ms", 5 ,10 ); //$NON-NLS-1$

			//draw silent period sign
			if(Vehicle.isSilent_period()){
				// display time
				g2d.setPaint(Color.red);
				g2d.setFont(silentPeriodFont_);
				g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
				g2d.drawString("SILENT PERIOD", 5 ,this.drawHeight_ - 10); //$NON-NLS-1$
			}
			
			if(simulationRunning_ && doPaintInitializedBySimulation_){
				doPaintInitializedBySimulation_ = false;
				try{
					barrierForSimulationMaster_.await();	//signal SimulationMaster that we are ready!
					barrierForSimulationMaster_.reset();
				} catch (Exception e){}
			}
		}
	}

	/**
	 * Creates an image to see the current scale.
	 * 
	 * @param image	the <code>BufferedImage</code> on which rendering should be done
	 */
	public void drawScale(BufferedImage image){
		Graphics2D g2d = image.createGraphics();
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		g2d.setColor(Color.white);
		g2d.fillRect(1, 1, 98, 28);
		g2d.setColor(Color.black);
		try{
			Point2D.Double point1 = new Point2D.Double();
			Point2D.Double point2 = new Point2D.Double();
			transform_.inverseTransform(new Point2D.Double(0,0), point1);
			transform_.inverseTransform(new Point2D.Double(0,70), point2);
			int len = (int)Math.round(point2.getY() - point1.getY());	//calculate length in cm of a 70px sample
			//now find an "appropriate" length which is smaller than 70px and an even multiple of a base distance (for example 7km, 300m, 20m instead of 7.2km, 312m and 24.2m) 
			if(len < 1){
				double len3 = (point2.getY() - point1.getY())*10;
				DecimalFormat df = new DecimalFormat(",###.###"); //$NON-NLS-1$
				g2d.drawString(df.format(len3) + " mm", 30, 15); //$NON-NLS-1$
				g2d.drawLine(15, 20, 85, 20);
				g2d.drawLine(15, 18, 15, 22);
				g2d.drawLine(85, 18, 85, 22);
			} else {
				transform_.transform(new Point2D.Double(0,0), point1);
				if(len > 10000000){	//100km and more
					len = len/10000000;
					transform_.transform(new Point2D.Double(0,len*10000000), point2);					
					g2d.drawString(len + "00 km", 25, 15);					 //$NON-NLS-1$
				} else if(len > 1000000){	//10km
					len = len/1000000;
					transform_.transform(new Point2D.Double(0,len*1000000), point2);
					g2d.drawString(len + "0 km", 30, 15); //$NON-NLS-1$
				} else if(len > 100000){	//1km
					len = len/100000;
					transform_.transform(new Point2D.Double(0,len*100000), point2);
					g2d.drawString(len + " km", 35, 15); //$NON-NLS-1$
				} else if(len > 10000){	//100m
					len = len/10000;
					transform_.transform(new Point2D.Double(0,len*10000), point2);
					g2d.drawString(len + "00 m", 35, 15); //$NON-NLS-1$
				} else if(len > 1000){		//10m
					len = len/1000;
					transform_.transform(new Point2D.Double(0,len*1000), point2);
					g2d.drawString(len + "0 m", 35, 15); //$NON-NLS-1$
				} else if(len > 100){		//1m
					len = len/100;
					transform_.transform(new Point2D.Double(0,len*100), point2);
					g2d.drawString(len + " m", 35, 15); //$NON-NLS-1$
				} else if(len > 10){		//10cm
					len = len/10;
					transform_.transform(new Point2D.Double(0,len*10), point2);
					g2d.drawString(len + "0 cm", 35, 15); //$NON-NLS-1$
				} else {		//1cm
					transform_.transform(new Point2D.Double(0,len), point2);
					g2d.drawString(len + " cm", 35, 15); //$NON-NLS-1$
				}
				len = (int)Math.round(point2.getY() - point1.getY());
				g2d.drawLine(15, 20, 15+len, 20);
				g2d.drawLine(15, 18, 15, 22);
				g2d.drawLine(15+len, 18, 15+len, 22);
			}
		} catch (Exception e){

		}
	}

	/**
	 * This function renders all non-moving objects on a supplied image.
	 * All streets and other static objects are rendered into the cached Image in this function.
	 * This function should only be called on
	 * <ul>
	 * <li>size changes of the drawing area</li>
	 * <li>zooming or panning</li>
	 * <li>map loading</li>
	 * <li>new map objects appearing</li>
	 * </ul>
	 * 
	 * <br><br>
	 * 
	 * Note: The main performance factor here are the calls to <code>draw()</code> or <code>drawLine()</code>. This is a factor determined by the underlying graphics
	 * subsytem and there's not much space for lot of improvement.
	 * Multithreading approaches are not possible here: The graphics subsystem is just singlethreaded (blocks concurrent draw-Calls
	 * even if they are done on different BufferedImages) and so there's no performance benefit.<br>
	 * Optimizing anything concerned with iterating through the regions is not worth it because the cpu time for this is barely measurable after
	 * using <code>ArrayLists</code> instead of <code>Iterators</code> (iterators create lots of object creations and calls)<br>
	 * 
	 * @param image the <code>BufferedImage</code> on which rendering should be done
	 */
	public synchronized void drawStaticObjects(BufferedImage image){
		Graphics2D g2d = image.createGraphics();
		Region[][] regions = map_.getRegions();
		
		if(regions != null && map_.getReadyState()){
			int savedRegionMinX = regionMinX_;		//copy as we might change it beneath
			int savedRegionMaxX = regionMaxX_;
			int savedRegionMinY = regionMinY_;
			int savedRegionMaxY = regionMaxY_;

			//try to reuse image elements if possible (only possible when panning); this reduces rendering time by up to 50%!
			if(!lastOverdrawn_ && (((panCountX_ == 1 || panCountX_ == -1) && panCountY_ == 0) || ((panCountY_ == 1 || panCountY_ == -1) && panCountX_ == 0))){	// only ONE change in ONE direction has happened since last render
				//draw old image onto new one with an offset in x or y direction
				g2d.drawImage(image, (int)Math.round(panCountX_*drawWidth_/2), (int)Math.round(panCountY_*drawHeight_/2), null, drawArea_);

				//calculate and set clipping for new drawing operation
				int clipX=0, clipY=0, clipWidth, clipHeight;
				if(panCountX_ != 0) clipWidth = drawWidth_/2;
				else clipWidth = drawWidth_;
				if(panCountY_ != 0) clipHeight = drawHeight_/2;
				else clipHeight = drawHeight_;
				if(panCountX_ < 0)	clipX = drawWidth_/2;
				else if(panCountY_ < 0) clipY = drawHeight_/2;
				g2d.setClip(clipX,clipY,clipWidth,clipHeight);

				//Update coordinates which need to be drawn
				int newMinX=mapMinX_, newMaxX=mapMaxX_,newMinY=mapMinY_, newMaxY=mapMaxY_;
				if(panCountX_ > 0) newMaxX = mapMaxX_ - ((mapMaxX_-mapMinX_)/2);
				else if(panCountX_ < 0) newMinX = mapMinX_ + ((mapMaxX_-mapMinX_)/2);
				if(panCountY_ > 0) newMaxY = mapMaxY_ - ((mapMaxY_-mapMinY_)/2);
				else if(panCountY_ < 0) newMinY = mapMinY_ + ((mapMaxY_-mapMinY_)/2);

				//Update regions which need to be drawn
				Region tmpRegion = map_.getRegionOfPoint(newMinX, newMinY);
				savedRegionMinX = tmpRegion.getX();
				savedRegionMinY = tmpRegion.getY();			
				tmpRegion = map_.getRegionOfPoint(newMaxX, newMaxY);
				savedRegionMaxX = tmpRegion.getX();
				savedRegionMaxY = tmpRegion.getY();
			}
			panCountX_ = 0;
			panCountY_ = 0;

			// fill background
			g2d.setColor(new Color(230,230,230));
			g2d.fillRect(0,0,drawWidth_,drawHeight_);	

			//Check if we're near enough to use antialiasing (quite costly if there are too many streets)
			boolean antialias = true;
			if(zoom_ > 0.0018) g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
			else {
				antialias = false;
				g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
			}

			// set transformation (for automatic scaling and panning)
			g2d.setTransform(transform_);

			// paint outline of map
			if(!antialias) g2d.setPaint(Color.gray);
			else g2d.setPaint(Color.black);
			g2d.drawRect(0, 0, map_.getMapWidth(), map_.getMapHeight());

			
			// prepare variables
			int i, j, k, totalLanes, lastEntry = -1;
			Street street;
			Region streetRegion;
			Node startNode, endNode;
			Street[] streets;
			TreeMap<Integer,Path2D.Float> layers = new TreeMap<Integer,Path2D.Float>();		//a sorted map so that we always get the same result (basically sorted by color)
			Path2D.Float currentPath = null;
			
			// display mix zones, moved to drawStaticObject() to improve the performance
			if(showMixZones_ || (hideMixZones_ && Vehicle.getCommunicationEnabled() && Vehicle.getMixZonesEnabled())){
				g2d.setPaint(Color.LIGHT_GRAY);
				for(i = regionMinX_; i <= regionMaxX_; ++i){
					for(j = regionMinY_; j <= regionMaxY_; ++j){
						Node[] nodes = regions[i][j].getMixZoneNodes();
						for(int l = 0; l < nodes.length; ++l){
							g2d.setPaint(Color.LIGHT_GRAY);
							g2d.fillOval(nodes[l].getX()-nodes[l].getMixZoneRadius(), nodes[l].getY()-nodes[l].getMixZoneRadius(),nodes[l].getMixZoneRadius()*2,nodes[l].getMixZoneRadius()*2);
							g2d.setPaint(Color.RED);
							if(nodes[l].getEncryptedRSU_() != null){
								g2d.fillOval(nodes[l].getEncryptedRSU_().getX() - 1500, nodes[l].getEncryptedRSU_().getY() - 1500,3000,3000);
							}
						}
					}
				}
			}
			

			//draw location Information silent period
			if(locationInformationSilentPeriod_ != null){
				showMixZones_ = true;
				String[] data;
				for(String location:locationInformationSilentPeriod_){
					data = location.split(":");
					if(data[0].equals("true"))g2d.setPaint(Color.RED);
					else if(data[0].equals("slow"))g2d.setPaint(Color.BLUE);
					else g2d.setPaint(Color.GREEN);
					g2d.drawOval(Integer.parseInt(data[1])-2500, Integer.parseInt(data[2])-2500,5000,5000);
				}
			}
			
	
			//draw location Information mix
			if(locationInformationMix_ != null){
				String[] data;
				int size = 0;
				for(String location:locationInformationMix_){
					data = location.split(":");
					if(Float.parseFloat(data[4]) != 0) size = (int)(1000 + (Float.parseFloat(data[4])/mixZoneAmount)*100000);
					else size = 0;
					g2d.setPaint(new Color((int)(Float.parseFloat(data[2])*200) + 55,(int)(Float.parseFloat(data[3])*200)+55,0));
					g2d.fillOval(Integer.parseInt(data[0])-size, Integer.parseInt(data[1])-size,size*2,size*2);
				}
			}
			
			// Create and draw a black background so that streets have black boundaries. Only makes sense when antialias is on because otherwise it looks ugly and is really slow(!)
			if(antialias){
				for(i = savedRegionMinX; i <= savedRegionMaxX; ++i){
					for(j = savedRegionMinY; j <= savedRegionMaxY; ++j){
						streets = regions[i][j].getStreets();
						for(k = 0; k < streets.length; ++k){
							street = streets[k];
							streetRegion = street.getMainRegion();
							if(streetRegion==regions[i][j] || streetRegion.getX()<savedRegionMinX || streetRegion.getX()>savedRegionMaxX || streetRegion.getY()<savedRegionMinY || streetRegion.getY()>savedRegionMaxY){	// only paint it if necessary (to prevent painting it multiple times)
								if(street.isOneway()) totalLanes = street.getLanesCount();
								else totalLanes = 2 * street.getLanesCount();
								if(lastEntry != totalLanes){
									lastEntry = totalLanes;
									currentPath = layers.get(totalLanes);
									if(currentPath == null){
										currentPath = new Path2D.Float (Path2D.WIND_NON_ZERO, 5000);
										layers.put(lastEntry, currentPath);
									}
								}
								startNode = street.getStartNode();		//saves some function calls
								endNode = street.getEndNode();								
								currentPath.moveTo(startNode.getX(), startNode.getY());
								currentPath.lineTo(endNode.getX(), endNode.getY());
							}
						}
					}
				}
				Iterator<Integer> iterator = layers.keySet().iterator();
				int key;
				g2d.setPaint(Color.black);
				g2d.setStroke(lineBackground_);
				//iterate through all layers and draw them
				while(iterator.hasNext()){
					key = iterator.next();
					if(key == 2) g2d.setStroke(lineBackground_);
					else g2d.setStroke(new BasicStroke(Map.LANE_WIDTH*key+90,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER));
					g2d.draw(layers.get(key));
				}
			}
			//now create the layers. This second iteration step (only second one if antialiasing is on) is actually faster than mixing it into the first one!)
			layers.clear();		// to store the layers of the map separated by color
			lastEntry = 1;
			boolean detailedView;
			if(zoom_ > 0.0001) detailedView = true;	// only show fast speed streets if zoom is very far away
			else detailedView = false;
			for(i = savedRegionMinX; i <= savedRegionMaxX; ++i){
				for(j = savedRegionMinY; j <= savedRegionMaxY; ++j){
					streets = regions[i][j].getStreets();
					for(k = 0; k < streets.length; ++k){
						street = streets[k];
						streetRegion = street.getMainRegion();
						if(streetRegion==regions[i][j] || streetRegion.getX()<savedRegionMinX || streetRegion.getX()>savedRegionMaxX || streetRegion.getY()<savedRegionMinY || streetRegion.getY()>savedRegionMaxY){	// only paint it if necessary (to prevent painting it multiple times)
							if(detailedView || street.getSpeed() > 2000){
								startNode = street.getStartNode();		//saves some function calls
								endNode = street.getEndNode();
								if(antialias){
									if(street.isOneway()) totalLanes = street.getDisplayColor().getRGB()*100 - street.getLanesCount();
									else totalLanes = street.getDisplayColor().getRGB()*100 - (2*street.getLanesCount());
								} else totalLanes = street.getDisplayColor().getRGB();							
								if(lastEntry != totalLanes){
									lastEntry = totalLanes;
									currentPath = layers.get(totalLanes);
									if(currentPath == null){	//create the layer if doesn't exist yet
										currentPath = new Path2D.Float (Path2D.WIND_NON_ZERO, 5000);
										layers.put(lastEntry, currentPath);
									}
								}
								currentPath.moveTo(startNode.getX(), startNode.getY());
								currentPath.lineTo(endNode.getX(), endNode.getY());
							}
						}
					}
				}
			}
			
			Color drawColor;	
			Iterator<Integer> coloriterator = layers.keySet().iterator();
			int key, lanes;
			//iterate through all layers and draw them
			while(coloriterator.hasNext()){
				key = coloriterator.next();
				if(antialias){
					drawColor = new Color(key/100);
					lanes = -key+(key/100*100);
					if(lanes == 2) g2d.setStroke(lineMain_);
					else g2d.setStroke(new BasicStroke(Map.LANE_WIDTH*lanes,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER));
				} else {
					g2d.setStroke(lineMain_);
					drawColor = new Color(Math.max((int)(((key >> 16) & 0xFF) *0.8), 0), Math.max((int)(((key >> 8) & 0xFF)*0.8), 0), Math.max((int)(((key >> 0) & 0xFF)*0.8), 0));	//simulate colors like if they were with antialias (darker)!
				}
				g2d.setPaint(drawColor);				
				g2d.draw(layers.get(key));
			}
			// If the zoom is near enough, do a correction to display bridges more accurately (though not perfect as intersection calculation
			// seems to be a little bit problematic in some rare cases)
			if(antialias){
				int l, correction;
				int[] start = new int[2];
				int[] end = new int[2];
				boolean correctStart, correctEnd;
				ArrayList<Point2D.Double> paintArrayList;
				//1. step: paint streets again which are bridges
				for(i = savedRegionMinX; i <= savedRegionMaxX; ++i){
					for(j = savedRegionMinY; j <= savedRegionMaxY; ++j){
						streets = regions[i][j].getStreets();
						for(k = 0; k < streets.length; ++k){
							street = streets[k];
							streetRegion = street.getMainRegion();
							if(streetRegion==regions[i][j] || streetRegion.getX()<savedRegionMinX || streetRegion.getX()>savedRegionMaxX || streetRegion.getY()<savedRegionMinY || streetRegion.getY()>savedRegionMaxY){	// only paint it if necessary (to prevent painting it multiple times)
								if(street.getBridgePaintLines() != null || street.getBridgePaintPolygons() != null){
									if(street.isOneway()) totalLanes = street.getLanesCount();
									else totalLanes = 2 * street.getLanesCount();
									correction = totalLanes * Map.LANE_WIDTH*2;
									correctStart = false;
									correctEnd = false;
									start[0] = street.getStartNode().getX();
									start[1] = street.getStartNode().getY();
									end[0] = street.getEndNode().getX();
									end[1] = street.getEndNode().getY();
									if(street.getStartNode().getCrossingStreetsCount() > 2) correctStart = true;
									if(street.getEndNode().getCrossingStreetsCount() > 2) correctEnd = true;
									if(street.getLength() > correction*2) {
										MapHelper.calculateResizedLine(start, end, correction, correctStart, correctEnd);
										//paint the now shorter line completely
										g2d.setStroke(new BasicStroke(Map.LANE_WIDTH*totalLanes+90,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
										g2d.setColor(Color.black);
										g2d.drawLine(start[0], start[1], end[0], end[1]);
										g2d.setStroke(new BasicStroke(Map.LANE_WIDTH*totalLanes,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER));
										g2d.setColor(street.getDisplayColor());
										g2d.drawLine(start[0], start[1], end[0], end[1]);
									}									
								}
							}
						}
					}
				}
				//Step 2: Paint the overlapping parts again
				for(i = savedRegionMinX; i <= savedRegionMaxX; ++i){
					for(j = savedRegionMinY; j <= savedRegionMaxY; ++j){
						streets = regions[i][j].getStreets();
						for(k = 0; k < streets.length; ++k){
							street = streets[k];
							streetRegion = street.getMainRegion();
							if(streetRegion==regions[i][j] || streetRegion.getX()<savedRegionMinX || streetRegion.getX()>savedRegionMaxX || streetRegion.getY()<savedRegionMinY || streetRegion.getY()>savedRegionMaxY){	// only paint it if necessary (to prevent painting it multiple times)
								//Step 2.1: Paint intersections which consist of 4 intersection points
								paintArrayList = street.getBridgePaintPolygons();
								if(paintArrayList != null){									
									g2d.setColor(street.getDisplayColor());
									for(l = 3; l < paintArrayList.size(); l= l+4){
										int[] xPoints = new int[4];
										int[] yPoints = new int[4];
										xPoints[0]=(int)Math.round(paintArrayList.get(l-3).x);
										yPoints[0]=(int)Math.round(paintArrayList.get(l-3).y);
										xPoints[1]=(int)Math.round(paintArrayList.get(l-2).x);
										yPoints[1]=(int)Math.round(paintArrayList.get(l-2).y);
										xPoints[2]=(int)Math.round(paintArrayList.get(l).x);
										yPoints[2]=(int)Math.round(paintArrayList.get(l).y);
										xPoints[3]=(int)Math.round(paintArrayList.get(l-1).x);
										yPoints[3]=(int)Math.round(paintArrayList.get(l-1).y);
										start[0] = xPoints[0];
										start[1] = yPoints[0];
										end[0] = xPoints[3];
										end[1] = yPoints[3];
										MapHelper.calculateResizedLine(start, end, 50, true, true);
										xPoints[0] = start[0];
										yPoints[0] = start[1];
										xPoints[3] = end[0];
										yPoints[3] = end[1];
										start[0] = xPoints[1];
										start[1] = yPoints[1];
										end[0] = xPoints[2];
										end[1] = yPoints[2];
										MapHelper.calculateResizedLine(start, end, 50, true, true);
										xPoints[1] = start[0];
										yPoints[1] = start[1];
										xPoints[2] = end[0];
										yPoints[2] = end[1];
										g2d.fillPolygon(xPoints, yPoints, 4);			
									}
									g2d.setStroke(new BasicStroke(45,BasicStroke.CAP_SQUARE,BasicStroke.JOIN_MITER));
									g2d.setColor(Color.black);
									for(l = 1; l < paintArrayList.size(); l= l+2){
										int x1=(int)Math.round(paintArrayList.get(l-1).x);
										int y1=(int)Math.round(paintArrayList.get(l-1).y);
										int x2=(int)Math.round(paintArrayList.get(l).x);
										int y2=(int)Math.round(paintArrayList.get(l).y);
										g2d.drawLine(x1, y1, x2, y2); 
									}								
								}
								//Step 2.2: Paint intersections which consist of 2 intersection points
								paintArrayList = street.getBridgePaintLines();
								if(paintArrayList != null){									
									g2d.setStroke(new BasicStroke(45,BasicStroke.CAP_SQUARE,BasicStroke.JOIN_MITER));
									g2d.setColor(Color.black);
									for(l = 1; l < paintArrayList.size(); l= l+2){
										int x1=(int)Math.round(paintArrayList.get(l-1).x);
										int y1=(int)Math.round(paintArrayList.get(l-1).y);
										int x2=(int)Math.round(paintArrayList.get(l).x);
										int y2=(int)Math.round(paintArrayList.get(l).y);
										g2d.drawLine(x1, y1, x2, y2); 
									}								
								}
							}
						}
					}
				}
			}
			// display RSUs
			if(isShowRSUs() || simulationRunning_){
				g2d.setStroke(new BasicStroke(20,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
				g2d.setPaint(rsuColor);
				for(i = regionMinX_; i <= regionMaxX_; ++i){
					for(j = regionMinY_; j <= regionMaxY_; ++j){
						RSU[] rsus = regions[i][j].getRSUs();
						for(int l = 0; l < rsus.length; ++l){	
							if(highlightCommunication_)g2d.drawOval(rsus[l].getX()-rsus[l].getWifiRadius(), rsus[l].getY()-rsus[l].getWifiRadius(),rsus[l].getWifiRadius()*2,rsus[l].getWifiRadius()*2);
							g2d.fillOval(rsus[l].getX() - 250, rsus[l].getY() - 250,500,500);
						}
					}
				}
			}
			
			// display mix entrances. Only active if debugMode is on
			
			if(debugMode){
				g2d.setPaint(Color.PINK);
				g2d.setFont(new Font("SansSerif", Font.PLAIN, 400));
				g2d.setStroke(new BasicStroke(20,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
				
				for(i = regionMinX_; i <= regionMaxX_; ++i){
					for(j = regionMinY_; j <= regionMaxY_; ++j){
						ArrayList<String> xxx = regions[i][j].xxx;
						ArrayList<String> yyy = regions[i][j].yyy;
						ArrayList<String> nnn = regions[i][j].nnn;
						if(xxx != null){
			
						for(k = 0; k < xxx.size(); k++){
							g2d.drawOval(Integer.parseInt(xxx.get(k))-300, Integer.parseInt(yyy.get(k))-300,300*2,300*2);
							g2d.drawString(nnn.get(k), Integer.parseInt(xxx.get(k)), Integer.parseInt(yyy.get(k)));
						}
						}
					}
				}
			}
	
				
			// display ARSUs
			if(isShowAttackers() || simulationRunning_){
				g2d.setStroke(new BasicStroke(20,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
				g2d.setPaint(arsuColor);
				AttackRSU[] tempARSUList= Vehicle.getArsuList();

				if(tempARSUList.length > 0){
				    for(int l = 0; l < tempARSUList.length;l++) {
						if(highlightCommunication_)g2d.drawOval(tempARSUList[l].getX()-tempARSUList[l].getWifiRadius(), tempARSUList[l].getY()-tempARSUList[l].getWifiRadius(),tempARSUList[l].getWifiRadius()*2,tempARSUList[l].getWifiRadius()*2);
						g2d.fillOval(tempARSUList[l].getX() - 250, tempARSUList[l].getY() - 250,500,500);    	
				      }
				}
			}
			
			
			// display EventSpots
			g2d.setStroke(new BasicStroke(300,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
			EventSpot tmpEventSpot = EventSpotList.getInstance().getHead_();
			while(tmpEventSpot != null){
				g2d.setPaint(tmpEventSpot.getEventSpotColor_());
				g2d.drawOval(tmpEventSpot.getX_()-tmpEventSpot.getRadius_(), tmpEventSpot.getY_()-tmpEventSpot.getRadius_(),tmpEventSpot.getRadius_()*2,tmpEventSpot.getRadius_()*2);
				g2d.fillOval(tmpEventSpot.getX_() - 500, tmpEventSpot.getY_() - 500,1000,1000);  
				
				tmpEventSpot = tmpEventSpot.getNext_();
			}
			
			// display Centroids
			g2d.setStroke(new BasicStroke(300,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
			g2d.setPaint(Color.red);

			if(centroidsX != null && centroidsY != null){
				for(int r = 0; r < centroidsX.size(); r++){
					g2d.drawOval(centroidsX.get(r).intValue()-500, centroidsY.get(r).intValue()-500,1000,1000);
				}

			}
			
			// display clusters
			g2d.setStroke(new BasicStroke(300,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
			
			for(Cluster cluster:clusters){
				g2d.setColor(cluster.getClusterColor());
				g2d.setFont(clusterIDFont_);
				g2d.drawString(cluster.getClusterID_(), cluster.getMinX_() - 500, cluster.getMinY_() - 500);
				g2d.drawRect(cluster.getMinX_(), cluster.getMinY_(), (cluster.getMaxX_() - cluster.getMinX_()), (cluster.getMaxY_()-cluster.getMinY_()));
			}
			
			//display specific cluster
			
			g2d.setStroke(new BasicStroke(25,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
			if(showAllClusters){
				for(Cluster cluster:clusters){
					g2d.setColor(cluster.getClusterColor());
					for(int e = 0; e < cluster.getxCoords_().size(); e++){
						
						g2d.drawOval(cluster.getxCoords_().get(e)-500, cluster.getyCoords_().get(e)-500,1000,1000);
					}
				}
			}
			else if(displayCluster_ != null){
				g2d.setColor(displayCluster_.getClusterColor());
				for(int e = 0; e < displayCluster_.getxCoords_().size(); e++){
					
					g2d.drawOval(displayCluster_.getxCoords_().get(e)-500, displayCluster_.getyCoords_().get(e)-500,1000,1000);
				}
			}
			
			if(showAmenities){
				// display amenities
				g2d.setStroke(new BasicStroke(20,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
				
				for(Node amenity:Map.getInstance().getAmenityList()){
					g2d.setPaint(amenity.getNodeColor());
					g2d.drawOval(amenity.getX()-200000, amenity.getY()-200000,400000,400000);
					g2d.fillOval(amenity.getX() - 500, amenity.getY() - 500,1000,1000);  
				}
			}
			

			
			if(grid != null){
				int maxMinusmin = maxGridValue - minGridValue;
				int basicStroke = 100;
				double probability = 0;
				int strokeSize = 0;
				
				for(int a = 0; a < grid.length; a++){
					for(int b = 0; b < grid[0].length; b++){
						if(grid[a][b] > 0) 	g2d.setPaint(new Color(255,0,0));
						else g2d.setPaint(new Color(0,255,0));
						
					//	g2d.setPaint(new Color((int)(255 * (((double)grid[a][b] - minEVA)) / maxMinusmin), 255 - (int)(255 * (((double)grid[a][b] - minEVA)) / maxMinusmin), 0));
						//g2d.drawRect(a*10000, b*10000, 10000 - 400, 10000 - 400);
						probability = ((double)grid[a][b] - minGridValue) / maxMinusmin;
						strokeSize = (int) (basicStroke + probability*(gridSize_ - basicStroke));
						g2d.setStroke(new BasicStroke(strokeSize,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
						g2d.drawRect(a*gridSize_ - (gridSize_ - strokeSize)/2, b*gridSize_ - (gridSize_ - strokeSize)/2, gridSize_ - strokeSize, gridSize_ - strokeSize);
				
					}
				}
			}

		} else {
			// just fill background
			g2d.setColor(new Color(230,230,230));
			g2d.fillRect(0,0,drawWidth_,drawHeight_);
		}
		g2d.dispose(); // should be disposed to aid garbage collector
	}

	/**
	 * Pans the viewable area.
	 * 
	 * @param direction <code>u</code> to pan up, <code>d</code> to pan down, <code>l</code> to pan left, <code>r</code> to pan right
	 */
	public synchronized void pan(char direction){
		if(direction == 'u'){
			panCountY_ += 1;
			middleY_ -= (drawHeight_/2 / zoom_); 
		}
		else if(direction == 'd'){
			panCountY_ -= 1;
			middleY_ += (drawHeight_/2 / zoom_);
		}
		else if(direction == 'l') {
			panCountX_ += 1;
			middleX_ -= (drawWidth_/2 / zoom_); 
		} else {
			panCountX_ -= 1;
			middleX_ += (drawWidth_/2 / zoom_);
		}
		updateParams();
	}

	/**
	 * Pans the viewable area.
	 * 
	 * @param x the value for how far to pan in x direction (in map scale!)
	 * @param y the value for how far to pan in y direction (in map scale!)
	 */
	public synchronized void pan(double x, double y){
		middleX_ += x;
		middleY_ += y;
		updateParams();
	}

	/**
	 * Sets a new zooming factor.
	 * 
	 * @param zoom the new zooming factor
	 */
	public synchronized void setMapZoom(double zoom){
		if(zoom > 0.0000045 && zoom < 0.5){		//limit zooming by mousewheel into range from 100km to 1m
			zoom_ = zoom;
			updateParams();
			
			// update the image for blockings. The code is taken from 
			// http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html (link last visited: 21.20.2008)
			// and the comments there. getScaledImage() is a little bit better quality but is deprecated according to this as it's slow.
			int size = (int)Math.round(1000 * zoom_);
			if(size < 4) size = 4;
			size = size - (size %2);

			Graphics2D g2;
			BufferedImage tmp;
			BufferedImage tmp2;
			GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
            int oldSize, curSize = blockingImage_.getWidth();
            if(size < blockingImage_.getWidth()/2){ // new image is smaller than the original
            	// add some blur
            	float weight = 1.0f/9.0f;
            	float[] elements = new float[9]; 
        	    for (int i = 0; i < 9; i++) {
        	        elements[i] = weight;
        	    }
        	    Kernel blurKernel = new Kernel(3, 3, elements);
        	    scaledBlockingImage_ = gc.createCompatibleImage(blockingImage_.getWidth(), blockingImage_.getHeight(), Transparency.TRANSLUCENT);
        	    new ConvolveOp(blurKernel).filter(blockingImage_, scaledBlockingImage_);
        	    
        	    scaledSlipperyImage_ = gc.createCompatibleImage(slipperyImage_.getWidth(), slipperyImage_.getHeight(), Transparency.TRANSLUCENT);
        	    new ConvolveOp(blurKernel).filter(slipperyImage_, scaledSlipperyImage_);
        	    
        	    // do a multi-step resizing to get higher quality
    	        do {
    	        	oldSize = curSize;
    	            if (curSize > size) {
    	            	curSize /= 2;
    	                if (curSize < size) curSize = size;
    	            }	            

    	            tmp = gc.createCompatibleImage(curSize, curSize, Transparency.TRANSLUCENT);
    	            g2 = tmp.createGraphics();
    	            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    	            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    	            g2.drawImage(scaledBlockingImage_, 0, 0, curSize, curSize ,0, 0, oldSize, oldSize, null);
    	            g2.dispose();

    	            scaledBlockingImage_ = tmp;
    	            
    	            tmp2 = gc.createCompatibleImage(curSize, curSize, Transparency.TRANSLUCENT);
    	            g2 = tmp2.createGraphics();
    	            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    	            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    	            g2.drawImage(scaledSlipperyImage_, 0, 0, curSize, curSize ,0, 0, oldSize, oldSize, null);
    	            g2.dispose();

    	            scaledSlipperyImage_ = tmp2;
    	            
    	        } while (curSize > size);	
            } else {		// new image is larger than the original
            	scaledBlockingImage_ = gc.createCompatibleImage(size, size, Transparency.TRANSLUCENT);
            	g2 = scaledBlockingImage_.createGraphics();
            	g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
	            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            	g2.drawImage(blockingImage_, 0, 0, size, size, null);
            	g2.dispose();
            	
            	scaledSlipperyImage_ = gc.createCompatibleImage(size, size, Transparency.TRANSLUCENT);
            	g2 = scaledSlipperyImage_.createGraphics();
            	g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
	            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            	g2.drawImage(slipperyImage_, 0, 0, size, size, null);
            	g2.dispose();
            }
		}		
	}

	/**
	 * Updates various internal parameters after changes through panning or zooming.<br>
	 * The regions which shall be drawn are calculated here. They are a little bit larger than
	 * normally necessary in order to correctly display communication distance and mix zones. Otherwise,
	 * the distance of a vehicle, which is a little bit outside the currently viewable area wouldn't
	 * be drawn. This all induces a little bit of an unnecessary overdraw to the static objects but this 
	 * should not matter a lot.
	 */
	public synchronized void updateParams(){
		if(map_.getReadyState() == true){
			transform_.setToScale(zoom_, zoom_);		// set the zoom
			transform_.translate(-middleX_ + (drawWidth_ / (zoom_*2)), -middleY_ + (drawHeight_/(zoom_*2)));		// pan and correct center of screen

			lastOverdrawn_ = currentOverdrawn_;
			currentOverdrawn_ = false;
			
			//add a value on top to be able to correctly display communication distance and mix radius
			int addValue = Vehicle.getMaximumCommunicationDistance();
			if(Vehicle.getMixZoneRadius() > addValue) addValue = Vehicle.getMixZoneRadius();
			
			// Minimum x coordinate to be considered for rendering
			long tmp = (long) StrictMath.floor(middleX_ - (drawWidth_ / (zoom_*2))) - addValue;
			if (tmp < 0){
				mapMinX_ = 0;
				currentOverdrawn_ = true;
			} else if(tmp < Integer.MAX_VALUE) mapMinX_ = (int) tmp;
			else mapMinX_ = Integer.MAX_VALUE;

			// Maximum x coordinate to be considered for rendering
			tmp = (long) StrictMath.ceil(middleX_ + (drawWidth_ / (zoom_*2))) + addValue;
			if (tmp < 0) mapMaxX_ = 0;
			else {
				if(tmp > map_.getMapWidth()) currentOverdrawn_ = true;
				if(tmp < Integer.MAX_VALUE) mapMaxX_ = (int) tmp;
				else mapMaxX_ = Integer.MAX_VALUE;
			}
			

			// Minimum y coordinate to be considered for rendering
			tmp = (long) StrictMath.floor(middleY_ - (drawHeight_ / (zoom_*2))) - addValue;
			if (tmp < 0){
				mapMinY_ = 0;		// Map stores only positive coordinates
				currentOverdrawn_ = true;
			} else if(tmp < Integer.MAX_VALUE) mapMinY_ = (int) tmp;
			else mapMinY_ = Integer.MAX_VALUE;

			// Maximum y coordinate to be considered for rendering
			tmp = (long) StrictMath.ceil(middleY_ + (drawHeight_ / (zoom_*2))) + addValue;
			if (tmp < 0) mapMaxY_ = 0;
			else {
				if(tmp > map_.getMapHeight()) currentOverdrawn_ = true;
				if(tmp < Integer.MAX_VALUE) mapMaxY_ = (int) tmp;
				else mapMaxY_ = Integer.MAX_VALUE;
			}
			

			// Get the regions to be considered for rendering
			Region tmpregion = map_.getRegionOfPoint(mapMinX_, mapMinY_);
			regionMinX_ = tmpregion.getX();
			regionMinY_ = tmpregion.getY();

			tmpregion = map_.getRegionOfPoint(mapMaxX_, mapMaxY_);
			regionMaxX_ = tmpregion.getX();
			regionMaxY_ = tmpregion.getY();
		}
	}
	
	/**
	 * Schedules an update of the {@link DrawingArea}. Note that depending on if a simulation is running or not, the update
	 * might be performed at a later time!
	 * 
	 * @param fullRender 	<code>true</code> if a full update including all static objects should be done, else <code>false</code>
	 * @param forceRenderNow <code>true</code> to force an immediate update regardless of consistency considerations (should only be used by the {@link vanetsim.simulation.SimulationMaster})
	 */
	public void ReRender(boolean fullRender, boolean forceRenderNow){
		if(fullRender) scheduleFullRender_ = true;
		if (drawArea_ != null){
			if(!simulationRunning_ || forceRenderNow){
				if(scheduleFullRender_){
					scheduleFullRender_ = false;
					drawArea_.prepareBufferedImages();					
				}
				doPaintInitializedBySimulation_ = true;
				drawArea_.repaint();
			}
		}
	}

	/**
	 * A method to move the camera to a specific region
	 */
	
	public void moveCamera(double middleX, double middleY, double zoom){
		middleX_ = middleX;
		middleY_ = middleY;
		
		setMapZoom(zoom);
	}

	/**
	 * Gets the x coordinate of the middle of the current view.
	 * 
	 * @return the x coordinate in map scale
	 */
	public double getMiddleX(){
		return middleX_;
	}

	/**
	 * Gets the y coordinate of the middle of the current view.
	 * 
	 * @return the y coordinate in map scale
	 */
	public double getMiddleY(){
		return middleY_;
	}
	
	/**
	 * Gets the current zooming factor.
	 * 
	 * @return the current zooming factor
	 */
	public double getMapZoom(){
		return zoom_;
	}

	/**
	 * Gets the time passed since simulation start.
	 * 
	 * @return the time passed in milliseconds
	 */
	public int getTimePassed(){
		return timePassed_;
	}


	/**
	 * Gets the currently active coordinate transformation.
	 * 
	 * @return the transform
	 */
	public AffineTransform getTransform(){
		return transform_;
	}

	/**
	 * Notify if the simulation is running or not.
	 * 
	 * @param running	<code>true</code> if a simulation is currently running, <code>false</code> if it's suspended
	 */
	public void notifySimulationRunning(boolean running){
		simulationRunning_ = running;
	}
	
	/**
	 * Sets a new marked street.
	 * 
	 * @param markedStreet the street to mark
	 */
	public synchronized void setMarkedStreet(Street markedStreet){
		markedStreet_ = markedStreet;	
	}

	/**
	 * Sets a new marked vehicle.
	 * 
	 * @param markedVehicle the vehicle to mark
	 */
	public synchronized void setMarkedVehicle(Vehicle markedVehicle){
		markedVehicle_ = markedVehicle;
	}

	/**
	 * Gets a marked vehicle.
	 *
	 */
	public synchronized Vehicle getMarkedVehicle(){
		return markedVehicle_;
	}
	
	/**
	 * Sets a new attacker vehicle.
	 * 
	 * @param attackerVehicle the attacker vehicle
	 */
	public synchronized void setAttackerVehicle(Vehicle attackerVehicle){
		attackerVehicle_ = attackerVehicle;
	}

	/**
	 * Gets the attacker vehicle.
	 *
	 */
	public synchronized Vehicle getAttackerVehicle(){
		return attackerVehicle_;
	}
	
	/**
	 * Sets the coordinates of the center of the viewable area.
	 * 
	 * @param x	the new x coordinate for the center of the viewable area
	 * @param y	the new y coordinate for the center of the viewable area
	 */
	public synchronized void setMiddle(int x, int y){
		middleX_ = x;
		middleY_ = y;		
		updateParams();
	}
	
	/**
	 * Sets the {@link DrawingArea} this Renderer is associated with.
	 * 
	 * @param drawArea 	the area on which this Renderer draws
	 */
	public void setDrawArea(DrawingArea drawArea){
		drawArea_ = drawArea;
	}

	/**
	 * Set the height of the {@link DrawingArea}.
	 * 
	 * @param drawHeight the new height
	 */
	public void setDrawHeight(int drawHeight) {
		drawHeight_ = drawHeight;
	}

	/**
	 * Set the width of the {@link DrawingArea}.
	 * 
	 * @param drawWidth the new width
	 */
	public void setDrawWidth(int drawWidth) {
		drawWidth_ = drawWidth;
	}
	
	/**
	 * Sets the time passed since simulation start.
	 * 
	 * @param timePassed the new time in milliseconds
	 */
	public void setTimePassed(int timePassed){
		timePassed_ = timePassed;
	}

	/**
	 * Sets the barrier for synchronization with the SimulationMaster.
	 * 
	 * @param barrier the barrier to use
	 */
	public void setBarrierForSimulationMaster(CyclicBarrier barrier){
		barrierForSimulationMaster_ = barrier;
	}

	/**
	 * If all nodes shall be highlighted.
	 * 
	 * @param highlightNodes <code>true</code> if you want to highlight nodes, else <code>false</code>
	 */
	public void setHighlightNodes(boolean highlightNodes){
		highlightAllNodes_ = highlightNodes;
	}
	
	/**
	 * If circles shall be displayed to show communication distance.
	 * 
	 * @param highlightCommunication <code>true</code> if you want to show the circles, else <code>false</code>
	 */
	public void setHighlightCommunication(boolean highlightCommunication){
		highlightCommunication_ = highlightCommunication;
	}
	
	/**
	 * If filled circles shall be displayed to hide the mix zones.
	 * 
	 * @param hideMixZones <code>true</code> if you want to show the circles, else <code>false</code>
	 */
	public void setHideMixZones(boolean hideMixZones){
		hideMixZones_ = hideMixZones;
	}
	
	/**
	 * If the IDs of the vehicle shall be drawn on the map.
	 * 
	 * @param displayVehicleIDs <code>true</code> if you want to show the IDs, else <code>false</code>
	 */
	public void setDisplayVehicleIDs(boolean displayVehicleIDs){
		displayVehicleIDs_ = displayVehicleIDs;
	}

	/**
	 * If you want to show all blockings.
	 * 
	 * @param showAllBlockings <code>true</code> if you want to show all blockings, else <code>false</code>
	 */
	public void setShowAllBlockings(boolean showAllBlockings){
		showAllBlockings_ = showAllBlockings;
	}
	
	/**
	 * If you want to display the monitored beacon zone.
	 * 
	 * @param showBeaconMonitorZone	<code>true</code> if you want to display the monitored beacon zones, else <code>false</code>
	 */
	public void setShowBeaconMonitorZone(boolean showBeaconMonitorZone){
		showBeaconMonitorZone_ = showBeaconMonitorZone;
	}
	
	/**
	 * Sets the values for the monitored beacon zone. A rectangular bounding box within the specified coordinates
	 * is monitored if {@link #setShowBeaconMonitorZone(boolean)} is set to <code>true</code>.
	 * 
	 * @param beaconMonitorMinX	the minimum x coordinate
	 * @param beaconMonitorMaxX	the maximum x coordinate
	 * @param beaconMonitorMinY	the minimum y coordinate
	 * @param beaconMonitorMaxY	the maximum y coordinate
	 */
	public void setMonitoredBeaconZoneVariables(int beaconMonitorMinX, int beaconMonitorMaxX, int beaconMonitorMinY, int beaconMonitorMaxY){
		beaconMonitorMinX_ = beaconMonitorMinX;
		beaconMonitorMaxX_ = beaconMonitorMaxX;
		beaconMonitorMinY_ = beaconMonitorMinY;
		beaconMonitorMaxY_ = beaconMonitorMaxY;
	}

	/**
	 * If you want to show all vehicles.
	 * 
	 * @param showVehicles <code>true</code> if you want to show all vehicles, else <code>false</code>
	 */
	public void setShowVehicles(boolean showVehicles) {
		showVehicles_ = showVehicles;
	}

	/**
	 * Gets if vehicles are displayed.
	 * 
	 * @return <code>true</code> if vehicles are displayed
	 */
	public boolean isShowVehicles() {
		return showVehicles_;
	}
	
	/**
	 * If you want to show all mix zones.
	 * 
	 * @param showMixZones <code>true</code> if you want to show all mix zones, else <code>false</code>
	 */
	public void setShowMixZones(boolean showMixZones) {
		showMixZones_ = showMixZones;
	}

	/**
	 * Gets if mix zones are displayed
	 * 
	 * @return true if mix zones are displayed
	 */
	public boolean isShowMixZones() {
		return showMixZones_;
	}

	/**
	 * If you want to add mix zones to all street corners automatically.
	 * 
	 * @param autoAddMixZones <code>true</code> if you want to add mix zones to all street corners automatically else <code>false</code>
	 */
	public void setAutoAddMixZones(boolean autoAddMixZones) {
		autoAddMixZones_ = autoAddMixZones;
	}

	/**
	 * Gets if mix zones are added automatically on each street corner
	 * 
	 * @return true if mix zones are added automatically
	 */
	public boolean isAutoAddMixZones() {
		return autoAddMixZones_;
	}

	/**
	 * If you want to show all RSUs.
	 * 
	 * @param showRSUs <code>true</code> if you want to show all RSUs, else <code>false</code>
	 */
	public void setShowRSUs(boolean showRSUs) {
		showRSUs_ = showRSUs;
	}

	/**
	 * Gets if RSUs are displayed
	 * 
	 * @return true if RSUs are displayed
	 */
	public boolean isShowRSUs() {
		return showRSUs_;
	}
	
	public Vehicle getAttackedVehicle() {
		return attackedVehicle_;
	}

	public void setAttackedVehicle(Vehicle attackedVehicle_) {
		this.attackedVehicle_ = attackedVehicle_;
	}

	public boolean isShowAttackers() {
		return showAttackers_;
	}

	public void setShowAttackers(boolean showAttackers_) {
		this.showAttackers_ = showAttackers_;
	}

	public boolean isConsoleStart() {
		return consoleStart;
	}

	public void setConsoleStart(boolean consoleStart) {
		this.consoleStart = consoleStart;
	}

	/**
	 * @param markedJunction_ the markedJunction_ to set
	 */
	public void setMarkedJunction_(Junction markedJunction_) {
		this.markedJunction_ = markedJunction_;
	}

	/**
	 * @return the markedJunction_
	 */
	public Junction getMarkedJunction_() {
		return markedJunction_;
	}

	public ArrayList<String> getLocationInformationMix() {
		return locationInformationMix_;
	}

	public void setLocationInformationMix(ArrayList<String> locationInformation) {
		this.locationInformationMix_ = locationInformation;
	}

	public ArrayList<String> getLocationInformationSilentPeriod_() {
		return locationInformationSilentPeriod_;
	}

	public void setLocationInformationSilentPeriod_(
			ArrayList<String> locationInformationSilentPeriod_) {
		this.locationInformationSilentPeriod_ = locationInformationSilentPeriod_;
	}

	public int getMixZoneAmount() {
		return mixZoneAmount;
	}

	public void setMixZoneAmount(int mixZoneAmount) {
		this.mixZoneAmount = mixZoneAmount;
	}

	/**
	 * @return the grid
	 */
	public int[][] getGrid() {
		return grid;
	}

	/**
	 * @param grid the grid to set
	 */
	public void setGrid(int[][] grid) {
		this.grid = grid;
	}

	/**
	 * @return the minGridValue
	 */
	public int getMinGridValue() {
		return minGridValue;
	}

	/**
	 * @param minGridValue the minGridValue to set
	 */
	public void setMinGridValue(int minGridValue) {
		this.minGridValue = minGridValue;
	}

	/**
	 * @return the maxGridValue
	 */
	public int getMaxGridValue() {
		return maxGridValue;
	}

	/**
	 * @param maxGridValue the maxGridValue to set
	 */
	public void setMaxGridValue(int maxGridValue) {
		this.maxGridValue = maxGridValue;
	}

	/**
	 * @return the gridSize_
	 */
	public int getGridSize_() {
		return gridSize_;
	}

	/**
	 * @param gridSize_ the gridSize_ to set
	 */
	public void setGridSize_(int gridSize_) {
		this.gridSize_ = gridSize_;
	}

	public boolean isShowPenaltyConnections_() {
		return showPenaltyConnections_;
	}

	public void setShowPenaltyConnections_(boolean showPenaltyConnections_) {
		this.showPenaltyConnections_ = showPenaltyConnections_;
	}

	public boolean isShowKnownVehiclesConnections_() {
		return showKnownVehiclesConnections_;
	}

	public void setShowKnownVehiclesConnections_(
			boolean showKnownVehiclesConnections_) {
		this.showKnownVehiclesConnections_ = showKnownVehiclesConnections_;
	}

	public ArrayList<Double> getCentroidsX() {
		return centroidsX;
	}

	public void setCentroidsX(ArrayList<Double> centroidsX) {
		this.centroidsX = centroidsX;
	}

	public ArrayList<Double> getCentroidsY() {
		return centroidsY;
	}

	public void setCentroidsY(ArrayList<Double> centroidsY) {
		this.centroidsY = centroidsY;
	}

	public ArrayList<Cluster> getClusters() {
		return clusters;
	}

	public Cluster getDisplayCluster_() {
		return displayCluster_;
	}

	public void setDisplayCluster_(Cluster displayCluster_) {
		this.displayCluster_ = displayCluster_;
	}

	public void setShowAllClusters(boolean showAllClusters) {
		this.showAllClusters = showAllClusters;
	}


} public final class ButtonCreator{
	/**
	 * Convenience method to get a JButton with an image on it.
	 * 
	 * @param imageName		the filename of the image relative to the <code>vanetsim/images</code> directory
	 * @param command		a command string which can be used to track events in <code>actionPerformed()</code>
	 * @param altString		an alternative string to display on the button when the original image can't be loaded and as a tooltip
	 * @param listener		an <code>ActionListener</code> which performs actions on button clicks
	 * 
	 * @return the <code>JButton</code> created
	 */
	public static JButton getJButton(String imageName, String command, String altString, ActionListener listener){
		JButton button;
		
		if(imageName.equals("")){ //$NON-NLS-1$
			button = new JButton(altString);
			button.setPreferredSize(new Dimension(42, 42));
		} else {
			URL url = ClassLoader.getSystemResource("vanetsim/images/" + imageName); //$NON-NLS-1$
			if (url != null){
				button = new JButton(new ImageIcon(url));
			} else {
				button = new JButton(altString);
				button.setPreferredSize(new Dimension(42, 42));
				ErrorLog.log(Messages.getString("ButtonCreator.imageNotFound") + imageName, 5, ButtonCreator.class.getName(), "getJButton", null); //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
		button.setFocusPainted(false);
		button.setToolTipText(altString);
		button.setActionCommand(command);
		button.addActionListener(listener);
		return button;
	}
	
	public static JButton getJButton(String imageName, String command, String altString, boolean resize, ActionListener listener){
		JButton button;
		
		if(imageName.equals("")){ //$NON-NLS-1$
			button = new JButton(altString);
			button.setPreferredSize(new Dimension(42, 42));
		} else {
			URL url = ClassLoader.getSystemResource("vanetsim/images/" + imageName); //$NON-NLS-1$
			if (url != null){
				ImageIcon tmpImg = new ImageIcon(url);
				button = new JButton(tmpImg);
				button.setPreferredSize(new Dimension(tmpImg.getIconWidth(), tmpImg.getIconHeight()));
				button.setMaximumSize(new Dimension(tmpImg.getIconWidth(), tmpImg.getIconHeight()));
			} else {
				button = new JButton(altString);
				button.setPreferredSize(new Dimension(42, 42));
				ErrorLog.log(Messages.getString("ButtonCreator.imageNotFound") + imageName, 5, ButtonCreator.class.getName(), "getJButton", null); //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
		button.setFocusPainted(false);
		button.setToolTipText(altString);
		button.setActionCommand(command);
		button.addActionListener(listener);
		return button;
	}
} public class WekaHelper {
	public static ArrayList<Double> readWekaCentroids(String filePath, String axis){
		ArrayList<Double> returnArray = new ArrayList<Double>();
		
		String[] tmpArray = null;
		
		try{
			// Open the file that is the first 
			// command line parameter
			FileInputStream fstream = new FileInputStream(filePath);
			// Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			//Read file line By line
			
			while ((strLine = br.readLine()) != null) {
				if(strLine.length() > 0){
					System.out.println(strLine.substring(0,1) +  ":::"  + axis);
					if(strLine.substring(0,1).equals(axis)){
						tmpArray = strLine.split(" ");
						
						for(int i = 0; i < tmpArray.length; i++) if(!tmpArray[i].equals("") && !tmpArray[i].equals(" ") && !tmpArray[i].equals(axis)) returnArray.add(Double.parseDouble(tmpArray[i]));
						
						break;
					}
				}

				
			}
			  
			//Close the input stream
			in.close();
		}catch (Exception e){//Catch exception if any
			System.err.println("Error: " + e.getMessage());
		}
		 
		return returnArray;
	}
	
} public final class Messages {
	
	/** The <code>ResourceBundle</code> where to get the messages from. */
	private static ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle("vanetsim.localization.messages");	//$NON-NLS-1$

	/**
	 * Instantiates a new instance.
	 */
	private Messages() {
	}

	/**
	 * Returns a localized string.
	 * 
	 * @param key	key to find the string in the language file
	 * 
	 * @return the localized string
	 */
	public static String getString(String key) {
		try {
			return RESOURCE_BUNDLE.getString(key);
		} catch (MissingResourceException e) {
			return '!' + key + '!';
		}
	}
	
	/**
	 * Returns a localized string.
	 * 
	 * @param key		key to find the string in the language file
	 * @param locale	locale which sets the language only for 1 time
	 * 
	 * @return the localized string
	 */
	public static String getString(String key, String locale) {
		try {
			Locale savedLocale = RESOURCE_BUNDLE.getLocale();
			RESOURCE_BUNDLE = ResourceBundle.getBundle("vanetsim.localization.messages", new Locale(locale));
			String returnMessage =  RESOURCE_BUNDLE.getString(key);
			RESOURCE_BUNDLE = ResourceBundle.getBundle("vanetsim.localization.messages", savedLocale);
			return returnMessage;
		} catch (MissingResourceException e) {
			return '!' + key + '!';
		}
	}
	
	/**
	 * changes the bundle. If this method is not called the standard language of the computer OS is chosen
	 */
	public static void setLanguage(String locale){
		RESOURCE_BUNDLE = ResourceBundle.getBundle("vanetsim.localization.messages", new Locale(locale));
	}
}
 public final class Map{

	/** The only instance of this class (singleton). */
	private static final Map INSTANCE = new Map();
	
	/** The width of a single lane (3m). Used in various other places in this program! */
	public static final int LANE_WIDTH = 300;

	/** The width of the map in cm. */
	private int width_ = 0;

	/** The height of the map in cm. */
	private int height_ = 0;

	/** The width of a region in cm. */
	private int regionWidth_ = 0;

	/** The height of a region in cm. */
	private int regionHeight_ = 0;

	/** The amount of regions in x direction. */
	private int regionCountX_ = 0;

	/** The amount of regions in y direction. */
	private int regionCountY_ = 0;

	/** An array holding all {@link Region}s. */
	private Region[][] regions_ = null;

	/** A flag to signal if loading is ready. While loading is in progress, simulation and rendering is not possible. */
	private boolean ready_ = true;	

	/** A list for amenitys */
	private ArrayList<Node> amenityList_ = new ArrayList<Node>();
	
	/** the map name */
	private String mapName_ = "";
	/**
	 * Empty, private constructor in order to disable instancing.
	 */
	private Map() {
	}	

	/**
	 * Gets the single instance of this map.
	 * 
	 * @return single instance of this map
	 */
	public static Map getInstance(){
		return INSTANCE;
	}

	/**
	 * Initializes a new map.
	 * 
	 * @param width			the width
	 * @param height		the height
	 * @param regionWidth	the width of a region
	 * @param regionHeight	the height of a region
	 */
	public void initNewMap(int width, int height, int regionWidth, int regionHeight){
		int i, j;
		if(ready_ == true){
			ready_ = false;
			//cleanup!
			if(!Renderer.getInstance().isConsoleStart()){
				Scenario.getInstance().initNewScenario();	//stops the simulation thread so we don't need to do it here
				Scenario.getInstance().setReadyState(true);
			}
			
			A_Star_LookupTableFactory.clear();
			Node.resetNodeID();
			width_ = width;
			height_ = height;
			regionWidth_ = regionWidth;
			regionHeight_ = regionHeight;

			Renderer.getInstance().setMarkedStreet(null);
			Renderer.getInstance().setMarkedVehicle(null);
			Renderer.getInstance().setAttackerVehicle(null);
			Renderer.getInstance().setAttackedVehicle(null);

			
			if(!Renderer.getInstance().isConsoleStart())MouseClickManager.getInstance().cleanMarkings();

			// create the regions on the map
			regionCountX_ = width_/regionWidth_;
			if(width_%regionWidth_ > 0) ++regionCountX_;
			regionCountY_ = height_/regionHeight_;
			if(height_%regionHeight_ > 0) ++regionCountY_;
			regions_ = new Region[regionCountX_][regionCountY_];
			int upperboundary = 0, leftboundary = 0;
			for(i = 0; i < regionCountX_; ++i){
				for(j = 0; j < regionCountY_; ++j){
					regions_[i][j] = new Region(i,j, leftboundary, leftboundary + regionWidth_-1, upperboundary, upperboundary + regionHeight_-1);
					upperboundary += regionHeight_;
				}
				leftboundary += regionWidth_;
				upperboundary = 0;
			}
			Vehicle.setRegions(regions_);
			EventSpot.setRegions_(regions_);
			RSU.setRegions(regions_);
		} else {
			ErrorLog.log(Messages.getString("Map.mapLocked"), 7, getClass().getName(), "initNewMap", null); //$NON-NLS-1$ //$NON-NLS-2$
		}
	}

	/**
	 * This function needs to be called to signal that the loading process of the map has finished.
	 */
	public void signalMapLoaded(){
		// optimize the ArrayLists in the regions in order to free wasted memory
		for(int i = 0; i < regionCountX_; ++i){
			for(int j = 0; j < regionCountY_; ++j){
				regions_[i][j].calculateJunctions();
			}
		}
		ready_ = true;
		if(!Renderer.getInstance().isConsoleStart()){
			Renderer.getInstance().setMiddle(width_/2, height_/2);
			Renderer.getInstance().setMapZoom(Math.exp(5/100.0)/1000);
			Renderer.getInstance().ReRender(true, false);

		}

		//start a thread which calculates bridges in background so that loading is faster (it's just eyecandy and not necessary otherwise ;))
		Runnable job = new Runnable() {
			public void run(){
				for(int i = 0; i < regionCountX_; ++i){
					for(int j = 0; j < regionCountY_; ++j){
						regions_[i][j].checkStreetsForBridges();
					}
				}
			}
		};
		Thread t = new Thread(job);
		t.setPriority(Thread.MIN_PRIORITY);
		t.start();
	}

	/**
	 * Load a map.
	 * 
	 * @param file	the file to load
	 * @param zip	<code>true</code> if the file given is zipped, else <code>false</code>
	 */
	public void load(File file, boolean zip){
		try{
			if(!Renderer.getInstance().isConsoleStart())VanetSimStart.setProgressBar(true);
			String childtype, setting, streetName, streetType, trafficSignalException, amenity ="";
			int x = 0, y = 0, maxSpeed, isOneway, lanes, newMapWidth, newMapHeight, newRegionWidth, newRegionHeight;
			Color displayColor;
			boolean xSet, ySet, trafficSignal;
			Node startNode, endNode;
			SMInputCursor childCrsr, nodeCrsr, settingsCrsr, streetCrsr, streetCrsr2, amenityCrsr, amenityCrsr2;
			XMLInputFactory factory = XMLInputFactory.newInstance();

			mapName_ = file.getName();
			
			ErrorLog.log(Messages.getString("Map.loadingMap") + file.getName(), 3, getClass().getName(), "loadMap", null); //$NON-NLS-1$ //$NON-NLS-2$
			// configure some factory options...
			factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
			factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
			factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
			factory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);

			InputStream filestream;
			if(zip){
				filestream = new ZipInputStream(new FileInputStream(file));
				((ZipInputStream) filestream).getNextEntry();
			} else filestream = new FileInputStream(file);
			XMLStreamReader sr = factory.createXMLStreamReader(filestream);	
			SMInputCursor rootCrsr = SMInputFactory.rootElementCursor(sr);
			rootCrsr.getNext();
			if(rootCrsr.getLocalName().toLowerCase().equals("map")){ //$NON-NLS-1$
				childCrsr = rootCrsr.childElementCursor();
				childCrsr.getNext();
				if(childCrsr.getLocalName().toLowerCase().equals("settings")){		// The settings section must be present! //$NON-NLS-1$
					newMapWidth = 0;
					newMapHeight = 0;
					newRegionWidth = 0;
					newRegionHeight = 0;
					settingsCrsr = childCrsr.childElementCursor();
					while (settingsCrsr.getNext() != null){		//parse general settings (size of regions and map)
						setting = settingsCrsr.getLocalName().toLowerCase();
						if(setting.equals("map_height")){ //$NON-NLS-1$
							try{
								newMapHeight = Integer.parseInt(settingsCrsr.collectDescendantText(false));
							} catch (Exception e) {}
						} else if(setting.equals("map_width")){ //$NON-NLS-1$
							try{
								newMapWidth = Integer.parseInt(settingsCrsr.collectDescendantText(false));
							} catch (Exception e) {}
						} else if(setting.equals("region_height")){ //$NON-NLS-1$
							try{
								newRegionWidth = Integer.parseInt(settingsCrsr.collectDescendantText(false));
							} catch (Exception e) {}
						} else if(setting.equals("region_width")){ //$NON-NLS-1$
							try{
								newRegionHeight = Integer.parseInt(settingsCrsr.collectDescendantText(false));
							} catch (Exception e) {}
						}
					}
					if(newMapWidth > 0 && newMapHeight > 0 && newRegionWidth > 0 && newRegionHeight > 0){		// only continue if settings were all found
						if(!Renderer.getInstance().isConsoleStart())VanetSimStart.setProgressBar(false);
						CyclicBarrier barrier = new CyclicBarrier(2);
						if(!Renderer.getInstance().isConsoleStart()){
							
							new MapSizeDialog(newMapWidth, newMapHeight, newRegionWidth, newRegionHeight, barrier);	//initialize new map
							//try {
						//		barrier.await();
						//	} catch (Exception e) {}
						}
						else Map.getInstance().initNewMap(newMapWidth, newMapHeight, newRegionWidth, newRegionHeight);
						int addX = (width_ - newMapWidth)/2;
						int addY = (height_ - newMapHeight)/2;
						if(!Renderer.getInstance().isConsoleStart())VanetSimStart.setProgressBar(true);
						while (childCrsr.getNext() != null){
							if(childCrsr.getLocalName().toLowerCase().equals("streets")){ //$NON-NLS-1$
								streetCrsr = childCrsr.childElementCursor();
								while (streetCrsr.getNext() != null){
									if(streetCrsr.getLocalName().toLowerCase().equals("street")){ //$NON-NLS-1$
										streetName = ""; //$NON-NLS-1$
										startNode = null;
										endNode = null;
										trafficSignal = false;
										streetType = "unkown";
										lanes = 0;
										isOneway = 0;
										maxSpeed = 0;
										trafficSignalException = "";
										displayColor = null;
										streetCrsr2 = streetCrsr.childElementCursor();
										while (streetCrsr2.getNext() != null){
											childtype = streetCrsr2.getLocalName().toLowerCase();
											if(childtype.equals("name")){ //$NON-NLS-1$
												streetName = streetCrsr2.collectDescendantText(true);
											} else if(childtype.equals("startnode") || childtype.equals("endnode")){ //$NON-NLS-1$ //$NON-NLS-2$
												xSet = false;
												ySet = false;
												nodeCrsr = streetCrsr2.childElementCursor();
												while (nodeCrsr.getNext() != null){
													if(nodeCrsr.getLocalName().toLowerCase().equals("x")){ //$NON-NLS-1$
														try{
															x = Integer.parseInt(nodeCrsr.collectDescendantText(false)) + addX;
															xSet = true;
														} catch (Exception e) {}														
													} else if(nodeCrsr.getLocalName().toLowerCase().equals("y")){ //$NON-NLS-1$
														try{
															y = Integer.parseInt(nodeCrsr.collectDescendantText(false)) + addY;
															ySet = true;
														} catch (Exception e) {}
													} else if(nodeCrsr.getLocalName().toLowerCase().equals("trafficsignal")){ //$NON-NLS-1$
														if(nodeCrsr.collectDescendantText(false).toLowerCase().equals("true")) trafficSignal = true;
														else trafficSignal = false;
													} else if(nodeCrsr.getLocalName().toLowerCase().equals("trafficsignalexceptions")){ //$NON-NLS-1$
														trafficSignalException = nodeCrsr.collectDescendantText(true);
													}	
												}
												if(xSet && ySet){
													if(childtype.equals("startnode")){
														startNode = new Node(x, y, trafficSignal); //$NON-NLS-1$
														if(!trafficSignalException.equals(""))startNode.addSignalExceptionsOfString(trafficSignalException);
													}
													else {
														endNode = new Node(x, y, trafficSignal);
														if(!trafficSignalException.equals(""))endNode.addSignalExceptionsOfString(trafficSignalException);
													}
												}
											} else if(childtype.equals("oneway")){ //$NON-NLS-1$
												if(streetCrsr2.collectDescendantText(false).toLowerCase().equals("true")) isOneway = 1; //$NON-NLS-1$
												else isOneway = 0;
											} else if(childtype.equals("streettype")){ //$NON-NLS-1$
												streetType = streetCrsr2.collectDescendantText(true);
											} else if(childtype.equals("lanes")){ //$NON-NLS-1$
												try{
													lanes = Integer.parseInt(streetCrsr2.collectDescendantText(false));
												} catch (Exception e) {}
											} else if(childtype.equals("speed")){ //$NON-NLS-1$
												try{
													maxSpeed = Integer.parseInt(streetCrsr2.collectDescendantText(false));
												} catch (Exception e) {}
											} else if(childtype.equals("color")){ //$NON-NLS-1$
												try{
													displayColor = new Color(Integer.parseInt(streetCrsr2.collectDescendantText(false)));
												} catch (Exception e) {}
											} else ErrorLog.log(Messages.getString("Map.unknownElement"), 3, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$
										}
										if(maxSpeed > 0 && startNode != null && endNode != null && displayColor != null && !streetName.equals("")){ //$NON-NLS-1$
											startNode = addNode(startNode);
											endNode = addNode(endNode);
											addStreet(new Street(streetName, startNode, endNode, streetType, isOneway, lanes, displayColor, getRegionOfPoint(startNode.getX(), startNode.getY()), maxSpeed));
										}
									} else ErrorLog.log(Messages.getString("Map.unknownElementOnlyStreet"), 3, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$
								}
							} //else ErrorLog.log(Messages.getString("Map.unknownElementOnlyStreets"), 3, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$
							else if(childCrsr.getLocalName().toLowerCase().equals("amenities")){ //$NON-NLS-1$
								amenityCrsr = childCrsr.childElementCursor();
								while (amenityCrsr.getNext() != null){
									if(amenityCrsr.getLocalName().toLowerCase().equals("amenity")){ //$NON-NLS-1$
										x = 0; //$NON-NLS-1$
										y = 0;
										amenity = "";
										amenityCrsr2 = amenityCrsr.childElementCursor();
										while (amenityCrsr2.getNext() != null){
											childtype = amenityCrsr2.getLocalName().toLowerCase();
											if(childtype.equals("x")){ //$NON-NLS-1$
												x = Integer.parseInt(amenityCrsr2.collectDescendantText(false));
											}
											else if(childtype.equals("y")){ //$NON-NLS-1$
												y = Integer.parseInt(amenityCrsr2.collectDescendantText(false));
											} 
											else if(childtype.equals("amenity")){ //$NON-NLS-1$
												amenity = amenityCrsr2.collectDescendantText(true);
											} 
											else ErrorLog.log(Messages.getString("Map.unknownElement"), 3, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$
										}
									} else ErrorLog.log(Messages.getString("Map.unknownElementOnlyAmenity"), 3, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$
									addAmenityNode(new Node(x,y,false), amenity);
								}
							} else ErrorLog.log(Messages.getString("Map.unknownElementOnlyAmenitiesOrStreets"), 3, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$
						}
					} else ErrorLog.log(Messages.getString("Map.settingsIncomplete"), 7, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$
				} else ErrorLog.log(Messages.getString("Map.settingsMissing"), 7, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$
			} else ErrorLog.log(Messages.getString("Map.wrongRoot"), 7, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$

			sr.close();
			filestream.close();
		} catch (Exception e) {ErrorLog.log(Messages.getString("Map.errorLoading"), 7, getClass().getName(), "load", e);} //$NON-NLS-1$ //$NON-NLS-2$
		if(!Renderer.getInstance().isConsoleStart())VanetSimStart.setProgressBar(false);
		signalMapLoaded();
		ErrorLog.log(Messages.getString("Map.loadingFinished"), 3, getClass().getName(), "load", null); //$NON-NLS-1$ //$NON-NLS-2$	
	}


	/**
	 * Save the map.
	 * 
	 * @param file	the file in which to save
	 * @param zip	if <code>true</code>, file is saved in a compressed zip file (extension .zip is added to <code>file</code>!). If <code>false</code>, no compression is made.
	 */
	public void save(File file, boolean zip){
		try{
			if(!Renderer.getInstance().isConsoleStart())VanetSimStart.setProgressBar(true);
			ErrorLog.log(Messages.getString("Map.savingMap") + file.getName(), 3, getClass().getName(), "save", null); //$NON-NLS-1$ //$NON-NLS-2$
			int i, j, k;
			Street[] streetsArray;
			Street street;
			SMOutputElement level1, level2;

			OutputStream filestream;
			if(zip){
				filestream = new ZipOutputStream(new FileOutputStream(file + ".zip")); //$NON-NLS-1$
				((ZipOutputStream) filestream).putNextEntry(new ZipEntry(file.getName()));
			} else filestream = new FileOutputStream(file);
			XMLStreamWriter xw = XMLOutputFactory.newInstance().createXMLStreamWriter(filestream);
			SMOutputDocument doc = SMOutputFactory.createOutputDocument(xw);
			doc.setIndentation("\n\t\t\t\t\t\t\t\t", 2, 1); ;  //$NON-NLS-1$
			doc.addComment("Generated on " + new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(new Date())); //$NON-NLS-1$ //$NON-NLS-2$
			doc.addComment("This file may contain data from the OpenStreetMap project which is licensed under the Creative Commons Attribution-ShareAlike 2.0 license."); //$NON-NLS-1$

			SMOutputElement root = doc.addElement("Map");			 //$NON-NLS-1$
			level1 = root.addElement("Settings"); //$NON-NLS-1$
			level2 = level1.addElement("Map_height"); //$NON-NLS-1$
			level2.addValue(height_);
			level2 = level1.addElement("Map_width"); //$NON-NLS-1$
			level2.addValue(width_);
			level2 = level1.addElement("Region_height"); //$NON-NLS-1$
			level2.addValue(regionHeight_);
			level2 = level1.addElement("Region_width"); //$NON-NLS-1$
			level2.addValue(regionWidth_);
			SMOutputElement streets = root.addElement("Streets");			 //$NON-NLS-1$

			for(i = 0; i < regionCountX_; ++i){
				for(j = 0; j < regionCountY_; ++j){
					streetsArray = regions_[i][j].getStreets();
					for(k = 0; k < streetsArray.length; ++k){
						street = streetsArray[k];
						if(street.getMainRegion() == regions_[i][j]){	//as a street can be in multiple regions only output it in the "main" region!
							level1 = streets.addElement("Street"); //$NON-NLS-1$
							level1.addElement("Name").addCharacters(street.getName()); //$NON-NLS-1$							
							level2 = level1.addElement("StartNode"); //$NON-NLS-1$
							level2.addElement("x").addValue(street.getStartNode().getX());; //$NON-NLS-1$
							level2.addElement("y").addValue(street.getStartNode().getY()); //$NON-NLS-1$
							if(street.getStartNode().isHasTrafficSignal_()) {
								level2.addElement("trafficSignal").addCharacters("true");
								if(street.getStartNode().hasNonDefaultSettings()) level2.addElement("TrafficSignalExceptions").addCharacters(street.getStartNode().getSignalExceptionsInString());
							}
							else level2.addElement("trafficSignal").addCharacters("false");
							level2 = level1.addElement("EndNode"); //$NON-NLS-1$
							level2.addElement("x").addValue(street.getEndNode().getX()); //$NON-NLS-1$
							level2.addElement("y").addValue(street.getEndNode().getY()); //$NON-NLS-1$
							if(street.getEndNode().isHasTrafficSignal_()) {
								level2.addElement("trafficSignal").addCharacters("true");
								if(street.getEndNode().hasNonDefaultSettings()) level2.addElement("TrafficSignalExceptions").addCharacters(street.getEndNode().getSignalExceptionsInString());
							}
							else level2.addElement("trafficSignal").addCharacters("false");
							if(street.isOneway()) level1.addElement("Oneway").addCharacters("true"); //$NON-NLS-1$ //$NON-NLS-2$
							else level1.addElement("Oneway").addCharacters("false"); //$NON-NLS-1$ //$NON-NLS-2$
							level1.addElement("StreetType").addCharacters(street.getStreetType_()); //$NON-NLS-1$
							level1.addElement("Lanes").addValue(street.getLanesCount()); //$NON-NLS-1$
							level1.addElement("Speed").addValue(street.getSpeed()); //$NON-NLS-1$
							level1.addElement("Color").addValue(street.getDisplayColor().getRGB()); //$NON-NLS-1$
						}
					}
				}
			}
			
			SMOutputElement amenities = root.addElement("Amenities"); //$NON-NLS-1$

			//save the list of amenities (e.g. schools, hospitals...)
			for(Node n: amenityList_){
				level1 = amenities.addElement("Amenity"); //$NON-NLS-1$
				level1.addElement("x").addValue(n.getX()); //$NON-NLS-1$
				level1.addElement("y").addValue(n.getY()); //$NON-NLS-1$
				level1.addElement("amenity").addCharacters(n.getAmenity_());
			}
			
			doc.closeRoot();
			xw.close();
			filestream.close();
		}catch (Exception e) {ErrorLog.log(Messages.getString("Map.errorSavingMap") , 6, getClass().getName(), "save", e);} //$NON-NLS-1$ //$NON-NLS-2$
		if(!Renderer.getInstance().isConsoleStart())VanetSimStart.setProgressBar(false);
	}

	/**
	 * Add a new node to the correct region. A node can only be in one region.
	 * 
	 * @param node	the node to add
	 * 
	 * @return the added node (might be different if already existing!)
	 */
	public Node addNode(Node node){
		int regionX = node.getX()/regionWidth_;	//implicit rounding (=floor)because of integer values!
		int regionY = node.getY()/regionHeight_;

		// to prevent "array out of bounds" when node is outside of map
		if (regionX >= regionCountX_) regionX = regionCountX_ - 1;
		else if(regionX < 0) regionX = 0;
		if (regionY >= regionCountY_) regionY = regionCountY_ - 1;
		else if (regionY < 0) regionY = 0;

		node.setRegion(regions_[regionX][regionY]);
		return regions_[regionX][regionY].addNode(node, true);
	}

	/**
	 * Delete a node from it's region.
	 * 
	 * @param node	the node to remove
	 */
	public void delNode(Node node){
		node.getRegion().delNode(node);
	}

	/**
	 * Add a new Road-Side-Unit to the correct region. A RSU can only be in one region.
	 * 
	 * @param rsu	the RSU to add
	 * 
	 */
	public void addRSU(RSU rsu){
		int regionX = rsu.getX()/regionWidth_;	//implicit rounding (=floor)because of integer values!
		int regionY = rsu.getY()/regionHeight_;

		// to prevent "array out of bounds" when RSU is outside of map
		if (regionX >= regionCountX_) regionX = regionCountX_ - 1;
		else if(regionX < 0) regionX = 0;
		if (regionY >= regionCountY_) regionY = regionCountY_ - 1;
		else if (regionY < 0) regionY = 0;

		rsu.setRegion(regions_[regionX][regionY]);
		regions_[regionX][regionY].addRSU(rsu);
	}

	/**
	 * Delete a Road-Side-Unit from it's region.
	 * 
	 * @param x  the x coordinate of the rsu
	 * @param y  the y coordinate of the rsu
	 */
	public void delRSU(int x, int y){
		// Get the nearest RSU
		RSU rsu = null;
		
		Region[][] tmpRegions = Map.getInstance().getRegions();
			
		int regionCountX = Map.getInstance().getRegionCountX();
		int regionCountY = Map.getInstance().getRegionCountY();
		int radius = 250;
			
		for(int i = 0; i <= regionCountX-1;i++){
			for(int j = 0; j <= regionCountY-1;j++){
				Region tmpRegion = tmpRegions[i][j];
					
				RSU[] rsus = tmpRegion.getRSUs();
					
				for(int k = 0;k < rsus.length; k++){
					if((x + radius) > rsus[k].getX() && (x - radius) < rsus[k].getX() && (y + radius) > rsus[k].getY() && (y - radius) < rsus[k].getY()){
						rsu = rsus[k];
					}
				}
			}
		}
		
		//if RSU was found, delete it
		if(rsu != null) rsu.getRegion().delRSU(rsu);
	}
	
	/**
	 * Add a new vehicle to the correct start region. A vehicle can only be in one region and may move to other regions during 
	 * the simulation. Although a vehicle is part of a scenario, it's added here as the map knows everything about how to
	 * correctly put it into a region.
	 * 
	 * @param vehicle	the vehicle to add
	 */
	public void addVehicle(Vehicle vehicle){
		int regionX = vehicle.getX()/regionWidth_;	//implicit rounding (=floor)because of integer values!
		int regionY = vehicle.getY()/regionHeight_;

		// to prevent "array out of bounds" when vehicle is outside of map
		if (regionX >= regionCountX_) regionX = regionCountX_ - 1;
		else if(regionX < 0) regionX = 0;
		if (regionY >= regionCountY_) regionY = regionCountY_ - 1;
		else if (regionY < 0) regionY = 0;

		vehicle.setRegion(regions_[regionX][regionY]);
		regions_[regionX][regionY].addVehicle(vehicle, false);
	}

	/**
	 * Deletes a vehicle from it's region.
	 * 
	 * @param vehicle	the vehicle to delete
	 */
	public void delVehicle(Vehicle vehicle){		
		int regionX = vehicle.getRegionX();
		int regionY = vehicle.getRegionY();
		regions_[regionX][regionY].delVehicle(vehicle);
	}

	/**
	 * Add a new street to the correct region(s). Note that a street can be in multiple regions and so we must determine
	 * all here! This makes rendering and calculations a lot easier later!
	 * 
	 * @param street	the street to add
	 */
	public void addStreet(Street street){
		int startRegionX = street.getStartNode().getRegion().getX();
		int startRegionY = street.getStartNode().getRegion().getY();
		int endRegionX = street.getEndNode().getRegion().getX();
		int endRegionY = street.getEndNode().getRegion().getY();
		int i;

		// find the regions in which this street belongs!
		if(startRegionX == endRegionX){
			if(startRegionY == endRegionY) regions_[startRegionX][startRegionY].addStreet(street, true);		// just in one region
			else{	// above or beneath
				if(startRegionY < endRegionY){
					for (i = startRegionY; i <= endRegionY; ++i) regions_[startRegionX][i].addStreet(street, true);
				} else {
					for (i = endRegionY; i <= startRegionY; ++i) regions_[startRegionX][i].addStreet(street, true);
				}
			}
		} else if(startRegionY == endRegionY){
			if(startRegionX < endRegionX){	// left or right
				for (i = startRegionX; i <= endRegionX; ++i) regions_[i][startRegionY].addStreet(street, true);
			} else {
				for (i = endRegionX; i <= startRegionX; ++i) regions_[i][startRegionY].addStreet(street, true);
			}
		} else{		// seems to be non-trivial crossing regions, try some kind of bruteforce now!
			// we now need the real coordinates and not just the regions!
			int start_x = street.getStartNode().getX();
			int start_y = street.getStartNode().getY();
			int end_x = street.getEndNode().getX();
			int end_y = street.getEndNode().getY();

			regions_[startRegionX][startRegionY].addStreet(street, true);
			regions_[endRegionX][endRegionY].addStreet(street, true);

			// calculate line parameters: y = ax + b
			double a = ((double)start_y - end_y) / ((double)start_x - end_x);	// (start_x - end_x) can't be zero because then (start_region_x == end_region_x) above would have been true!
			double b = start_y - a * start_x;

			double x, y;
			long tmp;

			int max_x = Math.max(endRegionX, startRegionX);		//cache so that the math-function isn't called too often
			int max_y = Math.max(startRegionY, endRegionY);
			for(i = Math.min(startRegionX, endRegionX); i < max_x; ++i){	// check all vertical grid lines of the regions to be considered
				y = a * (i * regionWidth_) + b;	// left side of this grid
				tmp = Math.round(y) / regionHeight_;
				if(tmp > -1 && tmp < regionCountY_) regions_[i][(int)tmp].addStreet(street, true);
				y = a * ((i * regionWidth_) + regionWidth_ - 1) + b;	// right side of this grid
				tmp = Math.round(y) / regionHeight_;
				if(tmp > -1 && tmp < regionCountY_) regions_[i][(int)tmp].addStreet(street, true);
			}
			for(i = Math.min(startRegionY, endRegionY); i < max_y; ++i){	// check all horizontal grid lines of the regions to be considered
				x = ((i * regionHeight_) - b)/ a;		// upper side of this grid
				tmp = Math.round(x) / regionWidth_;
				if(tmp > -1 && tmp < regionCountX_) regions_[(int)tmp][i].addStreet(street, true);
				x = (((i * regionHeight_) + regionHeight_ - 1) - b)/ a;	// lower side of this grid
				tmp = Math.round(x) / regionWidth_;
				if(tmp > -1 && tmp < regionCountX_) regions_[(int)tmp][i].addStreet(street, true);
			}
		}
	}


	/**
	 * Delete a street from all region(s) it's in.
	 * 
	 * @param street	the street to delete
	 */
	public void delStreet(Street street){
		int startRegionX = street.getStartNode().getRegion().getX();
		int startRegionY = street.getStartNode().getRegion().getY();
		int endRegionX = street.getEndNode().getRegion().getX();
		int endRegionY = street.getEndNode().getRegion().getY();
		int i;

		// find the regions in which this street belongs!
		if(startRegionX == endRegionX){
			if(startRegionY == endRegionY) regions_[startRegionX][startRegionY].delStreet(street);		// just in one region
			else{	// above or beneath
				if(startRegionY < endRegionY){
					for (i = startRegionY; i <= endRegionY; ++i) regions_[startRegionX][i].delStreet(street);
				} else {
					for (i = endRegionY; i <= startRegionY; ++i) regions_[startRegionX][i].delStreet(street);
				}
			}
		} else if(startRegionY == endRegionY){
			if(startRegionX < endRegionX){	// left or right
				for (i = startRegionX; i <= endRegionX; ++i) regions_[i][startRegionY].delStreet(street);
			} else {
				for (i = endRegionX; i <= startRegionX; ++i) regions_[i][startRegionY].delStreet(street);
			}
		} else{		// seems to be non-trivial crossing regions, try some kind of bruteforce now!
			// we now need the real coordinates and not just the regions!
			int start_x = street.getStartNode().getX();
			int start_y = street.getStartNode().getY();
			int end_x = street.getEndNode().getX();
			int end_y = street.getEndNode().getY();

			regions_[startRegionX][startRegionY].delStreet(street);
			regions_[endRegionX][endRegionY].delStreet(street);

			// calculate line parameters: y = ax + b
			double a = ((double)start_y - end_y) / ((double)start_x - end_x);	// (start_x - end_x) can't be zero because then (start_region_x == end_region_x) above would have been true!
			double b = start_y - a * start_x;

			double x, y;
			long tmp;

			int max_x = Math.max(endRegionX, startRegionX);		//cache so that the math-function isn't called too often
			int max_y = Math.max(startRegionY, endRegionY);
			for(i = Math.min(startRegionX, endRegionX); i < max_x; ++i){	// check all vertical grid lines of the regions to be considered
				y = a * (i * regionWidth_) + b;	// left side of this grid
				tmp = Math.round(y) / regionHeight_;
				if(tmp > -1 && tmp < regionCountY_) regions_[i][(int)tmp].delStreet(street);
				y = a * ((i * regionWidth_) + regionWidth_ - 1) + b;	// right side of this grid
				tmp = Math.round(y) / regionHeight_;
				if(tmp > -1 && tmp < regionCountY_) regions_[i][(int)tmp].delStreet(street);
			}
			for(i = Math.min(startRegionY, endRegionY); i < max_y; ++i){	// check all horizontal grid lines of the regions to be considered
				x = ((i * regionHeight_) - b)/ a;		// upper side of this grid
				tmp = Math.round(x) / regionWidth_;
				if(tmp > -1 && tmp < regionCountX_) regions_[(int)tmp][i].delStreet(street);
				x = (((i * regionHeight_) + regionHeight_ - 1) - b)/ a;	// lower side of this grid
				tmp = Math.round(x) / regionWidth_;
				if(tmp > -1 && tmp < regionCountX_) regions_[(int)tmp][i].delStreet(street);
			}
		}
	}


	/**
	 * Add a new mix zone to the correct region. A mix zone can only be in one region.
	 * 
	 * @param node	the mix zone node to add
	 * @param radius	the mix zone radius
	 */
	public void addMixZone(Node node, int radius){
		int regionX = node.getX()/regionWidth_;	//implicit rounding (=floor)because of integer values!
		int regionY = node.getY()/regionHeight_;
		regions_[regionX][regionY].addMixZone(node, radius);
	}
	
	/**
	 * Delete a mix zone in the correct region.
	 */
	public void deleteMixZone(Node node){
		int regionX = node.getX()/regionWidth_;	//implicit rounding (=floor)because of integer values!
		int regionY = node.getY()/regionHeight_;

		regions_[regionX][regionY].deleteMixZone(node);
	}
	
	/**
	 * Delete every Mix Zone on this map
	 */
	public void clearMixZones(){
		for(int i = 0; i < regionCountX_; ++i) for(int j = 0; j < regionCountY_; ++j) regions_[i][j].clearMixZones();
	}
	
	/**
	 * Delete every RSU on this map
	 */
	public void clearRSUs(){
		for(int i = 0; i < regionCountX_; ++i) for(int j = 0; j < regionCountY_; ++j) regions_[i][j].clearRSUs();
	}
	
	/**
	 * Delete every Vehicle on this map
	 */
	public void clearVehicles(){
		Renderer.getInstance().setMarkedVehicle(null);
		for(int i = 0; i < regionCountX_; ++i) for(int j = 0; j < regionCountY_; ++j) regions_[i][j].cleanVehicles();
	}
	
	/**
	 * Function to trim a map. If no coordinates are chosen the map will be trimmed to its borders (Only empty space will be removed). Otherwise the map will be trimmed according
	 * to the coordinates.
	 * 
	 * @param minX	the minimum x coordinate
	 * @param maxX	the maximum x coordinate
	 * @param minY	the minimum y coordinate
	 * @param maxY	the maximum y coordinate
	 */
	@SuppressWarnings("unchecked")
	public void autoTrimMap(int minX, int minY, int maxX, int maxY){
		Node[] tmpNodes = null;
		Street[] tmpStreets = null;
		
		boolean autoTrim = false;
		
		if(minX == -1 && minY == -1 && maxX == -1 && maxY == -1) autoTrim = true;
		
		//no coordinates where choosen, just trim the map
		if(autoTrim){
			minX = width_;
			minY = height_;
			
			maxX = 0;
			maxY = 0;
			
			int tmpX = 0;
			int tmpY = 0;
			

			
			//find min x and min y
			for(int i = 0; i < regions_.length; i++){
				for(int j = 0; j < regions_[i].length; j++){
					tmpNodes = regions_[i][j].getNodes();				
					for(int k = 0; k < tmpNodes.length; k++){
						tmpX = tmpNodes[k].getX();
						tmpY = tmpNodes[k].getY();
						
						if(tmpX < minX) minX = tmpX;
						if(tmpY < minY) minY = tmpY;
						
						if(tmpX > maxX) maxX = tmpX;
						if(tmpY > maxY) maxY = tmpY;
					}
				}
			}
		}
		//coordinates where chosen, trim according to coordinates
		else{
			Node tmpStartNode = null;
			Node tmpEndNode = null;
			//we probably have to trim some streets
			for(int i = 0; i < regions_.length; i++){
				for(int j = 0; j < regions_[i].length; j++){
					tmpStreets = regions_[i][j].getStreets();
					for(int k = 0; k < tmpStreets.length; k++){				
						tmpStartNode = tmpStreets[k].getStartNode();
						tmpEndNode = tmpStreets[k].getEndNode();
						
						//both nodes are outside of the array. Delete them
						if(((tmpStartNode.getX() < minX || tmpStartNode.getY() < minY) && 
								(tmpEndNode.getX() < minX || tmpEndNode.getY() < minY)) ||
								((tmpStartNode.getX() > maxX || tmpStartNode.getY() > maxY) && 
										(tmpEndNode.getX() > maxX || tmpEndNode.getY() > maxY))) {
							tmpStreets[k].getStartNode().delOutgoingStreet(tmpStreets[k]);
							tmpStreets[k].getStartNode().delCrossingStreet(tmpStreets[k]);
							if(tmpStreets[k].getStartNode().getCrossingStreetsCount()==0) Map.getInstance().delNode(tmpStreets[k].getStartNode());
							tmpStreets[k].getEndNode().delOutgoingStreet(tmpStreets[k]);
							tmpStreets[k].getEndNode().delCrossingStreet(tmpStreets[k]);
							if(tmpStreets[k].getEndNode().getCrossingStreetsCount()==0) Map.getInstance().delNode(tmpStreets[k].getEndNode());
							Map.getInstance().delStreet(tmpStreets[k]);
						}
						//the start node is outside the area
						else if	(tmpStartNode.getY() < minY ||  tmpStartNode.getY() > maxY || tmpEndNode.getY() < minY || tmpEndNode.getY() > maxY ||
								tmpStartNode.getX() < minX || tmpEndNode.getX() < minX || tmpStartNode.getX() > maxX || tmpEndNode.getX() > maxX){
							
							int tmpBorder;
							Node tmp = null;
							
							if (tmpStartNode.getY() < minY || tmpEndNode.getY() < minY) tmpBorder = minY;
							else if (tmpStartNode.getY() > maxY || tmpEndNode.getY() > maxY) tmpBorder = maxY;
							else if (tmpStartNode.getX() < minX || tmpEndNode.getX() < minX) tmpBorder = minX;
							else tmpBorder = maxX;
							
							if(tmpEndNode.getY() < minY || tmpEndNode.getY() > maxY || tmpEndNode.getX() < minX || tmpEndNode.getX() > maxX){
								tmp = tmpStartNode;
								tmpStartNode = tmpEndNode;
								tmpEndNode = tmp;
							}
							
							
							Node tmpNode = new Node(tmpStartNode.getX(), tmpStartNode.getY());
							tmpNode.addOutgoingStreet(tmpStreets[k]);
							tmpNode.addCrossingStreet(tmpStreets[k]);
							
							tmpStartNode.delCrossingStreet(tmpStreets[k]);
							tmpStartNode.delOutgoingStreet(tmpStreets[k]);
							
							
							regions_[i][j].delNode(tmpStartNode);
							
							regions_[i][j].addNode(tmpNode, false);
							tmpStartNode = tmpNode;
							
							if(tmp == null) tmpStreets[k].setStartNode(tmpStartNode);
							else tmpStreets[k].setEndNode(tmpStartNode);
							
							if(tmpStartNode.getX() < minX || tmpEndNode.getX() < minX || tmpStartNode.getX() > maxX || tmpEndNode.getX() > maxX){
								tmpStartNode.setY(trimStreet(tmpEndNode.getY(), tmpEndNode.getX(), tmpStartNode.getY(), tmpStartNode.getX(), tmpBorder));
								tmpStartNode.setX(tmpBorder);
							}
							else{
								tmpStartNode.setX(trimStreet(tmpEndNode.getX(), tmpEndNode.getY(), tmpStartNode.getX(), tmpStartNode.getY(), tmpBorder));
								tmpStartNode.setY(tmpBorder);
							}

						}
				 	}
				}
			}
		}

		
		//set new map width and height
		width_ = maxX - minX;
		height_ = maxY - minY;
		
		//change all nodes
		for(int i = 0; i < regions_.length; i++){
			for(int j = 0; j < regions_[i].length; j++){
				tmpNodes = regions_[i][j].getNodes();				
				for(int k = 0; k < tmpNodes.length; k++){				
					tmpNodes[k].setX(tmpNodes[k].getX() - minX);
					tmpNodes[k].setY(tmpNodes[k].getY() - minY);
				}
			}
		}
		
		ArrayList<Node> tmpList = (ArrayList<Node>) Map.getInstance().getAmenityList().clone();
		
		for(Node n:tmpList){
			if((n.getX() < minX || n.getY() < minY) || (n.getX() > maxX || n.getY() > maxY))  Map.getInstance().getAmenityList().remove(n);
			else{
				n.setX(n.getX() - minX);
				n.setY(n.getY() - minY);
			}
		}
		
		Renderer.getInstance().ReRender(true, true);
		
		saveReloadMap();
	}
		
	public int trimStreet(int streetXInside, int streetYInside, int streetXOutside, int streetYOutside, int border){
		int y, a, b, x;
		double f, fakt, c;
		if(streetXInside > streetXOutside) y = streetXInside - streetXOutside;
		else y = streetXOutside - streetXInside;
		
		if(streetYOutside > border){
			a = border - streetYInside;
			b = streetYOutside - border;
		}
		else{
			a = streetYInside - border;
			b = border - streetYOutside;
		}
		
		f = Math.sqrt(y*y + (a+b)*(a+b));
		fakt = f / (a+b);
		c = b * fakt;
		x = (int) Math.round(Math.sqrt(c*c - b*b));

		if(streetXOutside > streetXInside) return streetXOutside - x;
		else return streetXOutside + x;
	}
	
	/**
	 * Helper function to save and reload a map after editing
	 */
	public void saveReloadMap(){
		VanetSimStart.getMainControlPanel().changeFileChooser(false, true, false);
		final JFileChooser filechooser = VanetSimStart.getMainControlPanel().getFileChooser();
		int returnVal = filechooser.showSaveDialog(VanetSimStart.getMainFrame());
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			Runnable job = new Runnable() {
				public void run() {
					File file = filechooser.getSelectedFile();
					if(filechooser.getAcceptAllFileFilter() != filechooser.getFileFilter() && !file.getName().toLowerCase().endsWith(".xml")) file = new File(file.getAbsolutePath() + ".xml"); //$NON-NLS-1$ //$NON-NLS-2$
					Map.getInstance().save(file, false);
					Map.getInstance().load(file, false);
				}
			};
			new Thread(job).start();
		}
	}	
	

	/**
	 * Delete every turn-off lane on this map
	 */
	public void clearTrafficLights(){
		for(int i = 0; i < regionCountX_; ++i) for(int j = 0; j < regionCountY_; ++j) regions_[i][j].clearTrafficLights();
	}
		
	/**
	 * add amenity node
	 */
	public void addAmenityNode(Node n, String value){
		if(value.equals("school")) n.setNodeColor(Color.yellow);
		else if(value.equals("kindergarten")) n.setNodeColor(Color.green);
		else if(value.equals("hospital")) n.setNodeColor(Color.magenta);
	//	else if(value.equals("driving_school")) n.setNodeColor(Color.orange);
		else if(value.equals("police")) n.setNodeColor(Color.blue);
		else if(value.equals("fire_station")) n.setNodeColor(Color.red);

		n.setAmenity_(value);
		amenityList_.add(n);
	}
	
	/**
	 * Gets the map width.
	 * 
	 * @return the map width
	 */
	public int getMapWidth(){
		return width_;
	}

	/**
	 * Gets the map height.
	 * 
	 * @return the map height
	 */
	public int getMapHeight(){
		return height_;
	}

	/**
	 * Calculates the {@link Region} of a point.
	 * 
	 * @param x	the x coordinate of the point
	 * @param y	the y coordinate of the point
	 * 
	 * @return the region in which this point is located or <code>null</code> if there was a problem
	 */
	public Region getRegionOfPoint(int x, int y){
		if(regionWidth_ > 0 && regionHeight_ > 0){
			int region_x = x/regionWidth_;
			int region_y = y/regionHeight_;
			if (region_x < 0) region_x = 0;
			else if (region_x >= regionCountX_) region_x = regionCountX_ - 1;
			if (region_y < 0) region_y = 0;
			else if (region_y >= regionCountY_) region_y = regionCountY_ - 1;
			return regions_[region_x][region_y];
		} else return null;
	}	

	/**
	 * write silent period header to log file
	 */
	public void writeSilentPeriodHeader(){
		
	}
	
	
	/**
	 * Gets all regions.
	 * 
	 * @return all regions
	 */
	public Region[][] getRegions(){
		return regions_;
	}

	/**
	 * Gets the amount of regions in x direction.
	 * 
	 * @return the amount of regions in x direction
	 */
	public int getRegionCountX(){
		return regionCountX_;
	}

	/**
	 * Gets the amount of regions in y direction.
	 * 
	 * @return the amount of regions in y direction
	 */
	public int getRegionCountY(){
		return regionCountY_;
	}

	/**
	 * Returns if a map is currently in the process of being loaded. While loading, simulation
	 * and rendering should not be done because not all map elements are already existing!
	 * 
	 * @return <code>true</code> if loading has finished, else <code>false</code>
	 */
	public boolean getReadyState(){
		return ready_;
	}

	/**
	 * @return the amenityList
	 */
	public ArrayList<Node> getAmenityList() {
		return amenityList_;
	}

	/**
	 * @param amenityList the amenityList to set
	 */
	public  void setAmenityList(ArrayList<Node> amenityList) {
		amenityList_ = amenityList;
	}

	public String getMapName_() {
		return mapName_;
	}

	public void setMapName_(String mapName_) {
		this.mapName_ = mapName_;
	}
	
	
} public final class Node {
			
	/** A common counter to generate unique IDs */
	private static int counter_ = 0;
	
	/** A unique ID for this node */
	private final int nodeID_;
	
	/** The x coordinate. */
	private int x_;
	
	/** The y coordinate. */
	private int y_;	
	
	/** flag if the node has a traffic signal */
	private boolean hasTrafficSignal_;
	
	/** An array containing all streets going out from this node. */
	private Street[] outgoingStreets_ = new Street[0];		// only needed for storing/iterating of a small amount of streets so an array is by far the fastest solution!

	/** An array containing all streets coming into or going out from this node. */
	private Street[] crossingStreets_ = new Street[0];
	
	/** The region in which this node is. */
	private Region region_;
	
	/** Holds the junction associated with this node or <code>null</code> if this is not a junction. */
	private Junction junction_ = null;
	
	/** Saves the mix zone radius if this node includes a mix zone */
	private int mixZoneRadius_ = 0;
	
	/** Saves the RSU if encrypted beacons are activated */
	private RSU encryptedRSU_ = null;

	/** Traffic Light*/
	private TrafficLight trafficLight_ = null;
	
	/** Traffic Light Collections */
	private int[] streetHasException_ = null;
	
	/** saves amenitys from the osm map */
	private String amenity_ = "";
	
	/** the color of the node */
	private Color nodeColor = Color.black;
	
	/**
	 * Instantiates a new node.
	 * 
	 * @param x the x coordinate
	 * @param y the y coordinate
	 */
	public Node(int x, int y) {
		x_ = x;
		y_ = y;
		hasTrafficSignal_ = false;
		nodeID_ = counter_;
		++counter_;
	}
	
	/**
	 * Instantiates a new node.
	 * 
	 * @param x the x coordinate
	 * @param y the y coordinate
	 * @param hasTrafficSignal signals if node has a traffic signal
	 */
	public Node(int x, int y, boolean hasTrafficSignal) {
		x_ = x;
		y_ = y;
		hasTrafficSignal_ = hasTrafficSignal;
		nodeID_ = counter_;
		++counter_;
	}

	
	/**
	 * Calculates if this is a junction and the priorities of all possible ways which go over this junction.
	 */
	public void calculateJunction(){		
		if(crossingStreets_.length < 3) junction_ = null;	//if only 2 incomings, it's a street which is just continuing (or the end of a street)!
		else {
			int size = crossingStreets_.length;
			//check if there are at least 2 incoming streets!
			Street tmpStreet;
			int i, count = 0;
			for(i = 0; i < size; ++i){
				tmpStreet = crossingStreets_[i];
				if(!tmpStreet.isOneway() || tmpStreet.getEndNode() == this) ++count;
			}
			if(count < 2) junction_ = null;
			else {
				//A real junction which means quite a lot of work.
				ArrayList<Street> priorityStreets = new ArrayList<Street>();
				String[] namesArray = new String[size];		//basic arrays and bruteforce-search is best here because there are normally only about 3-4 streets!
				int[] foundCount = new int[size];
				int[] maxSpeedArray = new int[size];
				int j = 0;
				count = 0;
				boolean alreadyExisted, foundContinuing = false;
				ArrayList<Node> sourceNodes = new ArrayList<Node>();
				//Step 1: Try to find continuing streets and sourceNodes
				for(i = 0; i < size; ++i){
					tmpStreet = crossingStreets_[i];
					if(!tmpStreet.isOneway()){	//twowayStreet => always a source to add
						if(tmpStreet.getStartNode() != this) sourceNodes.add(tmpStreet.getStartNode());
						else sourceNodes.add(tmpStreet.getEndNode());
					} else if (tmpStreet.getStartNode() != this) sourceNodes.add(tmpStreet.getStartNode());		//onewayStreet: only add if it is really incoming!
					alreadyExisted = false;
					for(j = 0; j < count; ++j){		//check if we already found this street
						if (namesArray[j].equals(tmpStreet.getName())){
							alreadyExisted = true;
							foundContinuing = true;
							++foundCount[j];
							if(maxSpeedArray[j] < tmpStreet.getSpeed()) maxSpeedArray[j] = tmpStreet.getSpeed();
						}
					}
					if(!alreadyExisted){		//didn't exist previously
						namesArray[count] = tmpStreet.getName();
						foundCount[count] = 1;
						maxSpeedArray[count] = tmpStreet.getSpeed();
						++count;
					}
				}
				//Step 2: Find the priority streets based on 2 rules.
				//1. rule: try to find a street which is continuing (has same name) and take the one with the highest speed if there is more than one
				if(foundContinuing){
					int k = -1, maxSpeed = 0;				
					for(i = 0; i < count; ++i){
						if(foundCount[i] > 1 && maxSpeedArray[i] > maxSpeed){
							k = i;
							maxSpeed = maxSpeedArray[i];
						}
					}
					if(k != -1){
						for(i = 0; i < size; ++i){
							tmpStreet = crossingStreets_[i];
							if(tmpStreet.getName().equals(namesArray[k])) priorityStreets.add(tmpStreet);
						}
					}
				//2. rule: take the two streets with highest speed. If two of the three highest are oneway, add a third one.
				} else {
					Street fastestStreet = null, secondFastestStreet = null, thirdFastestStreet = null;
					int fastestSpeed = 0, secondFastestSpeed = 0, thirdFastestSpeed = 0;
					for(i = 0; i < size; ++i){
						tmpStreet = crossingStreets_[i];
						if(tmpStreet.getSpeed() > fastestSpeed){
							thirdFastestSpeed = secondFastestSpeed;
							thirdFastestStreet = secondFastestStreet;
							secondFastestSpeed = fastestSpeed;
							secondFastestStreet = fastestStreet;
							fastestSpeed = tmpStreet.getSpeed();
							fastestStreet = tmpStreet;
						} else if(tmpStreet.getSpeed() > secondFastestSpeed){
							secondFastestSpeed = fastestSpeed;
							secondFastestStreet = fastestStreet;
							fastestSpeed = tmpStreet.getSpeed();
							fastestStreet = tmpStreet;
						} else if(tmpStreet.getSpeed() > thirdFastestSpeed){
							thirdFastestSpeed = tmpStreet.getSpeed();
							thirdFastestStreet = secondFastestStreet;
						}
					}
					priorityStreets.add(fastestStreet);
					priorityStreets.add(secondFastestStreet);
					if(thirdFastestStreet != null && ((fastestStreet.isOneway() && secondFastestStreet.isOneway()) || (secondFastestStreet.isOneway() && thirdFastestStreet != null && thirdFastestStreet.isOneway()) || (fastestStreet.isOneway() && thirdFastestStreet != null && thirdFastestStreet.isOneway()))){
						priorityStreets.add(thirdFastestStreet);
					}
				}
				//we might have three streets now but three are only valid if there's one incoming oneway, one outgoing oneway and one twoway street. Else only take two.
				count = 2;
				if(priorityStreets.size() > 2){
					Street twoWayStreet = null;
					Street firstOnewayStreet = null;
					Street secondOnewayStreet = null;
					for(i = 0; i < 3; ++i){
						if(priorityStreets.get(i).isOneway()){
							if(firstOnewayStreet == null) firstOnewayStreet = priorityStreets.get(i);
							else if(secondOnewayStreet == null) secondOnewayStreet = priorityStreets.get(i);
						} else if(twoWayStreet == null) twoWayStreet = priorityStreets.get(i);
					}
					if(twoWayStreet != null && firstOnewayStreet != null && secondOnewayStreet != null){
						if((firstOnewayStreet.getEndNode() == this && secondOnewayStreet.getEndNode() != this) || (firstOnewayStreet.getEndNode() != this && secondOnewayStreet.getEndNode() == this)) count = 3;
					}
				}
				while(priorityStreets.size() > count) priorityStreets.remove(priorityStreets.size()-1);	//trim priorityStreets to correct size
				size = sourceNodes.size();
				Node tmpNode, priorityEndNode, turnOffNode;
				Street sourceStreet = null, targetStreet;
				Street priorityEndStreet;
				//Step 3: Now create the junction and fill it's hashmaps with priorities for every possible connection
				junction_ = new Junction(this, priorityStreets.toArray(new Street[1]));
				for(i = 0; i < size; ++i){
					tmpNode = sourceNodes.get(i);
					//find source street
					for(j = 0; j < crossingStreets_.length; ++j){
						if(crossingStreets_[j].getStartNode() == tmpNode || crossingStreets_[j].getEndNode() == tmpNode){
							sourceStreet = crossingStreets_[j];
							break;
						}
					}
					//find end street and end node of priority street if source is already a priority street
					priorityEndNode = null;
					priorityEndStreet = null;
						if(count == 2){
							for(j = 0; j < count; ++j){
								if(priorityStreets.get(j) != sourceStreet) priorityEndStreet = priorityStreets.get(j);				
							}
						} else {	//three priority streets with 2 oneways and 1 twoway
							if(!sourceStreet.isOneway()){	//the twoway street is the source
								for(j = 0; j < count; ++j){
									if(priorityStreets.get(j) != sourceStreet && priorityStreets.get(j).getStartNode() == this){	//take the outgoing oneway street
										priorityEndStreet = priorityStreets.get(j);
									}
								}
							} else {	//the incoming oneway street is the source
								for(j = 0; j < count; ++j){
									if(!priorityStreets.get(j).isOneway()){	//take the one and only twoway street
										priorityEndStreet = priorityStreets.get(j);
									}
								}
							}
						}
						if(priorityEndStreet.getStartNode() != this) priorityEndNode = priorityEndStreet.getStartNode();
						else priorityEndNode = priorityEndStreet.getEndNode();
				
					//look through all outgoing streets
					for(j = 0; j < outgoingStreets_.length; ++j){
						targetStreet = outgoingStreets_[j];	
						if(targetStreet != sourceStreet){
							if(targetStreet.getStartNode() != this) turnOffNode = targetStreet.getStartNode();
							else turnOffNode = targetStreet.getEndNode();
							if(priorityStreets.contains(sourceStreet)){
								if(targetStreet == priorityEndStreet){
									//add to highest prio
									junction_.addJunctionRule(tmpNode, turnOffNode, 1);
								} else if(isLineRight(tmpNode.getX(), tmpNode.getY(), priorityEndNode.getX(), priorityEndNode.getY(), turnOffNode.getX(), turnOffNode.getY())){
									//add to highest prio as a right turnoff
									junction_.addJunctionRule(tmpNode, turnOffNode, 2);
								} else {
									//add to middle prio
									junction_.addJunctionRule(tmpNode, turnOffNode, 3);
								}
							} else {
								if(priorityStreets.contains(targetStreet)){		// target is a priority street. Find the priority street belonging to this
									int k;
									if(count == 2){
										for(k = 0; k < count; ++k){
											if(priorityStreets.get(k) != targetStreet) priorityEndStreet = priorityStreets.get(k);				
										}
									} else {	//three priority streets with 2 oneways and 1 twoway
										if(!sourceStreet.isOneway()){	//the twoway street is the source
											for(k = 0; k < count; ++k){
												if(priorityStreets.get(k) != targetStreet && priorityStreets.get(k).getStartNode() == this){	//take the outgoing oneway street
													priorityEndStreet = priorityStreets.get(k);
												}
											}
										} else {	//the incoming oneway street is the source
											for(k = 0; k < count; ++k){
												if(!priorityStreets.get(k).isOneway()){	//take the one and only twoway street
													priorityEndStreet = priorityStreets.get(k);
												}
											}
										}
									}
									if(priorityEndStreet.getStartNode() != this) priorityEndNode = priorityEndStreet.getStartNode();
									else priorityEndNode = priorityEndStreet.getEndNode();
									if(isLineRight(tmpNode.getX(), tmpNode.getY(), priorityEndNode.getX(), priorityEndNode.getY(), turnOffNode.getX(), turnOffNode.getY())){
										//add lowest priority but without need to check if a vehicle is coming from targetStreet
										junction_.addJunctionRule(tmpNode, turnOffNode, 4);
									} else {
										//add to lowest priority
										junction_.addJunctionRule(tmpNode, turnOffNode, 5);
									}
								} else {
									//add to lowest priority
									junction_.addJunctionRule(tmpNode, turnOffNode, 5);
								}
							}
						}
					}
				}
			}			
		}
	}
	
	/**
	 * Checks if the turnoff is at the right hand side compared to a (priority) street which goes from start over current node to an end.
	 * 
	 * @param startX 		the x coordinate of the start
	 * @param starty 		the y coordinate of the start
	 * @param endX 			the x coordinate of the end
	 * @param endY 			the y coordinate of the end
	 * @param turnoffX 		the x coordinate of the turnoff
	 * @param turnoffY 		the y coordinate of the turnoff
	 * 
	 * @return <code>true</code>, if line is on right side, else <code>false</code>
	 */
	private boolean isLineRight(int startX, int starty, int endX, int endY, int turnoffX, int turnoffY){
		//get the polar coordinates (root of "virtual" coordinate system is the junction!) 
		double startLineAngle = (Math.atan2(starty - y_ , startX - x_) + Math.PI);	//adding PI to get non-negative values from 0...2PI
		double endLineAngle = (Math.atan2(endY - y_ , endX - x_) + Math.PI);
		double turnoffLineAngle = (Math.atan2(turnoffY - y_ , turnoffX - x_) + Math.PI);
		
		//check if the angle between turnoff and startLine is larger than the angle between endLine and startLine
		double diffTurnoff = turnoffLineAngle-startLineAngle;
		if (diffTurnoff < 0) diffTurnoff += 2*Math.PI;	//calculated angle in wrong orientation => correct
		double diffEnd = endLineAngle-startLineAngle;
		if (diffEnd < 0) diffEnd += 2*Math.PI;
		
		if(diffTurnoff > diffEnd) return true;
		else return false;
	}
	
	
	/**
	 * Returns the junction object associated with this node or <code>null</code> if this is not a junction.
	 * 
	 * @return the junction or <code>null</code> if this is not a junction
	 */
	public Junction getJunction(){
		return junction_;
	}
	
	/**
	 * Adds an outgoing street. If the array already contains the street, nothing is done.
	 * Note that this operation is not thread-safe.
	 * 
	 * @param street The outgoing street to add.
	 */
	public void addOutgoingStreet(Street street) {
		boolean found = false;
		for(int i = 0; i < outgoingStreets_.length; ++i){
			if(outgoingStreets_[i] == street){
				found = true;
				break;
			}			
		}
		if(!found){
			Street[] newArray = new Street[outgoingStreets_.length+1];
			System.arraycopy (outgoingStreets_,0,newArray,0,outgoingStreets_.length);
			newArray[outgoingStreets_.length] = street;
			outgoingStreets_ = newArray;
		}
	}
	
	/**
	 * Removes an outgoing street. If the array doesn't contain the street, nothing is done.
	 * Note that this operation is not thread-safe.
	 * 
	 * @param street The outgoing street to delete.
	 * 
	 * @return <code>true</code> if street was removed, <code>false</code> if the street wasn't in the list
	 */
	public boolean delOutgoingStreet(Street street){
		for(int i = 0; i < outgoingStreets_.length; ++i){
			if(outgoingStreets_[i] == street){
				Street[] newArray = new Street[outgoingStreets_.length-1];
				if(i > 0){
					System.arraycopy (outgoingStreets_,0,newArray,0,i);
					System.arraycopy (outgoingStreets_,i+1,newArray,i,outgoingStreets_.length-i-1);
				} else System.arraycopy (outgoingStreets_,1,newArray,0,outgoingStreets_.length-1);
				outgoingStreets_ = newArray;
				return true;
			}			
		}
		return false;
	}
	
		
	/**
	 * Gets an array of the outgoing streets of this node. You will always get an array (never <code>null</code>)
	 * but it might have zero size.
	 * 
	 * @return the array
	 */
	public Street[] getOutgoingStreets() {
		return outgoingStreets_;
	}
	
	/**
	 * Gets the number of streets going out from this node.
	 * 
	 * @return the amount of streets
	 */
	public int getOutgoingStreetsCount() {
		return outgoingStreets_.length;
	}
	
	/**
	 * Adds a crossing street. If the array already contains the street, nothing is done.
	 * Note that this operation is not thread-safe.
	 * 
	 * @param street	the crossing street to add.
	 */
	public void addCrossingStreet(Street street) {
		boolean found = false;
		for(int i = 0; i < crossingStreets_.length; ++i){
			if(crossingStreets_[i] == street){
				found = true;
				break;
			}			
		}
		if(!found){
			Street[] newArray = new Street[crossingStreets_.length+1];
			System.arraycopy (crossingStreets_,0,newArray,0,crossingStreets_.length);
			newArray[crossingStreets_.length] = street;
			crossingStreets_ = newArray;
		}
	}
	
	/**
	 * Removes a crossing street. If the array doesn't contain the street, nothing is done.
	 * Note that this operation is not thread-safe.
	 * 
	 * @param street	the incoming street to delete.
	 * 
	 * @return <code>true</code> if street was removed, <code>false</code> if the street wasn't in the list
	 */
	public boolean delCrossingStreet(Street street){
		for(int i = 0; i < crossingStreets_.length; ++i){
			if(crossingStreets_[i] == street){
				Street[] newArray = new Street[crossingStreets_.length-1];
				if(i > 0){
					System.arraycopy (crossingStreets_,0,newArray,0,i);
					System.arraycopy (crossingStreets_,i+1,newArray,i,crossingStreets_.length-i-1);
				} else System.arraycopy (crossingStreets_,1,newArray,0,crossingStreets_.length-1);
				crossingStreets_ = newArray;
				return true;
			}			
		}
		return false;
	}
	
	/**
	 * Gets an array of the streets which are crossing in this node. You will always get an array (never <code>null</code>)
	 * but it might have zero size.
	 * 
	 * @return the array
	 */
	public Street[] getCrossingStreets() {
		return crossingStreets_;
	}
	
	/**
	 * Gets the number of streets crossing in this node.
	 * 
	 * @return the amount of streets
	 */
	public int getCrossingStreetsCount() {
		return crossingStreets_.length;
	}
	
	/**
	 * Gets the x coordinate.
	 * 
	 * @return the x coordinate
	 */
	public int getX() {
		return x_;
	}

	/**
	 * Sets the x coordinate
	 * 
	 * @param x	the new coordinate
	 */
	public void setX(int x) {
		x_ = x;
	}
	/**
	 * Gets the y coordinate.
	 * 
	 * @return the y coordinate
	 */
	public int getY() {
		return y_;
	}
	
	/**
	 * Sets the y coordinate
	 * 
	 * @param y	the new coordinate
	 */
	public void setY(int y) {
		y_ = y;
	}

	/**
	 * Sets the region in which this node is found.
	 * 
	 * @param region the region
	 */
	public void setRegion(Region region) {
		region_ = region;
	}
	
	/**
	 * Gets the region in which this node is found.
	 * 
	 * @return the region
	 */
	public Region getRegion() {
		return region_;
	}
	
	/**
	 * Checks if this object is equal to another.
	 * 
	 * @param other the object to compare to
	 * 
	 * @return <code>true</code>, if both are equal
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	public boolean equals(Object other){
		if(other == null) return false;
		else if (!this.getClass().equals(other.getClass())) return false;
		else {
			Node othernode = (Node)other;
			if(othernode.getX() == x_ && othernode.getY() == y_) return true;
			else return false;
		}
	}
	
	/**
	 * Returns the unique ID of this node.
	 * 
	 * @return an integer
	 */
	public int getNodeID(){
		return nodeID_;
	}
	
	/**
	 * The maximum ID a node has.
	 * 
	 * @return the maximum ID
	 */
	public static int getMaxNodeID(){
		return counter_;
	}
	
	/**
	 * Resets the node ID counter so that newly created nodes begin with an ID of 0.
	 */
	public static void resetNodeID(){
		counter_ = 0;
	}
	
	/**
	 * Creates a hash code (needed for HashMaps or similar structures).
	 * 
	 * @return an Integer
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode(){
		return (x_ - y_);
	}
	
	/**
	 * Sets the mix zone radius
	 * 
	 * @param mixZoneRadius		the new mix zone radius
	 */
	public void setMixZoneRadius(int mixZoneRadius) {
		mixZoneRadius_ = mixZoneRadius;
	}

	/**
	 * The mixZoneRadius
	 * 
	 * @return the max zone radius
	 */
	public int getMixZoneRadius() {
		return mixZoneRadius_;
	}

	public RSU getEncryptedRSU_() {
		return encryptedRSU_;
	}

	public void setEncryptedRSU_(RSU encryptedRSU_) {
		this.encryptedRSU_ = encryptedRSU_;
	}

	public boolean isHasTrafficSignal_() {
		return hasTrafficSignal_;
	}

	public void setHasTrafficSignal_(boolean hasTrafficSignal_) {
		this.hasTrafficSignal_ = hasTrafficSignal_;
	}

	/**
	 * @param trafficLight_ the trafficLight_ to set
	 */
	public void setTrafficLight_(TrafficLight trafficLight_) {
		this.trafficLight_ = trafficLight_;
	}

	/**
	 * @return the trafficLight_
	 */
	public TrafficLight getTrafficLight_() {
		return trafficLight_;
	}


	/**
	 * @return the streetHasException_
	 */
	public int[] getStreetHasException_() {
		return streetHasException_;
	}

	/**
	 * @param streetHasException_ the streetHasException_ to set
	 */
	public void setStreetHasException_(int[] streetHasException_) {
		this.streetHasException_ = streetHasException_;
	}
	
	
	/**
	 * Fill Exception Array of a String.
	 */
	public void addSignalExceptionsOfString(String arrayString) {
		String[] tmpArray = arrayString.split(":");
		streetHasException_ = new int[tmpArray.length];
		
		for(int i = 0; i < tmpArray.length; i++) streetHasException_[i] = Integer.parseInt(tmpArray[i]);
	}


	/**
	 * Check if a Traffic Signal has non-default settings
	 * 
	 * @return true if settings are non-default
	 */
	public boolean hasNonDefaultSettings() {
		if(streetHasException_ == null) return false;
		
		boolean tmpReturn = false;
		
		for(int i : streetHasException_) if(i != 1) tmpReturn = true;
		
		return tmpReturn;
	}
	
	/**
	 * Write exception array in one string. Please check if Signal has exceptions before using.
	 * 
	 * @return string with exceptions
	 */
	public String getSignalExceptionsInString() {
		String tmpReturn = "";
		
		for(int i : streetHasException_) tmpReturn += i + ":";
		
		if(tmpReturn.length() > 0) tmpReturn = tmpReturn.substring(0, tmpReturn.length() - 1);
		
		return tmpReturn;
	}

	/**
	 * @return the amenity_
	 */
	public String getAmenity_() {
		return amenity_;
	}

	/**
	 * @param amenity_ the amenity_ to set
	 */
	public void setAmenity_(String amenity_) {
		this.amenity_ = amenity_;
	}

	/**
	 * @return the nodeColor
	 */
	public Color getNodeColor() {
		return nodeColor;
	}

	/**
	 * @param nodeColor the nodeColor to set
	 */
	public void setNodeColor(Color nodeColor) {
		this.nodeColor = nodeColor;
	}

	
	
} public class IDSProcessor{

	/** The time when the vehicle was last updated in milliseconds. */
	private int lastUpdate_;
	
	/** Link to the previous object. */
	protected IDSProcessor previous_;
	
	/** Link to the next object. */
	protected IDSProcessor next_;
	
	private int[] savedX_;
	
	/** Array to save the n last y for the IDS */
	private int[] savedY_;
	
	/** Array to save the n last speed for the IDS */
	private double[] savedSpeed_;
	
	/** Array to save the n last lastUpdate for the IDS */
	private int[] savedLastUpdate_;
	
	/** Amount of saved beacons (-1 == off) */
	private static int amountOfSavedBeacons_ = 10;
	
	/** counter to fill the array */
	private int arrayCounter = -1;
	
	private static String[] activeRules_;
	
	private Vehicle sourceVehicle_;
	private long ID_;
	private long monitoredVehicleID_;
	private String rule_;
	private int[] lane_;
	private int[] x_;
	private int [] y_;
	//private int sourceX_;
	//private int sourceY_;
	private double[] speed_;
	private int AMOUNT_OF_BEACONS_LOGGED = 8;
	private boolean isFake_;
	//private boolean debug = false;
	//private boolean emergencyVehicle_;
	private Street street_;
	//private boolean direction_;
	private int directionAsNumber_;
	private static int[] falsePositiv = new int[6];
	private static int[] falseNegativ = new int[6];
	private static int[] truePositiv = new int[6];
	private static int[] trueNegativ = new int[6];
	//private KnownVehicle knownVehicle_;
	//private boolean createBlocking_;
	private Vehicle penaltySourceVehicle_;
	
	private static int PCNDistance_ = 625;
	private static double PCNFORWARDThreshold_ = 0.5;
	private static double RHCNThreshold_ = 0.5;
	private static double EEBLThreshold_ = 0.5;
	private static double EVAFORWARDThreshold_ = 0.5;
	private static double EVABeaconTimeFactor_ = 2;
	private static double EVABeaconFactor_ = 3;

	/** flag to activate the advanced IDS attack rules*/
	private static boolean advancedIDSRules_ = false;
	
	private boolean ready_ = false;
	
	private boolean deleteProcessor_ = false;
	
	public static int fake = 0;
	public static int noFake = 0;
	
	/** a flag to make no logging (0), compact logging (1) and large logging (2) */
	private int loggingType_ = 1;
	
	/** A JList to save all available IDS rules **/
	//private static String[] idsData_ = {"HUANG_EEBL", "HUANG_PCN", "HUANG_RHCN", "HUANG_RFN", "HUANG_SVA", "HUANG_CCW", "HUANG_CVW", "HUANG_CL", "HUANG_EVA"};
	private static String[] idsData_ = {"HUANG_EEBL", "HUANG_PCN", "PCN_FORWARD", "HUANG_RHCN", "HUANG_EVA_FORWARD", "EVA_EMERGENCY_ID"};
	
	private static boolean logIDS_ = true;
	
	private int instantIDS_ = -1;
	
	/** the vehicle this structure belongs to */
	private Vehicle vehicle_;
	
	/** the x coordinate of the receiver on message reception */
	//private int oldX_;
	
	/** the y coordinate of the receiver on message reception */
	//private int oldY_;

	/** a flag to start in classic HUANG mode (eebl and rhcn) are with static distances */
	private boolean classicMode_ = false;
	/**
	 * Instantiates a new known vehicle.
	 * 
	 * @param vehicle	the vehicle
	 * @param ID		the ID of the vehicle
	 * @param x 		the x coordinate
	 * @param y			the y coordinate
	 * @param time		the current time
	 * @param speed		the current speed
	 * @param isEncrypted	if Beacon was encrypted
	 */
	public IDSProcessor(int x, int y, Street street,  int direction, Vehicle sourceVehicle, long ID, long monitoredVehicleID, Vehicle penaltySourceVehicle, String rule, boolean isFake, boolean emergencyVehicle, boolean createBlocking, int time, Vehicle thisVehicle){
		lastUpdate_ = time;
		vehicle_ = thisVehicle;
		monitoredVehicleID_ = monitoredVehicleID;
		sourceVehicle_ = sourceVehicle;
		ID_ = ID;
		rule_ = rule;
		//sourceX_ = x;
		//sourceY_ = y;
		isFake_ = isFake;
		//emergencyVehicle_ = emergencyVehicle;
		street_ = street;
		directionAsNumber_ = direction;
		//if(direction < 1) direction_ = true;
		//else direction_ = false;
		
		lane_ = new int[AMOUNT_OF_BEACONS_LOGGED];
		x_ = new int[AMOUNT_OF_BEACONS_LOGGED];
		y_ = new int[AMOUNT_OF_BEACONS_LOGGED];
		speed_ = new double[AMOUNT_OF_BEACONS_LOGGED];
		//createBlocking_ = createBlocking;
		penaltySourceVehicle_ = penaltySourceVehicle;
	
		
		for(int i = 0; i < AMOUNT_OF_BEACONS_LOGGED; i++){
			lane_[i] = -1;
			x_[i] = -1;
			y_[i] = -1;
			speed_[i] = -1;
		}
		
		//oldX_ = thisVehicle.curX_;
		//oldY_ = thisVehicle.curY_;
		
		
		if(loggingType_ > 1)writeLog(sourceVehicle_.getID() +  ":Monitoring:" + monitoredVehicleID_ + ":for:" + rule_);
	}

	/**
	 *  test the persistent contact the vehicle had
	 */
	public int getPersistentContactCount(){
		int beaconInterval = Vehicle.getBeaconInterval();
		int counter = 0;
		int savedLastUpdate = savedLastUpdate_[0];
		for(int i = 1; i <  amountOfSavedBeacons_; i++){
			if((savedLastUpdate_[i] - savedLastUpdate) == beaconInterval){
				counter++;
			}
			savedLastUpdate = savedLastUpdate_[i];

		}
		if((savedLastUpdate_[0] - savedLastUpdate_[amountOfSavedBeacons_-1]) == beaconInterval){
			counter++;
		}
		return counter;
	}
	

	/** adds Beacons to processor */
	public synchronized void addBeacon(int lane, int x, int y, double speed, int timePassed){
		if(loggingType_ > 1)writeLog("Added Beacon:" + sourceVehicle_.getID() +  ":" + monitoredVehicleID_);
		lastUpdate_ = timePassed;
		for(int i = 0; i < speed_.length; i++){
			if(speed_[i] == -1){
				lane_[i] = lane;
				x_[i] = x;
				y_[i] = y;
				speed_[i] = speed;
				
				if(i == (speed_.length - 1) || instantIDS_  == 0){
					ready_ = true;
					sourceVehicle_.setCheckIDSProcessors_(true);

					instantIDS_ = -1;


				}
				if(instantIDS_ > -1) instantIDS_--;
				break;
			}
		}
	}

	
	public String checkIDS(){
		//can be deleted after that
		lastUpdate_ = 0;
		
		ready_ = false;
		deleteProcessor_ = true;
		int lastLoggedBeacon = 0; //in the case where not enough Beacons are logged for IDS because the contact has been lost
		for(;lastLoggedBeacon < AMOUNT_OF_BEACONS_LOGGED; lastLoggedBeacon++) if(speed_[lastLoggedBeacon] == -1) break;
		if(lastLoggedBeacon == 0 && !rule_.equals("EVA_EMERGENCY_ID")) return "";
		
		boolean quit = true;
		for(int j = 0; j < activeRules_.length;j++)if(activeRules_[j].equals(rule_)){
			quit = false;
		}
		if(quit) return "";
		
		double distance = 0;
		double dx = 0;
		double dy = 0;

		
		
		if(rule_.equals("HUANG_PCN")){		
			

			//(advancedAttackRules_ && timeStanding[0] < 14) && 
		//			if((advancedAttackRules_ && (timeStanding[0] < 2 && timeStanding[0] < timeStanding[1])) ||  (!(advancedAttackRules_ && ((timeStanding[0]  - beaconsWithZeroSpeed) > 8)) && (PCNDistance_ < distance)) || (advancedAttackRules_ && ((timeStanding[1]  - 8)  == timeStanding[0]))){
//			if((advancedAttackRules_ && (timeStanding[0] - beaconsWithZeroSpeed < (timeStanding[1] - AMOUNT_OF_BEACONS_LOGGED - 4))) || !(advancedAttackRules_ && ((timeStanding[1]  - AMOUNT_OF_BEACONS_LOGGED -2)  < timeStanding[0])) &&  (PCNDistance_ < distance)){
//((advancedAttackRules_ && (timeStanding[0] - beaconsWithZeroSpeed) < 5) && 
			//if(speed_[0] == 0)System.out.println("timeStanding: " + timeStanding[0] + ":" + timeStanding[1] + " und ist fake: " + isFake_);
			
			//			if((!(advancedAttackRules_ && ((timeStanding[0] - beaconsWithZeroSpeed) > (timeStanding[1] - AMOUNT_OF_BEACONS_LOGGED - 2))) && ((timeStanding[0] - beaconsWithZeroSpeed) < 6) || (PCNDistance_ < distance))){
//			if((!(advancedAttackRules_ && ((timeStanding[0] - beaconsWithZeroSpeed) > (timeStanding[1] - AMOUNT_OF_BEACONS_LOGGED - 2))) && ((advancedAttackRules_ && (timeStanding[0] - beaconsWithZeroSpeed) < 6)) || (PCNDistance_ < distance))){
//(advancedAttackRules_ && (timeStanding[0] - beaconsWithZeroSpeed) < 6)) || 
			// && (!(advancedAttackRules_ && ((timeStanding[0] - beaconsWithZeroSpeed) < (timeStanding[1] - (AMOUNT_OF_BEACONS_LOGGED-1))) && )))
			dx = x_[0] -  x_[(lastLoggedBeacon-1)];
			dy = y_[0] -  y_[(lastLoggedBeacon-1)];
			
			
			distance = Math.sqrt(dx * dx + dy * dy);
			if(advancedIDSRules_){
				//get the time standing in beacon amount
				int beaconsWithZeroSpeed = 0;
				//we do not need the last one
				for(int i = 0; i < AMOUNT_OF_BEACONS_LOGGED-1; i++) if(speed_[i] == 0) beaconsWithZeroSpeed++;
				int[] timeStanding = vehicle_.getKnownVehiclesList().hasBeenSeenWaitingFor(monitoredVehicleID_);
				if((timeStanding[0] - beaconsWithZeroSpeed) < (timeStanding[1] - (AMOUNT_OF_BEACONS_LOGGED-1)) || (timeStanding[0] == 0 && PCNDistance_ < distance)){
					if(loggingType_ > 0)writeLog("Type:HUANG_PCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + PCNDistance_ + ":Distance:" + distance + ":Attack:" + true + ":Correct:" + (isFake_==true)  + timeStanding[0] + ":" + timeStanding[1] + ":" + beaconsWithZeroSpeed);
			
					if(isFake_) truePositiv[0]++;
					else falsePositiv[0]++;
					
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);
					return rule_;
				}
				else{
					if(loggingType_ > 0)writeLog("Type:HUANG_PCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + PCNDistance_ + ":Distance:" + distance + ":Attack:" + false + ":Correct:" + (isFake_==false)  + timeStanding[0] + ":" + timeStanding[1]  + ":" + beaconsWithZeroSpeed);
					
					if(!isFake_) trueNegativ[0]++;
					else  falseNegativ[0]++;
					
					Street[] routeStreets = vehicle_.getRouteStreets();
					boolean[] routeDirections = vehicle_.getRouteDirections();
					int i = vehicle_.getRoutePosition() + 1;	// increased by 1 because a penalty on the street on which the vehicle currently is isn't very helpful
					boolean found = false;
					for(;i < routeStreets.length; ++i){
						if(routeStreets[i] == street_){
							if(routeDirections[i]){		// from startNode to endNode
								if(directionAsNumber_ < 1){
									found = true;
									break;
								}
							} else {		// from endNode to startNode
								if(directionAsNumber_ > -1){
									found = true;
									break;
								}
							}
						}				
					}
					if(found){
						vehicle_.calculateRoute(true, true);
					}
					
					
				//	sourceVehicle_.calculateRoute(true, true);
					/*
					if(isFake_){
						long tmp1 = street_.getStartNode().getX() - x_[0];
						long tmp2 = street_.getStartNode().getY() - y_[0];
						for(int i = 1; i <= street_.getLanesCount(); ++i){
							//if(createBlocking_)sourceVehicle_.getTmpBlockings().add(new BlockingObject(i, direction_, street_, Math.sqrt(tmp1 * tmp1 + tmp2 * tmp2), rule_, (Renderer.getInstance().getTimePassed() + 10000)));
						}	
					}
					*/
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);

					return "";
					
				}
			}
			else{
				

				if((PCNDistance_ < distance)){


					if(loggingType_ > 0)writeLog("Type:HUANG_PCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + PCNDistance_ + ":Distance:" + distance + ":Attack:" + true + ":Correct:" + (isFake_==true));
			
					if(isFake_) truePositiv[0]++;
					else falsePositiv[0]++;
					
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);
					return rule_;
				}
				else{
					if(loggingType_ > 0)writeLog("Type:HUANG_PCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + PCNDistance_ + ":Distance:" + distance + ":Attack:" + false + ":Correct:" + (isFake_==false));
					
					if(!isFake_) trueNegativ[0]++;
					else  falseNegativ[0]++;
					
					
					Street[] routeStreets = vehicle_.getRouteStreets();
					boolean[] routeDirections = vehicle_.getRouteDirections();
					int i = vehicle_.getRoutePosition() + 1;	// increased by 1 because a penalty on the street on which the vehicle currently is isn't very helpful
					boolean found = false;
					for(;i < routeStreets.length; ++i){
						if(routeStreets[i] == street_){
							if(routeDirections[i]){		// from startNode to endNode
								if(directionAsNumber_ < 1){
									found = true;
									break;
								}
							} else {		// from endNode to startNode
								if(directionAsNumber_ > -1){
									found = true;
									break;
								}
							}
						}				
					}
					if(found){
						vehicle_.calculateRoute(true, true);
					}
					/*
					if(isFake_){
						long tmp1 = street_.getStartNode().getX() - x_[0];
						long tmp2 = street_.getStartNode().getY() - y_[0];
						for(int i = 1; i <= street_.getLanesCount(); ++i){
							//if(createBlocking_)sourceVehicle_.getTmpBlockings().add(new BlockingObject(i, direction_, street_, Math.sqrt(tmp1 * tmp1 + tmp2 * tmp2), rule_, (Renderer.getInstance().getTimePassed() + 10000)));
						}	
					}
					*/
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);

					return "";
					
				}
			}

		}
		else if(rule_.equals("PCN_FORWARD")){
			if(lastLoggedBeacon < 2) return "";
			
			//dx = x_[0] -  x_[1];
		//	dy =  y_[0] -  y_[1];
			
		//	double firstSpeed = speed_[0];
			
		//	dx = x_[(lastLoggedBeacon-2)] -  x_[(lastLoggedBeacon-1)];
		//	dy =  y_[(lastLoggedBeacon-2)] -  y_[(lastLoggedBeacon-1)];
			
		//	double lastSpeed = Math.sqrt(dx * dx + dy * dy) / 240 * 36;
			double ratio = 0;
			if(speed_[0] != 0) ratio = (speed_[lastLoggedBeacon-1]/speed_[0]);
			
			if(ratio > PCNFORWARDThreshold_){
				if(loggingType_ > 0)writeLog("Type:PCN_FOWARD:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + PCNFORWARDThreshold_ + ":Distance:" + ratio + ":Attack:" + true + ":Correct:" + (isFake_==true));
				
				if(isFake_) truePositiv[1]++;
				else falsePositiv[1]++;
				
				//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);

				
				return rule_;
			}
			else{
				if(loggingType_ > 0)writeLog("Type:PCN_FORWARD:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + PCNFORWARDThreshold_ + ":Distance:" + ratio + ":Attack:" + false + ":Correct:" + (isFake_==false));
				
				if(!isFake_) trueNegativ[1]++;
				else falseNegativ[1]++;
				
				sourceVehicle_.calculateRoute(true, true);
				/*
				long tmp1 = street_.getStartNode().getX() - x_[0];
				long tmp2 = street_.getStartNode().getY() - y_[0];
				for(int i = 1; i <= street_.getLanesCount(); ++i){
					sourceVehicle_.getTmpBlockings().add(new BlockingObject(i, direction_, street_, Math.sqrt(tmp1 * tmp1 + tmp2 * tmp2), rule_, (Renderer.getInstance().getTimePassed() + 10000)));

				}
				*/
				//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);

			}
		}
		else if(rule_.equals("HUANG_RHCN")){	
			if(lastLoggedBeacon < 2) return "";

			//dx = x_[0] -  x_[1];
			//dy =  y_[0] -  y_[1];
			
			//double firstSpeed = Math.sqrt(dx * dx + dy * dy) / 240 * 36;
			
			//dx = x_[(lastLoggedBeacon-2)] -  x_[(lastLoggedBeacon-1)];
			//dy =  y_[(lastLoggedBeacon-2)] -  y_[(lastLoggedBeacon-1)];
			
			//double lastSpeed = Math.sqrt(dx * dx + dy * dy) / 240 * 36;
			int beaconsWithZeroSpeed = 0;
			for(int i = 0; i < AMOUNT_OF_BEACONS_LOGGED; i++) if(speed_[i] == 0) beaconsWithZeroSpeed++;
			int[] timeStanding = vehicle_.getKnownVehiclesList().hasBeenSeenWaitingFor(monitoredVehicleID_);
			
			
			double ratio = 1;
			
			//double lastSpeedValues = vehicle_.getKnownVehiclesList().hasBeenSlowingDown(monitoredVehicleID_);
			
			//if((advancedAttackRules_ && speed_[0] == 0 && lastSpeedValues == 0) || ratio > RHCNThreshold_){
			
			if(advancedIDSRules_){
				double[] advancedSpeedData = vehicle_.getKnownVehiclesList().getSpecificSpeedDataSet(monitoredVehicleID_, 7);
				/*
				if((vehicle_.getID() + "").equals("234092945793091839") && (monitoredVehicleID_ + "").equals("2799064286177330538")){
					for(int i = 0; i < AMOUNT_OF_BEACONS_LOGGED; i++) System.out.println("speed: " + speed_[i]);
					vehicle_.getKnownVehiclesList().showSpeedData(monitoredVehicleID_);
					System.out.println(advancedSpeedData[0] + ":" + advancedSpeedData[1]);
				}
	*/
				if(advancedSpeedData[1] != -1 && advancedSpeedData[0] != -1){
					if(advancedSpeedData[0] != 0) ratio = (advancedSpeedData[1]/advancedSpeedData[0]);
				}
				else{
					if(speed_[0] != 0) ratio = (speed_[lastLoggedBeacon-1]/speed_[0]);
				}
				//(advancedAttackRules_ && ((timeStanding[0] - beaconsWithZeroSpeed) > 2)) ||
				//int editLastValue = 0;
				//if(speed_[lastLoggedBeacon-1] == 0) editLastValue = 1;
				//if(((timeStanding[0] - beaconsWithZeroSpeed + editLastValue) > 0) || (ratio > RHCNThreshold_)){
				if(speed_[0] == 0 || (speed_[0] > 400 && ratio > RHCNThreshold_)){
					if(loggingType_ > 0)writeLog("Type:HUANG_RHCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + RHCNThreshold_ + ":Distance:" + ratio + ":Attack:" + true + ":Correct:" + (isFake_==true)  + timeStanding[0] + ":" + timeStanding[1] + ":" + beaconsWithZeroSpeed);
					
					if(isFake_) truePositiv[2]++;
					else falsePositiv[2]++;
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);

					return rule_;
				}
				else{
					if(loggingType_ > 0)writeLog("Type:HUANG_RHCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + RHCNThreshold_ + ":Distance:" + ratio + ":Attack:" + false + ":Correct:" + (isFake_==false)  + timeStanding[0] + ":" + timeStanding[1] + ":" + beaconsWithZeroSpeed);
					
					if(!isFake_) trueNegativ[2]++;
					else falseNegativ[2]++;
					/*
					long tmp1 = street_.getStartNode().getX() - x_[0];
					long tmp2 = street_.getStartNode().getY() - y_[0];
					for(int i = 1; i <= street_.getLanesCount(); ++i){
						sourceVehicle_.getTmpBlockings().add(new BlockingObject(i, direction_, street_, Math.sqrt(tmp1 * tmp1 + tmp2 * tmp2), rule_, (Renderer.getInstance().getTimePassed() + 10000)));

					}
					*/
					
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);

				}
			}
			else if(classicMode_){
				dx = x_[0] -  x_[(lastLoggedBeacon-1)];
				dy = y_[0] -  y_[(lastLoggedBeacon-1)];
				
				
				distance = Math.sqrt(dx * dx + dy * dy);
				if(distance > RHCNThreshold_){
					if(loggingType_ > 0)writeLog("Type:HUANG_RHCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + RHCNThreshold_ + ":Distance:" + distance + ":Attack:" + true + ":Correct:" + (isFake_==true)  + timeStanding[0] + ":" + timeStanding[1] + ":" + beaconsWithZeroSpeed);
					
					if(isFake_) truePositiv[2]++;
					else falsePositiv[2]++;
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);

					return rule_;
				}
				else{
					if(loggingType_ > 0)writeLog("Type:HUANG_RHCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + RHCNThreshold_ + ":Distance:" + distance + ":Attack:" + false + ":Correct:" + (isFake_==false)  + timeStanding[0] + ":" + timeStanding[1] + ":" + beaconsWithZeroSpeed);
					
					if(!isFake_) trueNegativ[2]++;
					else falseNegativ[2]++;
					/*
					long tmp1 = street_.getStartNode().getX() - x_[0];
					long tmp2 = street_.getStartNode().getY() - y_[0];
					for(int i = 1; i <= street_.getLanesCount(); ++i){
						sourceVehicle_.getTmpBlockings().add(new BlockingObject(i, direction_, street_, Math.sqrt(tmp1 * tmp1 + tmp2 * tmp2), rule_, (Renderer.getInstance().getTimePassed() + 10000)));

					}
					*/
					
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);

				}
			}
			else{
				//calculate how many braking beacons and how much the speed was reduced
				double lastSavedSpeed = speed_[0];
				int amountOfBrakesSave = 0;
				int amountOfBrakes = 0;
				
				//int firstBeaconIndex = -1;
				for(int i = 1; i < AMOUNT_OF_BEACONS_LOGGED; i++ ){
					if(speed_[i] == -1)break;
					else{
						if((lastSavedSpeed - 50) > speed_[i]) amountOfBrakes++;
						else{
							if(amountOfBrakes > amountOfBrakesSave) amountOfBrakesSave = amountOfBrakes;
							amountOfBrakes = 0;
						}
						lastSavedSpeed = speed_[i];
					}
				}
				if(amountOfBrakes > amountOfBrakesSave) amountOfBrakesSave = amountOfBrakes;
				
				
				
				if(speed_[0] != 0) ratio = (speed_[lastLoggedBeacon-1]/speed_[0]);
				if(speed_[0] == 0 || (speed_[0] > 400 && ratio > RHCNThreshold_)){
					if(loggingType_ > 0)writeLog("Type:HUANG_RHCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + RHCNThreshold_ + ":Distance:" + ratio + ":braking:" + amountOfBrakesSave + ":lastLoggedBeacon:" + lastLoggedBeacon + ":startSpeed:" + speed_[0] + ":lastSpeed:" + speed_[lastLoggedBeacon-1] + ":Attack:" + true + ":Correct:" + (isFake_==true)  + timeStanding[0] + ":" + timeStanding[1] + ":" + beaconsWithZeroSpeed);
					
					if(isFake_) truePositiv[2]++;
					else falsePositiv[2]++;
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);

					return rule_;
				}
				else{
					if(loggingType_ > 0)writeLog("Type:HUANG_RHCN:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + RHCNThreshold_ + ":Distance:" + ratio + ":braking:" + amountOfBrakesSave + ":lastLoggedBeacon:" + lastLoggedBeacon + ":startSpeed:" + speed_[0] +  ":lastSpeed:" + speed_[lastLoggedBeacon-1] + ":Attack:" + false + ":Correct:" + (isFake_==false)  + timeStanding[0] + ":" + timeStanding[1] + ":" + beaconsWithZeroSpeed);
					
					if(!isFake_) trueNegativ[2]++;
					else falseNegativ[2]++;
					/*
					long tmp1 = street_.getStartNode().getX() - x_[0];
					long tmp2 = street_.getStartNode().getY() - y_[0];
					for(int i = 1; i <= street_.getLanesCount(); ++i){
						sourceVehicle_.getTmpBlockings().add(new BlockingObject(i, direction_, street_, Math.sqrt(tmp1 * tmp1 + tmp2 * tmp2), rule_, (Renderer.getInstance().getTimePassed() + 10000)));

					}
					*/
					
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);

				}
			}
			

		}
		else if(rule_.equals("HUANG_EVA_FORWARD")){		
			if(classicMode_){
				
				//dx = x_[0] -  x_[1];
				//dy =  y_[0] -  y_[1];
				
				//double firstSpeed = Math.sqrt(dx * dx + dy * dy) / 240 * 36;
				
				//dx = x_[(lastLoggedBeacon-2)] -  x_[(lastLoggedBeacon-1)];
				//dy =  y_[(lastLoggedBeacon-2)] -  y_[(lastLoggedBeacon-1)];
				
				//double lastSpeed = Math.sqrt(dx * dx + dy * dy) / 240 * 36;
			//	if((speed_[lastLoggedBeacon-1]/speed_[0]) > EVAFORWARDThreshold_){	
				//lane_[lastLoggedBeacon-1] == 0 || 
				dx = x_[0] -  x_[(lastLoggedBeacon-1)];
				dy = y_[0] -  y_[(lastLoggedBeacon-1)];
				
				
				distance = Math.sqrt(dx * dx + dy * dy);
				if(distance > EVAFORWARDThreshold_){	
					if(loggingType_ > 0)writeLog("Type:HUANG_EVA_FORWARD:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EVAFORWARDThreshold_ + ":Distance:" + distance + ":Attack:" + true + ":Correct:" + (isFake_==true));
					if(isFake_) truePositiv[3]++;
					else falsePositiv[3]++;
				//	sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);
				}
				else{
					if(!sourceVehicle_.isDrivingOnTheSide_() && sourceVehicle_.getKnownPenalties().hasToMoveOutOfTheWay(penaltySourceVehicle_))sourceVehicle_.setMoveOutOfTheWay_(true);
					if(!isFake_) trueNegativ[3]++;
					else falseNegativ[3]++;
					if(loggingType_ > 0)writeLog("Type:HUANG_EVA_FORWARD:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EVAFORWARDThreshold_ + ":Distance:" + distance + ":Attack:" + false + ":Correct:" + (isFake_==false));
				
					if(!sourceVehicle_.isDrivingOnTheSide_() && hasToMoveOutOfTheWay(penaltySourceVehicle_)){
						sourceVehicle_.setMoveOutOfTheWay_(true);
						sourceVehicle_.setWaitingForVehicle_(penaltySourceVehicle_);
					}
					
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);
				}
			}
			else{
				
				//dx = x_[0] -  x_[1];
				//dy =  y_[0] -  y_[1];
				
				//double firstSpeed = Math.sqrt(dx * dx + dy * dy) / 240 * 36;
				
				//dx = x_[(lastLoggedBeacon-2)] -  x_[(lastLoggedBeacon-1)];
				//dy =  y_[(lastLoggedBeacon-2)] -  y_[(lastLoggedBeacon-1)];
				
				//double lastSpeed = Math.sqrt(dx * dx + dy * dy) / 240 * 36;
			//	if((speed_[lastLoggedBeacon-1]/speed_[0]) > EVAFORWARDThreshold_){	
				//lane_[lastLoggedBeacon-1] == 0 || 
		
				double ratio = 1;
				
				if(speed_[0] != 0) ratio = (speed_[lastLoggedBeacon-1]/speed_[0]);

				if(ratio > EVAFORWARDThreshold_){	
					if(loggingType_ > 0)writeLog("Type:HUANG_EVA_FORWARD:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EVAFORWARDThreshold_ + ":Distance:" + ratio + ":Attack:" + true + ":Correct:" + (isFake_==true));
					if(isFake_) truePositiv[3]++;
					else falsePositiv[3]++;
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);
				}
				else{
					if(!sourceVehicle_.isDrivingOnTheSide_() && sourceVehicle_.getKnownPenalties().hasToMoveOutOfTheWay(penaltySourceVehicle_))sourceVehicle_.setMoveOutOfTheWay_(true);
					if(!isFake_) trueNegativ[3]++;
					else falseNegativ[3]++;
					if(loggingType_ > 0)writeLog("Type:HUANG_EVA_FORWARD:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EVAFORWARDThreshold_ + ":Distance:" + ratio + ":Attack:" + false + ":Correct:" + (isFake_==false));
				
					if(!sourceVehicle_.isDrivingOnTheSide_() && hasToMoveOutOfTheWay(penaltySourceVehicle_)){
						sourceVehicle_.setMoveOutOfTheWay_(true);
						sourceVehicle_.setWaitingForVehicle_(penaltySourceVehicle_);
					}
					
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);
				}
			}

		}
	
		else if(rule_.equals("EVA_EMERGENCY_ID")){
			 //It is important that the known vehicles timeout is smaller than the message interval (otherwise old knownVehicles won't get cleaned up and will be used for the evaluation of the IDS)
			int beaconAmount = KnownVehicle.getAmountOfSavedBeacons_();
			
			if(beaconAmount > 0){
				double[] response = sourceVehicle_.getKnownVehiclesList().getBeaconInformationFromVehicles(monitoredVehicleID_);
			
				//double thresholdTime = ((double)(beaconAmount * Vehicle.getBeaconInterval())/EVABeaconTimeFactor_);

				double thresholdTime = ((double)(beaconAmount * Vehicle.getBeaconInterval())/EVABeaconFactor_);
				double thresholdBeacon = ((double)beaconAmount/EVABeaconFactor_);
				
				//we have no neighbors and therefore, have to rely on our own data
				if(response[2] == 0){
					response = vehicle_.getKnownVehiclesList().checkBeacons(monitoredVehicleID_);
				}
				if(response != null){
					//if((thresholdBeacon > response[1])){

					if((thresholdBeacon > response[1]) && thresholdTime > response[0]){

						if(isFake_) truePositiv[4]++;
						else if(!isFake_) falsePositiv[4]++;
						try{
							if(loggingType_ > 0)writeLog("Type:EVA_EMERGENCY_ID:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Beacon-Threshold:" + thresholdBeacon + ":Beaconvalue:" +  response[1] + ":Time-Threshold:" + thresholdTime + ":Timevalue:" +  response[0]  + "Neighbours:" + response[2] + ":Attack:" + true + ":Correct:" + (isFake_==true));
						}
						catch(Exception e){
							e.printStackTrace();
						}
						//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);
					}
					else{
						if(loggingType_ > 0)writeLog("Type:EVA_EMERGENCY_ID:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Beacon-Threshold:" + thresholdBeacon + ":Beaconvalue:" +  response[1] + ":Time-Threshold:" + thresholdTime + ":Timevalue:" +  response[0]  + "Neighbours:" + response[2] + ":Attack:" + false + ":Correct:" + (isFake_==false));
						if(!isFake_) trueNegativ[4]++;
						else if(isFake_) falseNegativ[4]++;
						
						
						if(!sourceVehicle_.isDrivingOnTheSide_() && sourceVehicle_.getKnownPenalties().hasToMoveOutOfTheWay(penaltySourceVehicle_)){
							sourceVehicle_.setMoveOutOfTheWay_(true);
							sourceVehicle_.setWaitingForVehicle_(penaltySourceVehicle_);
						}
						//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);

							
						sourceVehicle_.setForwardMessage_(true);
					}
				}
				
			}
			else{
				System.out.println("not sufficient Beacons ...");
			}
		}
		
		else if(rule_.equals("HUANG_EEBL")){
			if(lastLoggedBeacon < 2) return "";
			double ratio = 0;
			if(speed_[0] != 0) ratio = (speed_[lastLoggedBeacon-1]/speed_[0]);
			
			
			if(advancedIDSRules_){
				
				double[] advancedSpeedData = vehicle_.getKnownVehiclesList().getSpecificSpeedDataSet(monitoredVehicleID_, 12);
				
				boolean useRule = true;
				if(advancedSpeedData[0] == -1) useRule = false;
				
				
				
				
				if(useRule && advancedSpeedData[0] == 0 || ratio > EEBLThreshold_){
					if(loggingType_ > 0)writeLog("Type:HUANG_EEBL:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EEBLThreshold_ + ":Distance:" + ratio + ":Attack:" + true + ":Correct:" + (isFake_==true));
					if(isFake_) truePositiv[5]++;
					else if(!isFake_) falsePositiv[5]++;
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);

					return rule_;
				
				}
				else {
					if(loggingType_ > 0)writeLog("Type:HUANG_EEBL:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EEBLThreshold_ + ":Distance:" + ratio + ":Attack:" + false + ":Correct:" + (isFake_==false));	
					
					if(!isFake_) trueNegativ[5]++;
					else if(isFake_) falseNegativ[5]++;
					
					//long tmp1 = street_.getStartNode().getX() - x_[0];
					//long tmp2 = street_.getStartNode().getY() - y_[0]; 
					
					/*
					int direction = 0;
					
					if(direction_) direction = 1; //$NON-NLS-1$
					else direction = -1; //$NON-NLS-1$
					try{
						StartBlocking start = new StartBlocking(Renderer.getInstance().getTimePassed(), x_[0], y_[0], direction, lane_[0], false, "HUANG_EEBL");
						if(!EventList.getInstance().eventAlreadyExists(start)){
							EventList.getInstance().addEvent(start); //$NON-NLS-1$	
							EventList.getInstance().addEvent(new StopBlocking(Renderer.getInstance().getTimePassed() + 3000, x_[0], y_[0], start)); //$NON-NLS-1$
						}

					}
					catch(Exception e){e.printStackTrace();}
			*/

					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);


					//sourceVehicle_.getTmpBlockings().add(new BlockingObject(lane_[0], direction_, street_, Math.sqrt(tmp1 * tmp1 + tmp2 * tmp2), rule_, (+ 2000), x_[0], y_[0]));
				}

			}
			else if(classicMode_){
				dx = x_[0] -  x_[(lastLoggedBeacon-1)];
				dy = y_[0] -  y_[(lastLoggedBeacon-1)];
				
				
				distance = Math.sqrt(dx * dx + dy * dy);
				if(distance > EEBLThreshold_){
					if(loggingType_ > 0)writeLog("Type:HUANG_EEBL:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EEBLThreshold_ + ":Distance:" + distance + ":Attack:" + true + ":Correct:" + (isFake_==true));
					if(isFake_) truePositiv[5]++;
					else if(!isFake_) falsePositiv[5]++;
					//sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);

					return rule_;
				
				}
				else {
					if(loggingType_ > 0)writeLog("Type:HUANG_EEBL:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EEBLThreshold_ + ":Distance:" + distance + ":Attack:" + false + ":Correct:" + (isFake_==false));	
					
					if(!isFake_) trueNegativ[5]++;
					else if(isFake_) falseNegativ[5]++;
					
					//long tmp1 = street_.getStartNode().getX() - x_[0];
					//long tmp2 = street_.getStartNode().getY() - y_[0]; 
					
					/*
					int direction = 0;
					
					if(direction_) direction = 1; //$NON-NLS-1$
					else direction = -1; //$NON-NLS-1$
					try{
						StartBlocking start = new StartBlocking(Renderer.getInstance().getTimePassed(), x_[0], y_[0], direction, lane_[0], false, "HUANG_EEBL");
						if(!EventList.getInstance().eventAlreadyExists(start)){
							EventList.getInstance().addEvent(start); //$NON-NLS-1$	
							EventList.getInstance().addEvent(new StopBlocking(Renderer.getInstance().getTimePassed() + 3000, x_[0], y_[0], start)); //$NON-NLS-1$
						}

					}
					catch(Exception e){e.printStackTrace();}
			*/

				//	sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);


					//sourceVehicle_.getTmpBlockings().add(new BlockingObject(lane_[0], direction_, street_, Math.sqrt(tmp1 * tmp1 + tmp2 * tmp2), rule_, (+ 2000), x_[0], y_[0]));
				}
			}
			else{
				if(ratio > EEBLThreshold_){
					if(loggingType_ > 0)writeLog("Type:HUANG_EEBL:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EEBLThreshold_ + ":Distance:" + ratio + ":Attack:" + true + ":Correct:" + (isFake_==true));
					if(isFake_) truePositiv[5]++;
					else if(!isFake_) falsePositiv[5]++;
				//	sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], true);

					return rule_;
				
				}
				else {
					if(loggingType_ > 0)writeLog("Type:HUANG_EEBL:Source:" + sourceVehicle_.getID() +  ":Monitored:" + monitoredVehicleID_ + ":Threshold:" + EEBLThreshold_ + ":Distance:" + ratio + ":Attack:" + false + ":Correct:" + (isFake_==false));	
					
					if(!isFake_) trueNegativ[5]++;
					else if(isFake_) falseNegativ[5]++;
					
					//long tmp1 = street_.getStartNode().getX() - x_[0];
					//long tmp2 = street_.getStartNode().getY() - y_[0]; 
					
					/*
					int direction = 0;
					
					if(direction_) direction = 1; //$NON-NLS-1$
					else direction = -1; //$NON-NLS-1$
					try{
						StartBlocking start = new StartBlocking(Renderer.getInstance().getTimePassed(), x_[0], y_[0], direction, lane_[0], false, "HUANG_EEBL");
						if(!EventList.getInstance().eventAlreadyExists(start)){
							EventList.getInstance().addEvent(start); //$NON-NLS-1$	
							EventList.getInstance().addEvent(new StopBlocking(Renderer.getInstance().getTimePassed() + 3000, x_[0], y_[0], start)); //$NON-NLS-1$
						}

					}
					catch(Exception e){e.printStackTrace();}
			*/

				//	sourceVehicle_.getKnownEventSourcesList_().update(penaltySourceVehicle_, monitoredVehicleID_, sourceX_, sourceY_, speed_[0], false);


					//sourceVehicle_.getTmpBlockings().add(new BlockingObject(lane_[0], direction_, street_, Math.sqrt(tmp1 * tmp1 + tmp2 * tmp2), rule_, (+ 2000), x_[0], y_[0]));
				}

			}
		}	
			return "";

	}
	/**
	 * writes logs and writes to console in debug mode
	 */
	
	/**
	 * checks if a rule is active
	 */
	public static boolean ruleIsActive(String rule){
		boolean returnValue = false;
		for(int i = 0; i < activeRules_.length;i++) if(activeRules_[i].equals(rule)) returnValue = true;
		return returnValue;
	}

	/**
	 * reports stats
	 */
	public static String getReport(){
		return "\nfake: "  + fake + "\nnofake: " + noFake + 	"\n:TP:TN:FP:FN:\n" + "PCN:	:" + truePositiv[0] + ":" + trueNegativ[0] + ":" + falsePositiv[0] + ":" + falseNegativ[0] + "\n" + "PCN_FOWARD:	:" + truePositiv[1] + ":" + trueNegativ[1] + ":" + falsePositiv[1] + ":" + falseNegativ[1] + "\n" +"RHCN:	" + truePositiv[2] + ":" + trueNegativ[2] + ":" + falsePositiv[2] + ":" + falseNegativ[2] + "\n"+"EVA FORWARD	:" + truePositiv[3] + ":" + trueNegativ[3] + ":" + falsePositiv[3] + ":" + falseNegativ[3] + "\n"+"EVA	:" + truePositiv[4] + ":" + trueNegativ[4] + ":" + falsePositiv[4] + ":" + falseNegativ[4] + "\n"+"EEBL	:" + truePositiv[5] + ":" + trueNegativ[5] + ":" + falsePositiv[5] + ":" + falseNegativ[5] + "\n";
	} 
	
	
	public void writeLog(String s){
		try{
			if(logIDS_)IDSLogWriter.log(s);
		}
		catch(Exception e){
			
		}
	}
	
	/**
	 * tests if a vehicle has to move out of the way because of a emergency vehicle approaching. (tests the next 4 streets)
	 */
	
	public boolean hasToMoveOutOfTheWay(Vehicle emergencyVehicle){
		// search if this new information affects the route of the vehicle as route calculation is quite costly
		Street[] routeStreets = emergencyVehicle.getRouteStreets();
		boolean[] routeDirections = emergencyVehicle.getRouteDirections();
		int i = emergencyVehicle.getRoutePosition();
		
		Street curStreet = sourceVehicle_.getCurStreet();
		boolean curDirection = sourceVehicle_.getCurDirection();
		
		for(;i < emergencyVehicle.getRoutePosition() + 5; ++i){
			if(routeStreets.length == i) break;
			if(routeStreets[i] == curStreet && routeDirections[i] == curDirection) return true;				
		}
		
		return false;
	}
	
	/**
	 * Updates the last modification time.
	 * 
	 * @param time	the current time
	 */
	public void setLastUpdate(int time){
		lastUpdate_ = time;
	}

	/**
	 * Gets when this vehicle was last updated.
	 * 
	 * @return the last update time in milliseconds
	 */
	public int getLastUpdate(){
		return lastUpdate_;
	}
	
	/**
	 * Returns the KnownVehicle after this one.
	 * 
	 * @return the next
	 */
	public IDSProcessor getNext() {
		return next_;
	}

	/**
	 * Returns the KnownVehicle before this one.
	 * 
	 * @return the previous
	 */
	public IDSProcessor getPrevious() {
		return previous_;
	}

	/**
	 * Sets the KnownVehicle after this one.
	 * 
	 * @param next	the object which comes after this one
	 */
	public void setNext(IDSProcessor next) {
		next_ = next;
	}

	/**
	 * Sets the KnownVehicle before this one.
	 * 
	 * @param previous	the object which comes before this one
	 */
	public void setPrevious(IDSProcessor previous) {
		previous_ = previous;
	}


	/**
	 * @return the savedX_
	 */
	public int[] getSavedX_() {
		return savedX_;
	}


	/**
	 * @param savedX_ the savedX_ to set
	 */
	public void setSavedX_(int[] savedX_) {
		this.savedX_ = savedX_;
	}


	/**
	 * @return the savedY_
	 */
	public int[] getSavedY_() {
		return savedY_;
	}


	/**
	 * @param savedY_ the savedY_ to set
	 */
	public void setSavedY_(int[] savedY_) {
		this.savedY_ = savedY_;
	}


	/**
	 * @return the savedSpeed_
	 */
	public double[] getSavedSpeed_() {
		return savedSpeed_;
	}


	/**
	 * @param savedSpeed_ the savedSpeed_ to set
	 */
	public void setSavedSpeed_(double[] savedSpeed_) {
		this.savedSpeed_ = savedSpeed_;
	}


	/**
	 * @return the savedLastUpdate_
	 */
	public int[] getSavedLastUpdate_() {
		return savedLastUpdate_;
	}


	/**
	 * @param savedLastUpdate_ the savedLastUpdate_ to set
	 */
	public void setSavedLastUpdate_(int[] savedLastUpdate_) {
		this.savedLastUpdate_ = savedLastUpdate_;
	}


	/**
	 * @return the aMOUNT_OF_SAVED_BEACONS
	 */
	public static int getAmountOfSavedBeacons_() {
		return amountOfSavedBeacons_;
	}


	/**
	 * @param aMOUNT_OF_SAVED_BEACONS the aMOUNT_OF_SAVED_BEACONS to set
	 */
	public static void setAmountOfSavedBeacons(int amountOfSavedBeacons) {
		amountOfSavedBeacons_ = amountOfSavedBeacons;
	}


	/**
	 * @return the arrayCounter
	 */
	public int getArrayCounter() {
		return arrayCounter;
	}


	/**
	 * @param arrayCounter the arrayCounter to set
	 */
	public void setArrayCounter(int arrayCounter) {
		this.arrayCounter = arrayCounter;
	}

	/**
	 * @return the iD_
	 */
	public long getID_() {
		return ID_;
	}

	/**
	 * @param iD_ the iD_ to set
	 */
	public void setID_(long iD_) {
		ID_ = iD_;
	}

	/**
	 * @return the idsData_
	 */
	public static String[] getIdsData_() {
		return idsData_;
	}

	/**
	 * @param idsData_ the idsData_ to set
	 */
	public static void setIdsData_(String[] idsData_) {
		IDSProcessor.idsData_ = idsData_;
	}

	/**
	 * @return the logIDS_
	 */
	public static boolean isLogIDS_() {
		return logIDS_;
	}

	/**
	 * @param logIDS_ the logIDS_ to set
	 */
	public static void setLogIDS_(boolean logIDS_) {
		IDSProcessor.logIDS_ = logIDS_;
	}

	/**
	 * @return the pCNDistance_
	 */
	public static int getPCNDistance_() {
		return PCNDistance_;
	}

	/**
	 * @param pCNDistance_ the pCNDistance_ to set
	 */
	public static void setPCNDistance_(int pCNDistance_) {
		PCNDistance_ = pCNDistance_;
	}

	/**
	 * @return the pCNFORWARDThreshold_
	 */
	public static double getPCNFORWARDThreshold_() {
		return PCNFORWARDThreshold_;
	}

	/**
	 * @param pCNFORWARDThreshold_ the pCNFORWARDThreshold_ to set
	 */
	public static void setPCNFORWARDThreshold_(double pCNFORWARDThreshold_) {
		PCNFORWARDThreshold_ = pCNFORWARDThreshold_;
	}

	/**
	 * @return the rHCNThreshold_
	 */
	public static double getRHCNThreshold_() {
		return RHCNThreshold_;
	}

	/**
	 * @param rHCNThreshold_ the rHCNThreshold_ to set
	 */
	public static void setRHCNThreshold_(double rHCNThreshold_) {
		RHCNThreshold_ = rHCNThreshold_;
	}

	/**
	 * @return the eEBLThreshold_
	 */
	public static double getEEBLThreshold_() {
		return EEBLThreshold_;
	}

	/**
	 * @param eEBLThreshold_ the eEBLThreshold_ to set
	 */
	public static void setEEBLThreshold_(double eEBLThreshold_) {
		EEBLThreshold_ = eEBLThreshold_;
	}

	/**
	 * @return the activeRules_
	 */
	public static String[] getActiveRules_() {
		return activeRules_;
	}

	/**
	 * @param activeRules_ the activeRules_ to set
	 */
	public static void setActiveRules_(String[] activeRules_) {
		IDSProcessor.activeRules_ = activeRules_;
	}

	/**
	 * @return the monitoredVehicleID_
	 */
	public long getMonitoredVehicleID_() {
		return monitoredVehicleID_;
	}

	/**
	 * @param monitoredVehicleID_ the monitoredVehicleID_ to set
	 */
	public void setMonitoredVehicleID_(long monitoredVehicleID_) {
		this.monitoredVehicleID_ = monitoredVehicleID_;
	}

	/**
	 * @return the deleteProcessor_
	 */
	public boolean isDeleteProcessor_() {
		return deleteProcessor_;
	}

	public static double getEVAFORWARDThreshold_() {
		return EVAFORWARDThreshold_;
	}

	public static void setEVAFORWARDThreshold_(double eVAFORWARDThreshold_) {
		EVAFORWARDThreshold_ = eVAFORWARDThreshold_;
	}

	/**
	 * @return the ready_
	 */
	public boolean isReady_() {
		return ready_;
	}

	public void setReady_(boolean ready_) {
		this.ready_ = ready_;
	}

	public int isInstantIDS_() {
		return instantIDS_;
	}

	public void setInstantIDS_(int instantIDS_) {
		this.instantIDS_ = instantIDS_;
	}



	public static double getEVABeaconTimeFactor_() {
		return EVABeaconTimeFactor_;
	}

	public static void setEVABeaconTimeFactor_(double eVABeaconTimeFactor_) {
		EVABeaconTimeFactor_ = eVABeaconTimeFactor_;
	}

	public static double getEVABeaconFactor_() {
		return EVABeaconFactor_;
	}

	public static void setEVABeaconFactor_(double eVABeaconFactor_) {
		EVABeaconFactor_ = eVABeaconFactor_;
	}


	public static boolean isAdvancedIDSRules_() {
		return advancedIDSRules_;
	}

	public static void setAdvancedIDSRules_(boolean advancedIDSRules_) {
		IDSProcessor.advancedIDSRules_ = advancedIDSRules_;
	}



} public class Vehicle extends LaneObject{
	
	/** A reference to the map so that we don't need to call this over and over again. */
	private static final Map MAP = Map.getInstance();
	
	/** A reference to the reporting control panel so that we don't need to call this over and over again. */
	private static final ReportingControlPanel REPORT_PANEL = getReportingPanel();
	
	/** When known vehicles are rechecked for outdated entries. Measured in milliseconds. */
	private static final int KNOWN_VEHICLES_TIMEOUT_CHECKINTERVAL = 5000;
	
	/** When known Road-Side-Units are rechecked for outdated entries. Measured in milliseconds. */
	private static final int KNOWN_RSUS_TIMEOUT_CHECKINTERVAL = 5000;
	
	/** How long to wait between searching the known penalties for outdated entries. Measured in milliseconds. */
	private static final int KNOWN_PENALTIES_TIMEOUT_CHECKINTERVAL = 30000;

	/** The minimum time between two newly created messages in milliseconds (does not apply to forwarded messages!). */
	private static final int MESSAGE_INTERVAL = 5000;

	/** The minimum time between two lane changes in milliseconds. */
	private static final int LANE_CHANGE_INTERVAL = 5000;

	/** If a vehicle does not move (speed=0) for this time (in milliseconds), a jam is detected and a penalty message created. */
	private static final int TIME_FOR_JAM = 5000;

	/** The radius in cm around the message destination in which the message will be flooded. */
	private static final int PENALTY_MESSAGE_RADIUS = 50000;
	
	/** The radius in cm around the message destination in which the message will be flooded. */
	private static final int PENALTY_FAKE_MESSAGE_RADIUS = 50000;

	/** The radius in cm around the message destination in which the message will be flooded. */
	private static final int PENALTY_EVA_MESSAGE_RADIUS = 50000;
	
	/** How long the penalty message will be valid (in milliseconds). 20000*/
	private static final int PENALTY_MESSAGE_VALID = 10000;
	
	/** The penalty which will be added through the message in cm. This value will be added in routing calculations. */
	private static final int PENALTY_MESSAGE_VALUE = 2000000;	// 20km
	
	/** How long the penalty itself will be valid (in milliseconds). 5000*/
	private static final int PENALTY_VALID = 5000;
	
	/** How long a vehicle waits to check if it's inside a mix. */
	private static final int MIX_CHECK_INTERVAL = 1000;
	
	/** How long the attacker waits (steps) to check if has new information about the attacked vehicle and needs to reroute. */
	private static final int ATTACKER_INTERVAL = 50;
	
	/** When the speed fluctuation is changed. Measured in milliseconds. */
	private static final int SPEED_FLUCTUATION_CHECKINTERVAL = 5000;
	
	/** When the speed fluctuation is changed. Measured in milliseconds. */
	private static final int SPEED_NO_FLUCTUATION_CHECKINTERVAL = 10000;
	
	/** Deviation from max speed limit. Simulation fluctuations in the drivers speed when reaching the speed limit. Maximum in cm/s^2 */
	private static final int SPEED_FLUCTUATION_MAX = 6;
	
	/** A global random number generator used to initialize the generators of the vehicles. */
	private static final Random RANDOM = new Random(1L);

	/** The routing algorithm used. */
	private static final RoutingAlgorithm ROUTING_ALGO = new A_Star_Algorithm();	

	/** The routing mode used. See the A_Star_Algo for details. */
	private static int routingMode_ = 1;
	
	/** The minimum time a vehicle must have traveled to get recycled. This shall prevent very shortliving 
	 * vehicles from consuming lots of CPU time for recycling. */
	private static int minTravelTimeForRecycling_ = 60000;

	/** If communication is enabled */
	private static boolean communicationEnabled_ = true;	

	/** If beacons are enabled */
	private static boolean beaconsEnabled_ = true;

	/** If mix zones are enabled */
	private static boolean mixZonesEnabled_ = true;
	
	/** If a fallback to the beaconless method shall be done in mix zones */
	private static boolean mixZonesFallbackEnabled_ = true;
	
	/** If the fallback mode only sends messages which are in flooding/broadcast mode. */
	private static boolean mixZonesFallbackFloodingOnly_ = true;

	/** How large a mix is in cm. Set in the common settings */
	private static int mixZoneRadius_ = 10000;
	
	/** How large a mix is in cm max. */
	private static int maxMixZoneRadius_ = 0;
	
	/** How long a vehicle waits to communicate again (in milliseconds). Also used for cleaning up outdated known messages. */
	private static int communicationInterval_ = 160;

	/** How long a vehicle waits to send its beacons again. */
	private static int beaconInterval_ = 240;

	/** The maximum communication distance a vehicle has. */
	private static int maximumCommunicationDistance_ = 0;

	/** An array holding all regions of the map. */
	private static Region[][] regions_;

	/** If monitoring the beacon is enabled or not. */
	private static boolean beaconMonitorEnabled_ = false;
	
	/** The minimum x coordinate which is checked during beacon monitoring. */
	private static int beaconMonitorMinX_ = -1;
	
	/** The maximum x coordinate which is checked during beacon monitoring. */
	private static int beaconMonitorMaxX_ = -1;
	
	/** The minimum y coordinate which is checked during beacon monitoring. */
	private static int beaconMonitorMinY_ = -1;
	
	/** The maximum y coordinate which is checked during beacon monitoring. */
	private static int beaconMonitorMaxY_ = -1;
	
	/** If recycling of vehicles is allowed or not */
	private static boolean recyclingEnabled_ = true;
	
	/** List of all AttackRSUs */
	private static AttackRSU arsuList[] = new AttackRSU[0];
	
	/** If attacker logging is enabled */
	private static boolean attackerDataLogged_ = false;	
	
	/** If attacker encrypted logging is enabled */
	private static boolean attackerEncryptedDataLogged_ = false;
	
	/** If attacker logging is enabled */
	private static boolean privacyDataLogged_ = false;	
	
	/** ID of the attacked vehicle */
	private static long attackedVehicleID_ = 0;
	
	/** Time for reroute of attacker */
	private static int reRouteTime_ = -1;
	
	/** encrypted beacon communication in Mix-Zones */
	private static boolean encryptedBeaconsInMix_ = false;

	/** A counter for the steady id */
	private static int steadyIDCounter = 0;
	
	/** time between silent-periods (in ms)*/
	private static int TIME_BETWEEN_SILENT_PERIODS = 10000;

	/** time of silent-periods (in ms)*/
	private static int TIME_OF_SILENT_PERIODS = 2000;
	
	/** flag to show if there is a silent period at the moment */
	private static boolean silent_period = false;
	
	/** flag to turn silent periods on/off */
	private static boolean silentPeriodsOn = false;
	
	/** time until pseudonym change in slow model */
	private static int TIME_TO_PSEUDONYM_CHANGE = 3000;
	
	/** speed limit for slow model */
	private static int  SLOW_SPEED_LIMIT = (int)(30 * 100000.0/3600);
	
	/** enable/disable slow */
	private static boolean slowOn = false;
	
	/** ids is activated */
	private static boolean idsActivated = false;
	
	/** the interval to generate fake messages */
	private static int fakeMessagesInterval_ = 10000;
	
	/** mode to send message only to vehicles within reach and to disable forwarding! (used for IDS evaluation) */
	private static boolean directCommunicationMode_ = true;
	
	
	/** How many simulation steps we wait until we send the RHCN message */
	private static int WAIT_TO_SEND_RHCN_ = 4;
	
	/** the counter for the rhcn message */
	private int waitToSendRHCNCounter_ = -1;
	
//	private static int vehiclesInSlow = 0;
	// object variables begin here

	/** The destinations this vehicle wants to visit. */
	public ArrayDeque<WayPoint> originalDestinations_;
	
	/** The <code>WayPoint</code> where this vehicles started. */
	private final WayPoint startingWayPoint_;	
	
	/** The vehicle length in cm. */
	private int vehicleLength_;
	
	/** The maximum speed of this car in cm/s. */
	private int maxSpeed_;

	/** The color of the vehicle. */
	private Color color_;
	
	/** The braking rate in cm/s^2. */
	private int brakingRate_;
	
	/** The acceleration rate in cm/s^2. */
	private int accelerationRate_ ;
	
	/** if activated the vehicle is an emergency vehicle */
	private boolean emergencyVehicle_;

	/** The maximum braking distance in cm/s*/
	private final int maxBrakingDistance_;
	
	/** Deviation from max speed limit. Simulates driver types which drive slower / faster */
	private int speedDeviation_;
	
	/** A class storing messages of different states: execute, forward and old ones. Could also be stored inside the
	 * vehicle class but it's a lot more clearly arranged like that. */ 
	private final KnownMessages knownMessages_ = new KnownMessages(this);

	/** A list of all vehicles currently known because of received beacons. */
	private final KnownVehiclesList knownVehiclesList_ = new KnownVehiclesList();
	

	/** A list of all idsprocessors currently running. */
	private final IDSProcessorList idsProcessorList_ = new IDSProcessorList(this);
	
	/** A list of all known event sources. */
	private final KnownEventSourcesList knownEventSourcesList_ = new KnownEventSourcesList(this.getID());
	
	/** A list of all Road-Side-Units currently known because of received beacons. */
	private final KnownRSUsList knownRSUsList_ = new KnownRSUsList();

	/** All known penalties. */
	private final KnownPenalties knownPenalties_ = new KnownPenalties(this);
	
	/** <code>true</code> if this vehicle has a communication device (WiFi), else <code>false</code> . */
	private boolean wiFiEnabled_;
	
	/** A random number generator for each vehicle. Primarily used for ID generation but can be used for other tasks, too. */
	private final Random ownRandom_;

	/** An ID used in communication (beacons). This might change (=> mixing zone)!It cannot be guaranteed 
	 * that this is really an unique ID as it's generated randomly! */
	private long ID_;
	
	/** An ID used to track vehicles after changing pseudonyms (for logging purpose only) */
	private int steadyID_;
	
	/** The destinations this vehicle wants to visit. */
	private ArrayDeque<WayPoint> destinations_;

	/** The new speed after the step. <code>curSpeed_</code> will be set to this in the moving-process to circumvent synchronisation problems. */
	private double newSpeed_;

	/** The new lane after the step. <code>curLane_</code> will be set to this in the moving-process to circumvent synchronisation problems. */
	private int newLane_ = 1;

	/** If set to true, this car is active and thus is drawn and moves. */
	private boolean active_ = false;

	/** An array containing ALL streets on which this vehicle will move until the next destination is reached. This can change when a rerouting is done! */
	private Street[] routeStreets_;

	/** An array with the directions on the streets corresponding to <code>routeStreets_</code> */
	private boolean[] routeDirections_;

	/** The current position in the <code>routeStreets_</code> and <code>routeDirections_</code> array */
	private int routePosition_;

	/** The current braking distance. */
	private int curBrakingDistance_;

	/** The speed at last braking distance calculation. */
	private double speedAtLastBrakingDistanceCalculation_ = 0;

	/** If the vehicle is currently in a mixing zone */
	private boolean isInMixZone_ = false;

	/** A node that we are allowed to pass. */
	private Node junctionAllowed_ = null;

	/** The maximum distance in cm this car can communicate. */
	private int maxCommDistance_;

	/** The current region. */
	private Region curRegion_;

	/** The time in milliseconds before doing the next movement. During waiting the vehicle communicates but does not 
	 * block other cars from passing. */
	private int curWaitTime_;

	/** The total time in milliseconds this vehicle traveled (excludes predefined waittimes!) */
	private int totalTravelTime_;

	/** The total distance in cm this vehicle traveled */
	private long totalTravelDistance_;

	/** If braking for the next destination should be done currently. */
	private boolean brakeForDestination_ = false;

	/** A countdown for braking. */
	private int brakeForDestinationCountdown_ = Integer.MAX_VALUE;

	/** A countdown for doing the next destination check. */
	private int destinationCheckCountdown_ = 0;

	/** A countdown to check if the minimum time between two lane changes has been reached. */
	private int laneChangeCountdown = 0;

	/** A countdown for communication. Also used for cleaning up outdated known messages. */
	private int communicationCountdown_;

	/** A countdown for sending beacons. */
	private int beaconCountdown_;

	/** A countdown for checking if inside a mix or not. */
	private int mixCheckCountdown_;

	/** A countdown for rechecking if known vehicles are outdated. */
	private int knownVehiclesTimeoutCountdown_;
	
	/** A countdown for rechecking if known RSUs are outdated. */
	private int knownRSUsTimeoutCountdown_;
	
	/** A countdown for rechecking if known penalties are outdated. */
	private int knownPenaltiesTimeoutCountdown_;

	/** A countdown for recalculating the speed fluctuation. */
	private int speedFluctuationCountdown_;
	
	/** Flag to show if a breaking because of the speed fluctuation is currently in progress */
	private boolean isBraking_ = false;
	
	/** Breaking of vehicle in fluctuation mode */
	private double fluctuation_ = 0;
	
	/** How much time has passed since the last message was created*/
	//private int lastMessageCreated = 0;

	/** How much time has passed since the last rhcn message was created*/
	private int lastRHCNMessageCreated = 0;

	/** How much time has passed since the last pcn message was created*/
	private int lastPCNMessageCreated = 0;
	
	/** How much time has passed since the last pcn forward message was created*/
	private int lastPCNFORWARDMessageCreated = 0;
	
	/** How much time has passed since the last eva message was created*/
	private int lastEVAMessageCreated = 0;
	
	/** How much time has passed since the last eva forward message was created*/
	//private int lastEVAFORWARDMessageCreated = 0;
	
	/** How much time has passed since the last eebl message was created*/
	//private int lastEEBLMessageCreated = 0;
	
	/** How much time has passed since the last fake message was created*/
	//private int lastFAKEMessageCreated = 0;

	
	
	/** How long this vehicle is waiting in a jam (in milliseconds). */
	private int stopTime_ = 0;
	
	/** How many messages this vehicle has created. */
	//private int messagesCreated_ = 0;
	
	/** How many messages this vehicle has created. Used to give the messages unique ids (vehicle id + messageCounter; note that a collusion is possible but unlikely) */
	private int messagesCounter_ = 0;
	
	
	/** How many messages this vehicle has created. */
	private int pcnMessagesCreated_ = 0;

	/** How many messages this vehicle has created. */
	private int pcnForwardMessagesCreated_ = 0;
	
	/** How many messages this vehicle has created. */
	private int evaMessagesCreated_ = 0;
	
	/** How many messages this vehicle has created. */
	private int evaForwardMessagesCreated_ = 0;
	
	/** How many messages this vehicle has created. */
	private int rhcnMessagesCreated_ = 0;
	
	/** How many messages this vehicle has created. */
	private int eeblMessagesCreated_ = 0;
	
	/** How many messages this vehicle has created. */
	private int fakeMessagesCreated_ = 0;
	
	/** How many times this vehicle changed it's ID (due to mixes) */
	private int IDsChanged_ = 0;
	
	/** If the vehicle may be reused. */
	private boolean mayBeRecycled_ = false;
	
	/** Forbid to recycle vehicle. */
	private boolean doNotRecycle_ = false;
	
	/** Used to reroute the attacker after leaving the mix-zone */
	private Boolean attackerWasInMix = false;
	
	/** Used to reroute the attacker after leaving the mix-zone */
	private Boolean attackedWasInMix = false;
	
	/** Flag is set true when the attacker finds the attacked vehicle the first time */
	private Boolean firstContact = false;

	/** Saves the node of the current mix-zone. Used for encrypted Beacons*/
	private Node curMixNode_ = null;

	/** Vehicle is waiting behind a traffic signal (do not send any message)*/
	private boolean waitingForSignal_ = false;
	
	/** A distance between vehicles based on time (ms) (between 0 - 1000)*/
	private int timeDistance_ = 1000;
	
	/** A politeness factor in % */
	private int politeness_ = 0;
	
	/** Flag to log begin and end of silent periods */
	private boolean silentPeriod = false;
	
	/** Saved Beacon 1 */
	private String savedBeacon1 = "";
	
	/** Saved Beacon 2 */
	private String savedBeacon2 = "";
	
	/** variable to log next x beacons */
	private int logNextBeacons = 0;
	
	/** flag to show if vehicle is in slow period */
	private boolean isInSlow = false;
	
	/** flag to show if vehicle already changed pseudonym in slow period */
	private boolean changedPseudonymInSlow = false;
	
	/** timestamp of entrance in slow period */
	private int slowTimestamp = 0;
	
	/** flag to show if beacons where already logged */
	private boolean slowBeaconsLogged = false;
	
	/** flag to show if vehicle just started (important for slow-model, because first slow-period won't be logged) */
	private boolean vehicleJustStartedInSlow = true;

	/** flag to move a vehicle if a emergency is behind it */
	private boolean moveOutOfTheWay_ = false;
	
	/** flag if EVA message has to be forwarded */
	private boolean forwardMessage_ = false;

	/** flag if vehicle is faking messages */
	private boolean fakingMessages_ = false;
	
	/** message type the vehicle is faking */
	private String fakeMessageType_ = "";
	
	/** A countdown for sending fake messages. */
	private int fakeMessageCountdown_ = 0;
	
	/** counter to switch between fake message types if "all" is activated */
	private int fakeMessageCounter_ = 0;
	
	/** number of fake message types */
	private int fakeMessageTypesCount = IDSProcessor.getIdsData_().length;
	
	/** emergency braking interval */
	private static int emergencyBrakingInterval_ = 600000;

	/** emergency braking */
	private boolean emergencyBraking_ = false;
	
	/** emergency braking duration */
	private int emergencyBrakingDuration_= 3000;
	
	/** emergency braking countdown */
	private int emergencyBrakingCountdown_= -1;
	
	/** flag to send EEBL messages only once */
	private boolean EEBLmessageIsCreated_ = false;
	
	/** counter how much emergency beacons should be faked */
	private int emergencyBeacons = -1;
	
	/** local blocking object */
	//private ArrayList<BlockingObject> tmpBlockings = new ArrayList<BlockingObject>();
	
	/** flag to save if vehicle is driving on the side of a road because of a emergency vehicle. */
	private boolean drivingOnTheSide_ = false;
	
	/** the emergency vehicle (faking vehicle) we are waiting for until driving back on street */
	private Vehicle waitingForVehicle_ = null;
	
	/** a flag to show if a vehicle is currently near a blocking and needs to update his position information */
	private boolean passingBlocking_ = false;
	
	/** vehicle is in a traffic jam (used to forward pcn message (ids check)) */
	private boolean inTrafficJam_ = false;
	
	/** flag if a ids processor of this vehicle needs to be fired */
	private boolean checkIDSProcessors_ = false;
	
	/** a counter to save the amount of found spamers */
	private int spamCounter_ = 0;
	

	private int EVAMessageDelay_ = 3;

	private static int minEVAMessageDelay_ = 1;
	private static int maxEVAMessageDelay_ = 10;
	
	private boolean logBeaconsAfterEvent_ = false;
	private String beaconString_ = "";
	private int amountOfLoggedBeacons_ = 0;

	/**
	 * Instantiates a new vehicle. You will get an exception if the destinations don't contain at least two <b>valid</b> elements.<br>
	 * Elements are considered as invalid if
	 * <ul>
	 * <li>no route can be found between them and the first destination</li>
	 * <li>the destination is on the same street as the first destination</li>
	 * </ul>
	 * 
	 * @param destinations		an <code>ArrayDeque</code> with at least 2 elements (start and target) indicating where to move.
	 * @param vehicleLength		the vehicle length
	 * @param maxSpeed			the maximum speed of this vehicle in cm/s
	 * @param maxCommDist		the maximum distance in cm this vehicle can communicate
	 * @param wiFiEnabled		<code>true</code> if this vehicle has a communication device (WiFi), else <code>false</code>
	 * @param emergencyVehicle	<code>true</code> vehicle is an emergency vehicle
	 * @param brakingRate		the braking rate in cm/s^2
	 * @param accelerationRate	the acceleration rate in cm/s^2
	 * @param color				the color of the vehicle, if empty the default (color.black) is used
	 * @param timeDistance		the time distance
	 * @param politeness		the politeness
	 * @throws ParseException an Exception indicating that you did not supply a valid destination list.
	 */
	public Vehicle(ArrayDeque<WayPoint> destinations, int vehicleLength, int maxSpeed, int maxCommDist, boolean wiFiEnabled, boolean emergencyVehicle, int brakingRate, int accelerationRate, int timeDistance, int politeness, int speedDeviation, Color color, boolean fakingMessages, String fakeMessageType) throws ParseException {
		if(destinations != null && destinations.size()>1){
			originalDestinations_ = destinations; 
			destinations_ = originalDestinations_.clone();			
			ID_ = RANDOM.nextLong();
			steadyID_ = steadyIDCounter++;
			vehicleLength_ = vehicleLength;
			maxSpeed_ = maxSpeed;
			emergencyVehicle_ = emergencyVehicle;
			color_ = color;
			brakingRate_ = brakingRate;
			accelerationRate_ = accelerationRate;
			timeDistance_ = timeDistance;
			politeness_ = politeness;
			maxBrakingDistance_ = maxSpeed_ + maxSpeed_ * maxSpeed_ / (2 * brakingRate_);	// see http://de.wikipedia.org/wiki/Bremsweg
			startingWayPoint_ = destinations_.pollFirst();		// take the first element and remove it from the destinations!
			wiFiEnabled_ = wiFiEnabled;
			speedDeviation_ = speedDeviation;
			ownRandom_ = new Random(RANDOM.nextLong());
			curX_ = startingWayPoint_.getX();
			curY_ = startingWayPoint_.getY();
			curPosition_ = startingWayPoint_.getPositionOnStreet();
			curStreet_ = startingWayPoint_.getStreet();
			curWaitTime_ = startingWayPoint_.getWaittime();
			
			fakingMessages_ = fakingMessages;
			fakeMessageType_ = fakeMessageType;
			curRegion_ = Map.getInstance().getRegionOfPoint(curX_,curY_);
			maxCommDistance_ = maxCommDist;
			curSpeed_ = brakingRate_/2;
			newSpeed_ = curSpeed_;
			if(curStreet_.isOneway()){
				while(!destinations_.isEmpty() && (destinations_.peekFirst().getStreet() == curStreet_ || !calculateRoute(true, false))){
					curWaitTime_ = destinations_.pollFirst().getWaittime();
				}
			} else {
				while(!destinations_.isEmpty() && (destinations_.peekFirst().getStreet() == curStreet_ || !calculateRoute(false, false))){
					curWaitTime_ = destinations_.pollFirst().getWaittime();
				}
			}
			if(destinations_.size() == 0) throw new ParseException(Messages.getString("Vehicle.errorNotEnoughDestinations"),0); //$NON-NLS-1$
			if(curWaitTime_ == 0){
				active_ = true;
				curStreet_.addLaneObject(this, curDirection_);
			}
			calculatePosition();
			
			//set the countdowns so that not all fire at the same time!
			beaconCountdown_ = (int)Math.round(curPosition_)%beaconInterval_;
			communicationCountdown_ = (int)Math.round(curPosition_)%communicationInterval_;
			mixCheckCountdown_ = (int)Math.round(curPosition_)%MIX_CHECK_INTERVAL;
			knownVehiclesTimeoutCountdown_ = (int)Math.round(curPosition_)%KNOWN_VEHICLES_TIMEOUT_CHECKINTERVAL;
			knownPenaltiesTimeoutCountdown_ = (int)Math.round(curPosition_)%KNOWN_PENALTIES_TIMEOUT_CHECKINTERVAL;
		

			knownRSUsTimeoutCountdown_ = (int)Math.round(curPosition_)%KNOWN_RSUS_TIMEOUT_CHECKINTERVAL;
			speedFluctuationCountdown_ = (int)Math.round(curPosition_)%SPEED_FLUCTUATION_CHECKINTERVAL;
			fakeMessageCountdown_ = (int)Math.round(curPosition_)%fakeMessagesInterval_;
			emergencyBrakingCountdown_ = ownRandom_.nextInt(emergencyBrakingInterval_)+1;
			
			EVAMessageDelay_ = minEVAMessageDelay_ + ownRandom_.nextInt(maxEVAMessageDelay_);
		} else throw new ParseException(Messages.getString("Vehicle.errorNotEnoughDestinations"),0); //$NON-NLS-1$
	}


	/**
	 * (Re-)Calculates the route to the next destination.
	 * 
	 * @param careAboutDirection	<code>true</code> if the direction on the current street shall be used for calculation, <code>false</code> if the direction can be chosen freely
	 * @param isReroute				<code>true</code> to indicate that this is a Rerouting. If it's a rerouting, a non-existent route does not lead to the vehicle getting inactive!
	 * 
	 * @return <code>true</code> if new route has been found, else <code>false</code> (on error)
	 */
	public boolean calculateRoute(boolean careAboutDirection, boolean isReroute){
		try{
			WayPoint nextPoint = destinations_.peekFirst();			
			if(curStreet_ == nextPoint.getStreet()){
				boolean neededDirection;
				if(curPosition_ < nextPoint.getPositionOnStreet()) neededDirection = true;
				else neededDirection = false;
				if(!careAboutDirection || neededDirection == curDirection_){
					routeStreets_ = new Street[1];
					routeStreets_[0] = curStreet_;
					routeDirections_ = new boolean[1];
					routeDirections_[0] = true;
					routePosition_ = 0;
					return true;
				} //else calculate with routing algo below!
			}
			int direction;
			if(!careAboutDirection) direction = 0;
			else if(curDirection_) direction = -1;
			else direction = 1;
			ArrayDeque<Node> routing = ROUTING_ALGO.getRouting(routingMode_, direction, curX_, curY_, curStreet_, curPosition_, nextPoint.getX(), nextPoint.getY(), nextPoint.getStreet(), nextPoint.getPositionOnStreet(), knownPenalties_.getStreets(), knownPenalties_.getDirections(), knownPenalties_.getPenalties(), knownPenalties_.getSize(), maxSpeed_);

			if(routing.size() > 0){
				if(routing.size() == 1){
					routeStreets_ = new Street[2];
					routeStreets_[0] = curStreet_;
					routeStreets_[1] = nextPoint.getStreet();
					routeDirections_ = new boolean[2];
					if(routing.peekFirst() == curStreet_.getEndNode()) routeDirections_[0] = true;
					else routeDirections_[0] = false;
					if(routing.peekFirst() == nextPoint.getStreet().getStartNode()) routeDirections_[1] = true;
					else routeDirections_[1] = false;
					routePosition_ = 0;
					return true;
				} else {
					Node nextNode;
					int i;
					boolean usedDestination = false;
					Street[] outgoingStreets;
					Street tmpStreet = curStreet_, tmpStreet2;
					routeStreets_ = new Street[routing.size() + 1];
					routeDirections_ = new boolean[routing.size() + 1];
					Iterator<Node> routeIterator = routing.iterator();
					if(routing.peekFirst() == curStreet_.getEndNode()) curDirection_ = true;
					else curDirection_ = false;
					routeStreets_[0] = curStreet_;	//add current street as first element
					boolean tmpDirection;
					if(routeIterator.next() == curStreet_.getEndNode()) tmpDirection = true;
					else tmpDirection = false;
					routeDirections_[0] = tmpDirection;
					routePosition_ = 1;
	
					while(true){	//add all streets from routing
						if(routeIterator.hasNext()) nextNode = routeIterator.next();
						else if (!usedDestination){
							usedDestination = true;
							if(nextPoint.getStreet() != tmpStreet){
								nextNode = nextPoint.getStreet().getStartNode();
								if((!tmpDirection && nextNode == tmpStreet.getStartNode()) || (tmpDirection && nextNode == tmpStreet.getEndNode())) nextNode = nextPoint.getStreet().getEndNode();
							} else break;
						} else break;
						if(tmpDirection) outgoingStreets = tmpStreet.getEndNode().getOutgoingStreets();
						else outgoingStreets = tmpStreet.getStartNode().getOutgoingStreets();
						for(i = 0; i < outgoingStreets.length; ++i){
							tmpStreet2 = outgoingStreets[i];
							if (tmpStreet2.getStartNode() == nextNode){
								tmpStreet = tmpStreet2;
								tmpDirection = false;
								break;		// found street we want to => no need to look through others
							} else if (tmpStreet2.getEndNode() == nextNode){
								tmpStreet = tmpStreet2;
								tmpDirection = true;
								break;		// found street we want to => no need to look through others
							}
						}
						routeStreets_[routePosition_] = tmpStreet;
						routeDirections_[routePosition_] = tmpDirection;
						++routePosition_;
					}
					routePosition_ = 0;
					destinationCheckCountdown_ = 0;
					return true;
				}
			} else {
				if(!isReroute && destinations_.size() < 2) {
					active_ = false;
					curWaitTime_ = Integer.MIN_VALUE;
					if(totalTravelTime_ >= minTravelTimeForRecycling_) mayBeRecycled_ = true;
				}
				return false;
			}


		} catch (Exception e){ 
			return false;
		}
	}

	/**
	 * A method to be filled when implementing IDE
	 * @param timePerStep
	 */
	
	public void adjustSpeedWithIDM(int timePerStep){
		// start vehicle
		if(curWaitTime_ != 0 && curWaitTime_ != Integer.MIN_VALUE){
			if(curWaitTime_ <= timePerStep){
				//the time the vehicle will wait until it starts driving
				curWaitTime_ = 0;
				//needs to be set for vehicle to start driving
				active_ = true;
				brakeForDestination_ = false;
				//add the vehicle to the current lane object
				curStreet_.addLaneObject(this, curDirection_);
			} else curWaitTime_ -= timePerStep;
		}
		if(active_){
			if(curWaitTime_ == 0 && curStreet_ != null){
				//as a result of this method a newSpeed_ must be set
				newSpeed_ = 270;	
				if(this.getCurStreet().getName().contains("Mittelweg")) newSpeed_ = 27000;		
			}
		}
	}
	
	/**
	 * A method to be filled when implementing MOBIL
	 * @param timePerStep
	 */
	
	public boolean checkLaneFreeMOBIL(int lane){
		//Check implementation of checkLaneFree(int) to get ideas!
		return true;
	}
	
	
	/**
	 * A method to be filled when using vehicle sjtu trace files
	 * @param timePerStep
	 */
	
	public void adjustSpeedWithSJTUTraceFiles(int timePerStep){
		// start vehicle

		//needs to be set for vehicle to start driving
		active_ = true;
		
		//the time the vehicle will wait until it starts driving
		curWaitTime_ = 0;
		
		//add the vehicle to the current lane object
		curStreet_.addLaneObject(this, curDirection_);

		//as a result of this method a newSpeed_ must be set
		newSpeed_ = 1800;	
	}
	
	/**
	 * A method to be filled when using vehicle San Francisco trace files
	 * @param timePerStep
	 */
	
	public void adjustSpeedWithSanFranciscoTraceFiles(int timePerStep){
		// start vehicle

		//needs to be set for vehicle to start driving
		active_ = true;
		
		//the time the vehicle will wait until it starts driving
		curWaitTime_ = 0;
		
		//add the vehicle to the current lane object
		curStreet_.addLaneObject(this, curDirection_);

		//as a result of this method a newSpeed_ must be set
		newSpeed_ = 1800;	
	}

	
	/**
	 * Adjust the speed if reaching crossings or other cars. It also checks if the vehicle should get active.
	 * Furthermore some cleanup in the known messages and vehicles is done and new jam messages are created if necessary.
	 * 
	 * @param timePerStep the time per step in milliseconds
	 */

	public void adjustSpeed(int timePerStep){
		waitingForSignal_ = false;
		if(curWaitTime_ != 0 && curWaitTime_ != Integer.MIN_VALUE){
			if(curWaitTime_ <= timePerStep){
				curWaitTime_ = 0;
				active_ = true;
				brakeForDestination_ = false;
				curStreet_.addLaneObject(this, curDirection_);
			} else curWaitTime_ -= timePerStep;
		}

		if(active_){
			if(curWaitTime_ == 0 && curStreet_ != null){
				//curBrakingDistance always needs to be up-to-date but speed normally doesn't change too often...
				if(curSpeed_ != speedAtLastBrakingDistanceCalculation_){
					speedAtLastBrakingDistanceCalculation_ = curSpeed_;
					//curBrakingDistance_ = (int)StrictMath.floor(((timeDistance_/1000)*curSpeed_) + curSpeed_ * curSpeed_ / (2 * brakingRate_)); <-- new version, commented out because of performance issues (vehicles are to near together when blocking occurs)
					//System.out.println(curBrakingDistance_);
					curBrakingDistance_ = (int)StrictMath.floor(0.5d + curSpeed_ + curSpeed_ * curSpeed_ / (2 * brakingRate_));
					if(curBrakingDistance_ < 500) curBrakingDistance_ = 500;
				}
				// ================================= 
				// Step 1: Check if vehicle is near destination so that it needs to brake (only checked when necessary => timer!)
				// ================================= 
				if(destinationCheckCountdown_ <= 0 && ! brakeForDestination_){
					WayPoint destinationWayPoint = destinations_.peekFirst();
					long dx = destinationWayPoint.getX() - curX_;
					long dy = destinationWayPoint.getY() - curY_;
					long distanceSquared = dx * dx + dy * dy;
					if(distanceSquared < (long)maxBrakingDistance_*maxBrakingDistance_*2){		//seems we're quite near a destination! This happens only in the last about 2-3 seconds!
						if(destinationWayPoint.getStreet() == curStreet_){ //if on the same street, the distance calculation is already correct!
							if(distanceSquared <= (long)curBrakingDistance_*curBrakingDistance_){
								if(brakeForDestinationCountdown_ > 1000) brakeForDestinationCountdown_ = 1000;
								brakeForDestination_ = true;
							} else destinationCheckCountdown_ = (int)StrictMath.floor(0.5d + ((StrictMath.sqrt(distanceSquared)-maxBrakingDistance_)/maxSpeed_)*1000);
						} else {	//not on the same street. Need to calculate the length of the rest of the way to the destination
							double distance = 0, tmpPosition = curPosition_;
							Street tmpStreet = curStreet_;
							boolean tmpDirection = curDirection_;
							int i;
							int j = routeStreets_.length-1;
							for(i = routePosition_; i < j;){
								if(tmpDirection) distance += tmpStreet.getLength() - tmpPosition;
								else distance += tmpPosition;
								++i;
								tmpDirection = routeDirections_[i];
								tmpStreet = routeStreets_[i];
								if(tmpDirection) tmpPosition = 0;
								else tmpPosition = tmpStreet.getLength();
							}
							if(tmpDirection) distance += destinations_.getFirst().getPositionOnStreet() - tmpPosition;	//left over...
							else distance += tmpPosition - destinations_.getFirst().getPositionOnStreet();			
							if(distance <= curBrakingDistance_){		//near enough to schedule braking!
								if(brakeForDestinationCountdown_ > 1000) brakeForDestinationCountdown_ = 1000;
								brakeForDestination_ = true;
							} else if(distance > maxBrakingDistance_) {	//far enough that we can sleep a little bit more
								destinationCheckCountdown_ = (int)StrictMath.floor(0.5d + (distance-maxBrakingDistance_)/maxSpeed_*1000);	//set time to recheck (using calculated distance and maximum speed!
							}	//don't need to change destinationCheckCountdown as we want to recheck next time
						}
					} else destinationCheckCountdown_ = (int)StrictMath.floor(0.5d + ((StrictMath.sqrt(distanceSquared)-maxBrakingDistance_)/maxSpeed_)*1000);		//set time to recheck (using minimum distance and maximum speed => can never be too high (vehicle might accelerate)!
					
					
					
				} else destinationCheckCountdown_ -= timePerStep;

				// ================================= 
				// Step 2: Check for vehicle/blocking in front of this one or a slower street and try to change lane
				// ================================= 
				int result = checkCurrentBraking(curLane_);
				boolean changedLane = false;
				laneChangeCountdown -= timePerStep;
				if(laneChangeCountdown < 0 && curLane_ == 0) newLane_ = 1;
				
				if(laneChangeCountdown < 0 && result == 1){
					if(curLane_ > 1){
						curBrakingDistance_ += 2000;	//make it little bit longer so that changes are not made too often if one lane has a little bit more space ;)
						int result2 = checkCurrentBraking(curLane_-1);
						curBrakingDistance_ -= 2000;
						
						if(result2 == 0 && checkLaneFree(curLane_+1)){	// only change lane if there are no obstacles on other lane or emergency vehicle is approaching
							newLane_ = curLane_ - 1;

							changedLane = true;
							
							laneChangeCountdown = LANE_CHANGE_INTERVAL;
							result = 0;
							moveOutOfTheWay_ = false;
							drivingOnTheSide_ = false;

						}
						
					}
					if(result == 1 && curStreet_.getLanesCount() > curLane_){
						curBrakingDistance_ += 2000;
						int result2 = checkCurrentBraking(curLane_+1);
						curBrakingDistance_ -= 2000;
						if(result2 == 0 && checkLaneFree(curLane_+1)){	// only change lane if there are no obstacles on other lane
							newLane_ = curLane_ + 1;

							changedLane = true;
							laneChangeCountdown = LANE_CHANGE_INTERVAL;
							result = 0;
						}
					}
				}
				// found a blocking. Check if we might change lane to prevent this
				

				boolean brakeOnce = false;
				if(result > 0){
					brakeOnce = true;
				}

				// ================================= 
				// Step 3: Check if we can change to the right lane
				// ================================= 
				if(laneChangeCountdown < 0 && curLane_ > 1 && !changedLane && result == 0){
					if(checkLaneFree(curLane_ - 1)){
						newLane_ = curLane_ - 1;
						changedLane = true;
						//if(moveOutOfTheWay_) newLane_= 0;
						laneChangeCountdown = LANE_CHANGE_INTERVAL;
						moveOutOfTheWay_ = false;
						drivingOnTheSide_ = false;

					}
				}
				
				if(drivingOnTheSide_){		
					if(next_ != null && next_.getClass().equals(Vehicle.class) && waitingForVehicle_ != null && ((Vehicle) next_).getID() == waitingForVehicle_.getID()){
						drivingOnTheSide_ = false;
						waitingForVehicle_ = null;
						laneChangeCountdown = 1000;
					}
				}

				
				if(emergencyVehicle_) newLane_ = curStreet_.getLanesCount();
				if(moveOutOfTheWay_ && !emergencyVehicle_) {
					if(forwardMessage_ && waitingForVehicle_ != null){
						
						forwardMessage_ = false;
						// find the destination for the message. Will be sent to the next junction in FRONT of us!
						boolean tmpDirection2 = !curDirection_;
						Street tmpStreet2 = curStreet_;
						Street[] crossingStreets;
						Node tmpNode;
						int k, l = 0, destX = -1, destY = -1;
						do{
							++l;
							if(tmpDirection2){
								tmpNode = tmpStreet2.getStartNode();
							} else {
								tmpNode = tmpStreet2.getEndNode();
							}
							if(tmpNode.getJunction() != null){
								destX = tmpNode.getX();
								destY = tmpNode.getY();
								break;
							}
							crossingStreets = tmpNode.getCrossingStreets();
							// find next street behind of us
							if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
								destX = tmpNode.getX();
								destY = tmpNode.getY();
								break;
							}
							for(k = 0; k < crossingStreets.length; ++k){
								if(crossingStreets[k] != tmpStreet2){
									tmpStreet2 = crossingStreets[k];
									if(tmpStreet2.getStartNode() == tmpNode) tmpDirection2 = false;
									else tmpDirection2 = true;
									break;
								}
							}
						} while(tmpStreet2 != curStreet_ && l < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
						// found destination...now insert into messagequeue
						if(destX != -1 && destY != -1){
							int direction = -1;
							if(!curDirection_) direction = 1;
							int time = Renderer.getInstance().getTimePassed();
							PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_EVA_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, false, ID_, waitingForVehicle_ , "HUANG_EVA_FORWARD", false, false);
							message.setFloodingMode(false);	// enable flooding mode if within distance!						
							knownMessages_.addMessage(message, false, true);
							++evaForwardMessagesCreated_;
													
						}	
					}
					
					if(newLane_ == curStreet_.getLanesCount() && ownRandom_.nextInt(100) == 0) {
						drivingOnTheSide_ = true;
						newLane_= curLane_-1;
						changedLane = true;
						laneChangeCountdown = LANE_CHANGE_INTERVAL + 20000;
						moveOutOfTheWay_ = false;
						
						
					}			
				}

				if((curLane_ == 0 && curSpeed_ > 277) || emergencyBraking_){
					brakeOnce = true;	
				}


				// ================================= 
				// Step 4: Break or accelerate
				// ================================= 
				if(brakeForDestinationCountdown_ > 0 && brakeForDestination_) {
					brakeForDestinationCountdown_ -= timePerStep;
				}
				if((brakeForDestinationCountdown_ <= 0 && brakeForDestination_) || brakeOnce || isBraking_){

					if(isBraking_ && !(brakeOnce || (brakeForDestinationCountdown_ <= 0 && brakeForDestination_))) newSpeed_ = curSpeed_ - (fluctuation_ * (double)timePerStep/1000);
					else newSpeed_ = curSpeed_ - (brakingRate_ * (double)timePerStep/1000);

					if(!brakeOnce && newSpeed_ < brakingRate_/2) newSpeed_ = brakingRate_/2;

				}
				if(!brakeForDestination_ && !brakeOnce && !isBraking_){		//if no breaking is scheduled we can accelerate (we don't need to look forward here because cars are not allowed by law to accelerate before they're on a "faster" street :D)

					if(curSpeed_ < (curStreet_.getSpeed() + speedDeviation_)) { 
						newSpeed_ = curSpeed_ + (accelerationRate_ * (double)timePerStep/1000);
					}
				}


				// ================================= 
				// Step 5: Correct to suit hard limits
				// ================================= 
				

				if(newSpeed_ > (maxSpeed_ + speedDeviation_)) newSpeed_ = (maxSpeed_ + speedDeviation_);
				else if (newSpeed_ < 0) newSpeed_ = 0;	//no negative speed
				if((curStreet_.getSpeed() + speedDeviation_) > 0 && newSpeed_ > (curStreet_.getSpeed() + speedDeviation_) && this != Renderer.getInstance().getAttackerVehicle() && !emergencyVehicle_) newSpeed_ = (curStreet_.getSpeed() + speedDeviation_);
			}

		
			// ================================= 
			// Step 6: Check message/beacons/penalties countdown and cleanup
			// ================================= 

			// in this first simulation step, no other communication is done. So we can we can some work concerning messages and 
			// known vehicles here without synchronization problems!	
			
			
			if(speedFluctuationCountdown_ < 1){

				isBraking_ = !isBraking_;
				if(isBraking_){
			
					fluctuation_ = ownRandom_.nextInt(SPEED_FLUCTUATION_MAX);
			
					speedFluctuationCountdown_ += SPEED_FLUCTUATION_CHECKINTERVAL;
				
				}
				else{

					speedFluctuationCountdown_ += SPEED_NO_FLUCTUATION_CHECKINTERVAL;
					fluctuation_ = 0;
				}
				//currentSpeedFluctuation_ = ownRandom_.nextInt(SPEED_FLUCTUATION_MAX) + 1;
				//if(Renderer.getInstance().getMarkedVehicle() != null && Renderer.getInstance().getMarkedVehicle().equals(this)) System.out.println("Geschwindigkeit: " + currentSpeedFluctuation_);
			}
			else speedFluctuationCountdown_ -= timePerStep;
			
			
				if(EEBLmessageIsCreated_){
					emergencyBrakingCountdown_ -= timePerStep;
					if(emergencyBrakingCountdown_ < 1){
						emergencyBraking_ = false;
						EEBLmessageIsCreated_ = false;
					}
				}
				
				//else if(emergencyBrakingCountdown_ < emergencyBrakingDuration_){
					
					/*
					if(!EEBLmessageIsCreated_){
						EEBLmessageIsCreated_ = true;
						//lastMessageCreated = 0;
						// find the destination for the message. Will be sent to the next junction behind us!
						boolean tmpDirection2 = curDirection_;
						Street tmpStreet2 = curStreet_;
						Street[] crossingStreets;
						Node tmpNode;
						int k, l = 0, destX = -1, destY = -1;
						do{
							++l;
							if(tmpDirection2){
								tmpNode = tmpStreet2.getStartNode();
							} else {
								tmpNode = tmpStreet2.getEndNode();
							}
							if(tmpNode.getJunction() != null){
								destX = tmpNode.getX();
								destY = tmpNode.getY();
								break;
							}
							crossingStreets = tmpNode.getCrossingStreets();
							// find next street behind of us
							if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
								destX = tmpNode.getX();
								destY = tmpNode.getY();
								break;
							}
							for(k = 0; k < crossingStreets.length; ++k){
								if(crossingStreets[k] != tmpStreet2){
									tmpStreet2 = crossingStreets[k];
									if(tmpStreet2.getStartNode() == tmpNode) tmpDirection2 = false;
									else tmpDirection2 = true;
									break;
								}
							}
						} while(tmpStreet2 != curStreet_ && l < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
						// found destination...now insert into messagequeue
						if(destX != -1 && destY != -1){
							int direction = -1;
							if(!curDirection_) direction = 1;
							int time = Renderer.getInstance().getTimePassed();
							PenaltyMessage message = new PenaltyMessage(this.getX(), this.getY(), destX, destY, PENALTY_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, false, ID_, this,  "HUANG_EEBL", false, true);
							long dx = message.getDestinationX_() - curX_;
							long dy = message.getDestinationY_() - curY_;
							if((long)PENALTY_MESSAGE_RADIUS * PENALTY_MESSAGE_RADIUS >= (dx*dx + dy*dy)){
								message.setFloodingMode(true);	// enable flooding mode if within distance!
							}								
							knownMessages_.addMessage(message, true, true);

							++eeblMessagesCreated_;
						}	
						*/
						//emergencyBraking_ = true;
					//}
					
				//}
				
			
			if(isWiFiEnabled() && communicationEnabled_){
				
				if(knownMessages_.hasNewMessages()) {
					
					knownMessages_.processMessages();
				}
				communicationCountdown_ -= timePerStep;
				if(communicationCountdown_ < 1) knownMessages_.checkOutdatedMessages(true);

				knownPenaltiesTimeoutCountdown_ -= timePerStep;
				if(knownPenaltiesTimeoutCountdown_ < 1){
					if(knownPenalties_.getSize() > 0) knownPenalties_.checkValidUntil();
					knownPenaltiesTimeoutCountdown_ += KNOWN_PENALTIES_TIMEOUT_CHECKINTERVAL;
				}

				if(beaconsEnabled_){
					beaconCountdown_ -= timePerStep;

					// recheck known vehicles for outdated entries.
					if(knownVehiclesTimeoutCountdown_ < 1){
						knownVehiclesList_.checkOutdatedVehicles();
						idsProcessorList_.checkOutdatedProcessors();
						knownVehiclesTimeoutCountdown_ += KNOWN_VEHICLES_TIMEOUT_CHECKINTERVAL;
					} else knownVehiclesTimeoutCountdown_ -= timePerStep;
					
					// recheck known RSUs for outdated entries.
					if(knownRSUsTimeoutCountdown_ < 1){
						knownRSUsList_.checkOutdatedRSUs();
						knownRSUsTimeoutCountdown_ += KNOWN_RSUS_TIMEOUT_CHECKINTERVAL;
					} else knownRSUsTimeoutCountdown_ -= timePerStep;
					
				}


				
				// ================================= 
				// Step 7: Check if this vehicle is currently in a traffic jam and should create a message.
				// ================================= 
				//lastMessageCreated += timePerStep;
				lastRHCNMessageCreated += timePerStep;
				lastPCNMessageCreated += timePerStep;
				lastPCNFORWARDMessageCreated += timePerStep;
				lastEVAMessageCreated += timePerStep;
				//lastEVAFORWARDMessageCreated += timePerStep;
				//lastEEBLMessageCreated += timePerStep;
				//lastFAKEMessageCreated += timePerStep;
				if(newSpeed_ == 0){
					stopTime_ += timePerStep;
					
					if(stopTime_ > TIME_FOR_JAM && !waitingForSignal_){
						inTrafficJam_ = true;
						if(lastPCNMessageCreated >= MESSAGE_INTERVAL){
							lastPCNMessageCreated = 0;
							// find the destination for the message. Will be sent to the next junction behind us!
							boolean tmpDirection = curDirection_;
							Street tmpStreet = curStreet_;
							Street[] crossingStreets;
							Node tmpNode;
							int i, j = 0, destX = -1, destY = -1;
							do{
								++j;
								if(tmpDirection){
									tmpNode = tmpStreet.getStartNode();
								} else {
									tmpNode = tmpStreet.getEndNode();
								}
								if(tmpNode.getJunction() != null){
									destX = tmpNode.getX();
									destY = tmpNode.getY();
									break;
								}
								crossingStreets = tmpNode.getCrossingStreets();
								// find next street behind of us
								if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
									destX = tmpNode.getX();
									destY = tmpNode.getY();
									break;
								}
								for(i = 0; i < crossingStreets.length; ++i){
									if(crossingStreets[i] != tmpStreet){
										tmpStreet = crossingStreets[i];
										if(tmpStreet.getStartNode() == tmpNode) tmpDirection = false;
										else tmpDirection = true;
										break;
									}
								}
							} while(tmpStreet != curStreet_ && j < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
							// found destination...now insert into messagequeue
							if(destX != -1 && destY != -1){
								int direction = -1;
								if(!curDirection_) direction = 1;
								int time = Renderer.getInstance().getTimePassed();
								PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, false, ID_, this,  "HUANG_PCN", false, false);
								long dx = message.getDestinationX_() - curX_;
								long dy = message.getDestinationY_() - curY_;
								if((long)PENALTY_MESSAGE_RADIUS * PENALTY_MESSAGE_RADIUS >= (dx*dx + dy*dy)){
									message.setFloodingMode(true);	// enable flooding mode if within distance!
								}								
								knownMessages_.addMessage(message, false, true);

								++pcnMessagesCreated_;
							}							
						}					
					}
				} else {
					inTrafficJam_ = false;
					stopTime_ = 0;
				}


				
				// ================================= 
				// Step 8: Check if vehicle is inside a mix zone and change vehicle ID if entering mix zone
				// ================================= 

				if(mixZonesEnabled_){
					mixCheckCountdown_ -= MIX_CHECK_INTERVAL;
					if(mixCheckCountdown_ <= 0){
						int MapMinX, MapMinY, MapMaxX, MapMaxY, RegionMinX, RegionMinY, RegionMaxX, RegionMaxY;
						int i, j, k, size;
						Node node;
						long dx, dy, mixDistanceSquared = (long)getMaxMixZoneRadius() * getMaxMixZoneRadius();
						boolean needsToMix = false;

						// Minimum x coordinate to be considered
						long tmp = curX_ - getMaxMixZoneRadius();
						if (tmp < 0) MapMinX = 0;	// Map stores only positive coordinates
						else if(tmp < Integer.MAX_VALUE) MapMinX = (int) tmp;
						else MapMinX = Integer.MAX_VALUE;

						// Maximum x coordinate to be considered
						tmp = curX_ + getMaxMixZoneRadius();
						if (tmp < 0) MapMaxX = 0;
						else if(tmp < Integer.MAX_VALUE) MapMaxX = (int) tmp;
						else MapMaxX = Integer.MAX_VALUE;

						// Minimum y coordinate to be considered
						tmp = curY_ - getMaxMixZoneRadius();
						if (tmp < 0) MapMinY = 0;
						else if(tmp < Integer.MAX_VALUE) MapMinY = (int) tmp;
						else MapMinY = Integer.MAX_VALUE;

						// Maximum y coordinate to be considered
						tmp = curY_ + getMaxMixZoneRadius();
						if (tmp < 0) MapMaxY = 0;
						else if(tmp < Integer.MAX_VALUE) MapMaxY = (int) tmp;
						else MapMaxY = Integer.MAX_VALUE;

						// Get the regions to be considered
						Region tmpregion = MAP.getRegionOfPoint(MapMinX, MapMinY);
						RegionMinX = tmpregion.getX();
						RegionMinY = tmpregion.getY();

						tmpregion = MAP.getRegionOfPoint(MapMaxX, MapMaxY);
						RegionMaxX = tmpregion.getX();
						RegionMaxY = tmpregion.getY();

						// only check those regions which are within the radius
						for(i = RegionMinX; i <= RegionMaxX; ++i){
	
							for(j = RegionMinY; j <= RegionMaxY; ++j){
								Node[] mixNodes = regions_[i][j].getMixZoneNodes();
								size = mixNodes.length;
								for(k = 0; k < size; ++k){
									node = mixNodes[k];
									// precheck if the mixing node is near enough and valid (check is not exact as its a rectangular box and not circle)
									if(node.getX() >= curX_ - node.getMixZoneRadius() && node.getX() <= curX_ + node.getMixZoneRadius() && node.getY() >= curY_ - node.getMixZoneRadius() && node.getY() <= curY_ + node.getMixZoneRadius()){
										dx = node.getX() - curX_;
										dy = node.getY() - curY_;

										mixDistanceSquared = node.getMixZoneRadius() * node.getMixZoneRadius();
										if((dx * dx + dy * dy) <= mixDistanceSquared){	// Pythagorean theorem: a^2 + b^2 = c^2 but without the needed Math.sqrt to save a little bit performance
											needsToMix = true;
											curMixNode_ = node;

											//change values to break out of all loops as we don't need to check further!
											i = RegionMaxX;
											j = RegionMaxY;
											k = mixNodes.length -1;									
										}
									} 
								}
							}
						}
						
						if(needsToMix != isInMixZone_){
							if(privacyDataLogged_){
								if(needsToMix) 	PrivacyLogWriter.log(Renderer.getInstance().getTimePassed() + ":Steady ID:" + this.steadyID_ + ":Pseudonym:" + Long.toHexString(this.ID_) + ":TraveledDistance:" + totalTravelDistance_ + ":TraveledTime:" + totalTravelTime_ + ":Node ID:" + curMixNode_.getNodeID() + ":Direction:IN" + ":Street:" + this.getCurStreet().getName() + ":StreetSpeed:" + this.getCurStreet().getSpeed() + ":VehicleSpeed:" + this.getCurSpeed() +  ":x:" + this.curX_ + ":y:" + this.curY_);
								else PrivacyLogWriter.log(Renderer.getInstance().getTimePassed() + ":Steady ID:" + this.steadyID_ + ":Pseudonym:" + Long.toHexString(this.ID_) + ":TraveledDistance:" + totalTravelDistance_ + ":TraveledTime:" + totalTravelTime_ + ":Node ID:" + curMixNode_.getNodeID() + ":Direction:OUT" + ":Street:" + this.getCurStreet().getName() + ":StreetSpeed:" + this.getCurStreet().getSpeed() + ":VehicleSpeed:" + this.getCurSpeed() + ":x:" + this.curX_ + ":y:" + this.curY_);
							}
							if(needsToMix){
								++IDsChanged_;
								ID_ = ownRandom_.nextLong();
							}
							isInMixZone_ = needsToMix;
						}
						if(!needsToMix) curMixNode_ = null;
					}
				}
			
				// ================================= 
				// Step 9: Send fake messages
				// ================================= 
				if(fakingMessages_){
					fakeMessageCountdown_ -= timePerStep;
					if(fakeMessageCountdown_ < 0){
						
						//fake messages
						fakeMessageCountdown_ = fakeMessagesInterval_;
						String messageType = fakeMessageType_;
						if(fakeMessageType_.equals("all") || fakeMessageType_.equals("Alle")) messageType = IDSProcessor.getIdsData_()[ownRandom_.nextInt(fakeMessageTypesCount)];
						
						// find the destination for the message. Will be sent to the next junction behind us! (if its pcn we send it in front)
						boolean tmpDirection2 = curDirection_;
						if(messageType.equals("HUANG_PCN")) tmpDirection2 = !curDirection_;
						
						Street tmpStreet2 = curStreet_;
						Street[] crossingStreets;
						Node tmpNode;
						int k, l = 0, destX = -1, destY = -1;
						do{
							++l;
							if(tmpDirection2){
								tmpNode = tmpStreet2.getStartNode();
							} else {
								tmpNode = tmpStreet2.getEndNode();
							}
							if(tmpNode.getJunction() != null){
								destX = tmpNode.getX();
								destY = tmpNode.getY();
								break;
							}
							crossingStreets = tmpNode.getCrossingStreets();
							// find next street behind of us
							if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
								destX = tmpNode.getX();
								destY = tmpNode.getY();
								break;
							}
							for(k = 0; k < crossingStreets.length; ++k){
								if(crossingStreets[k] != tmpStreet2){
									tmpStreet2 = crossingStreets[k];
									if(tmpStreet2.getStartNode() == tmpNode) tmpDirection2 = false;
									else tmpDirection2 = true;
									break;
								}
							}
						} while(tmpStreet2 != curStreet_ && l < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
						// found destination...now insert into messagequeue
						if(destX != -1 && destY != -1){
							int direction = -1;
							//if(!curDirection_) direction = 1;
							int time = Renderer.getInstance().getTimePassed();
							if(messageType.equals("HUANG_EVA_FORWARD")){
								PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_FAKE_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, true, ID_, this,  messageType, false, true);
								message.setFloodingMode(true);	// enable flooding mode if within distance!				
								knownMessages_.addMessage(message, false, true);
							}
							else if(messageType.equals("EVA_EMERGENCY_ID")){
								if(emergencyBeacons == -1){
									emergencyBeacons = EVAMessageDelay_;
								}
				
							}
							else if(messageType.equals("HUANG_PCN")){
								//if(stopTime_ < 2000){
									//int messageX = 0;
									//int messageY = 0;
									//send message in front and only if vehicle has a street to drive left!
									/*
									if((routeStreets_.length - routePosition_) > 1){
										if(routeDirections_[(routePosition_+1)]){
											messageX = routeStreets_[(routePosition_+1)].getEndNode().getX();
											messageY = routeStreets_[(routePosition_+1)].getEndNode().getY();
										}
										else{
											messageX = routeStreets_[(routePosition_+1)].getStartNode().getX();
											messageY = routeStreets_[(routePosition_+1)].getStartNode().getY();
										}
										*/
	
										PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_FAKE_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, true, ID_, this, messageType, false, true);

										long dx = message.getDestinationX_() - curX_;
										long dy = message.getDestinationY_() - curY_;
										if((long)PENALTY_MESSAGE_RADIUS * PENALTY_MESSAGE_RADIUS >= (dx*dx + dy*dy)){
											message.setFloodingMode(true);	// enable flooding mode if within distance!
										}
										knownMessages_.addMessage(message, false, true);
									//}
								//}
							}
							//else if(messageType.equals("PCN_FORWARD")){}
							else{
								PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_FAKE_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, true, ID_, this, messageType, false, true);

								long dx = message.getDestinationX_() - curX_;
								long dy = message.getDestinationY_() - curY_;
								if((long)PENALTY_MESSAGE_RADIUS * PENALTY_MESSAGE_RADIUS >= (dx*dx + dy*dy)){
									message.setFloodingMode(true);	// enable flooding mode if within distance!
								}
							    knownMessages_.addMessage(message, false, true);

							}
					//		System.out.println(time + ":fake message created: " + messageType);
						}		
						++fakeMessagesCreated_;
					
						fakeMessageCounter_ = fakeMessageCounter_%fakeMessageTypesCount;
					}
				}
			}
		}			
	}
/*
	public void test(int timePerStep){
		if(knownMessages_.hasNewMessages()) {
			messageSendCounter_++;
			knownMessages_.processMessages();
		}
	
		/*
		knownPenaltiesTimeoutCountdown_ -= timePerStep;
		if(knownPenaltiesTimeoutCountdown_ < 1){
			if(knownPenalties_.getSize() > 0) knownPenalties_.checkValidUntil();
			knownPenaltiesTimeoutCountdown_ += KNOWN_PENALTIES_TIMEOUT_CHECKINTERVAL;
		}

		if(beaconsEnabled_){
			beaconCountdown_ -= timePerStep;

			// recheck known vehicles for outdated entries.
			if(knownVehiclesTimeoutCountdown_ < 1){
				knownVehiclesList_.checkOutdatedVehicles();
				idsProcessorList_.checkOutdatedProcessors();
				knownVehiclesTimeoutCountdown_ += KNOWN_VEHICLES_TIMEOUT_CHECKINTERVAL;
			} else knownVehiclesTimeoutCountdown_ -= timePerStep;
			
			// recheck known RSUs for outdated entries.
			if(knownRSUsTimeoutCountdown_ < 1){
				knownRSUsList_.checkOutdatedRSUs();
				knownRSUsTimeoutCountdown_ += KNOWN_RSUS_TIMEOUT_CHECKINTERVAL;
			} else knownRSUsTimeoutCountdown_ -= timePerStep;
			
		}
		
	}
	
	public void testa(int timePerStep){
		communicationCountdown_ -= timePerStep;
		if(communicationCountdown_ < 1) knownMessages_.checkOutdatedMessages(true);

		/*
		knownPenaltiesTimeoutCountdown_ -= timePerStep;
		if(knownPenaltiesTimeoutCountdown_ < 1){
			if(knownPenalties_.getSize() > 0) knownPenalties_.checkValidUntil();
			knownPenaltiesTimeoutCountdown_ += KNOWN_PENALTIES_TIMEOUT_CHECKINTERVAL;
		}

		if(beaconsEnabled_){
			beaconCountdown_ -= timePerStep;

			// recheck known vehicles for outdated entries.
			if(knownVehiclesTimeoutCountdown_ < 1){
				knownVehiclesList_.checkOutdatedVehicles();
				idsProcessorList_.checkOutdatedProcessors();
				knownVehiclesTimeoutCountdown_ += KNOWN_VEHICLES_TIMEOUT_CHECKINTERVAL;
			} else knownVehiclesTimeoutCountdown_ -= timePerStep;
			
			// recheck known RSUs for outdated entries.
			if(knownRSUsTimeoutCountdown_ < 1){
				knownRSUsList_.checkOutdatedRSUs();
				knownRSUsTimeoutCountdown_ += KNOWN_RSUS_TIMEOUT_CHECKINTERVAL;
			} else knownRSUsTimeoutCountdown_ -= timePerStep;
			
		}
		
	}
	
	public void test2(int timePerStep){
		/*
		if(knownMessages_.hasNewMessages()) {
			messageSendCounter_++;
			knownMessages_.processMessages();
		}
		communicationCountdown_ -= timePerStep;
		if(communicationCountdown_ < 1) knownMessages_.checkOutdatedMessages(true);

		
		knownPenaltiesTimeoutCountdown_ -= timePerStep;
		if(knownPenaltiesTimeoutCountdown_ < 1){
			if(knownPenalties_.getSize() > 0) knownPenalties_.checkValidUntil();
			knownPenaltiesTimeoutCountdown_ += KNOWN_PENALTIES_TIMEOUT_CHECKINTERVAL;
		}
/*
		if(beaconsEnabled_){
			beaconCountdown_ -= timePerStep;

			// recheck known vehicles for outdated entries.
			if(knownVehiclesTimeoutCountdown_ < 1){
				knownVehiclesList_.checkOutdatedVehicles();
				idsProcessorList_.checkOutdatedProcessors();
				knownVehiclesTimeoutCountdown_ += KNOWN_VEHICLES_TIMEOUT_CHECKINTERVAL;
			} else knownVehiclesTimeoutCountdown_ -= timePerStep;
			
			// recheck known RSUs for outdated entries.
			if(knownRSUsTimeoutCountdown_ < 1){
				knownRSUsList_.checkOutdatedRSUs();
				knownRSUsTimeoutCountdown_ += KNOWN_RSUS_TIMEOUT_CHECKINTERVAL;
			} else knownRSUsTimeoutCountdown_ -= timePerStep;
			
		}
		
	}
	
	public void test3(int timePerStep){
		/*
		if(knownMessages_.hasNewMessages()) {
			messageSendCounter_++;
			knownMessages_.processMessages();
		}
		communicationCountdown_ -= timePerStep;
		if(communicationCountdown_ < 1) knownMessages_.checkOutdatedMessages(true);

		
		knownPenaltiesTimeoutCountdown_ -= timePerStep;
		if(knownPenaltiesTimeoutCountdown_ < 1){
			if(knownPenalties_.getSize() > 0) knownPenalties_.checkValidUntil();
			knownPenaltiesTimeoutCountdown_ += KNOWN_PENALTIES_TIMEOUT_CHECKINTERVAL;
		}

		if(beaconsEnabled_){
			beaconCountdown_ -= timePerStep;

			// recheck known vehicles for outdated entries.
			if(knownVehiclesTimeoutCountdown_ < 1){
				knownVehiclesList_.checkOutdatedVehicles();
				idsProcessorList_.checkOutdatedProcessors();
				knownVehiclesTimeoutCountdown_ += KNOWN_VEHICLES_TIMEOUT_CHECKINTERVAL;
			} else knownVehiclesTimeoutCountdown_ -= timePerStep;
			
			// recheck known RSUs for outdated entries.
			if(knownRSUsTimeoutCountdown_ < 1){
				knownRSUsList_.checkOutdatedRSUs();
				knownRSUsTimeoutCountdown_ += KNOWN_RSUS_TIMEOUT_CHECKINTERVAL;
			} else knownRSUsTimeoutCountdown_ -= timePerStep;
			
		}
		
	}
	*/
	//Still in development, commented code is needed!
	/**
	 * helper class to develope politness factor
	 */
	@SuppressWarnings("unused")
	private final boolean checkPolitness(int lane){
		/*
		//Noch auszubessern: in checkLaneFree() die neededDistance wieder zur�cksetzen
		boolean vehicleInFront = false;
		boolean vehicleInFront2 = false;
		boolean vehicleBehind = false;
		boolean vehicleBehind2 = false;	
		Vehicle b = null, b2 = null, f = null, f2 = null;
		
		System.out.println("**************");
		//fahrzeug das jetzt vor uns ist:
		if(next_ != null){
			if(next_.getCurLane() == lane){	// next one is on the same lane
				//System.out.println("Fahrzeug vor uns: (1.Versuch) " + next_.curPosition_);
				if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)next_).setColor(Color.cyan);
				f = (Vehicle)next_;
				vehicleInFront = true;
			} else {	// need to search for the next which is on our lane
				LaneObject tmpLaneObject = next_.getNext();
				while(tmpLaneObject != null){
					if(tmpLaneObject.getCurLane() == lane){
					//	System.out.println("Fahrzeug vor uns: (2. Versuch)" + tmpLaneObject.curPosition_);
						if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)tmpLaneObject).setColor(Color.cyan);
						f = (Vehicle)tmpLaneObject;
						vehicleInFront = true;
						break;	// only check the first on our lane!
					}
					tmpLaneObject = tmpLaneObject.getNext();
				}
			}
		}
		
		if(!vehicleInFront){
			for(int i = routePosition_; i < routeStreets_.length; i++){
				boolean tmpDirection = routeDirections_[i];
				Street tmpStreet = routeStreets_[i];
				
				LaneObject tmpLaneObject = tmpStreet.getFirstLaneObject(tmpDirection);
				
				while(tmpLaneObject != null){
					if(tmpLaneObject.getCurLane() == lane){
						//System.out.println("Fahrzeug vor uns: (3. Versuch)" + tmpLaneObject.curPosition_);
						if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)tmpLaneObject).setColor(Color.cyan);
						f = (Vehicle) tmpLaneObject;
						i=routeStreets_.length;
						break;
					}
					
					tmpLaneObject = tmpLaneObject.getNext();
				}			
			}
		}
		
		// fahrzeug das dann vor uns ist:
		if(next_ != null){
			if(next_.getCurLane() == lane+1){	// next one is on the same lane
				//System.out.println("!!!Fahrzeug dann vor uns: (1.Versuch) " + next_.curPosition_);
				if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)next_).setColor(Color.cyan);
				f2 = (Vehicle)next_;
				vehicleInFront2 = true;
			} else {	// need to search for the next which is on our lane
				LaneObject tmpLaneObject = next_.getNext();
				while(tmpLaneObject != null){
					if(tmpLaneObject.getCurLane() == lane+1){
						//System.out.println("!!!Fahrzeug dann vor uns: (2. Versuch)" + tmpLaneObject.curPosition_);
						if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)tmpLaneObject).setColor(Color.cyan);
						f2 = (Vehicle)tmpLaneObject;
						vehicleInFront2 = true;
						break;	// only check the first on our lane!
					}
					tmpLaneObject = tmpLaneObject.getNext();
				}
			}
		}
		
		if(!vehicleInFront2){
			for(int i = routePosition_; i < routeStreets_.length; i++){
				boolean tmpDirection = routeDirections_[i];
				Street tmpStreet = routeStreets_[i];
				
				LaneObject tmpLaneObject = tmpStreet.getFirstLaneObject(tmpDirection);
				
				while(tmpLaneObject != null){

					if(tmpLaneObject.getCurLane() == lane+1){
						//System.out.println("!!!Fahrzeug dann vor uns: (3. Versuch)" + tmpLaneObject.curPosition_);
						if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)tmpLaneObject).setColor(Color.cyan);
						f2 = (Vehicle) tmpLaneObject;
						i = routeStreets_.length;
						break;
					}
					
					tmpLaneObject = tmpLaneObject.getNext();
				}			
			}
		}
		

		// ================================= 
		// Step 2: Check space behind
		// ================================= 

		if(previous_ == null)System.out.println("ist null");
		// check the lane object behind us (on our street)
		if(previous_ != null){
			
			if(previous_.getCurLane() == lane){	// is on the same lane
				vehicleBehind = true;
				//System.out.println("Fahrzeug hinter uns: (1. Versuch)" + previous_.curPosition_);
				if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)previous_).setColor(Color.cyan);
				b = (Vehicle)previous_;
			} else {	// need to search for the previous one which is on our lane
				LaneObject tmpLaneObject = previous_.getPrevious();
				while(tmpLaneObject != null){
					if(tmpLaneObject.getCurLane() == lane){
						vehicleBehind = true;
					//	System.out.println("Fahrzeug hinter uns: (2. Versuch)" + previous_.curPosition_);
						if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)tmpLaneObject).setColor(Color.cyan);
						b = (Vehicle)tmpLaneObject;
						break;	// only check the first on our lane!
					}
					tmpLaneObject = tmpLaneObject.getPrevious();
				}
			}
		}
		
		if(!vehicleBehind){
			Street[] outgoingStreets;
			Street tmpStreet = curStreet_, tmpStreet2;
			LaneObject tmpLaneObject;
			Node nextNode = null;
			boolean tmpDirection = curDirection_;
			int i;
			// check previous streets. We can't use the routing here so it's limited to streets with no junctions which makes it a bit simpler.
			int counter = 0;
			while(counter < 2){
				if(tmpDirection) nextNode = tmpStreet.getStartNode();
				else nextNode = tmpStreet.getEndNode();
				if(nextNode.getCrossingStreetsCount() != 2) break;		// don't handle junctions!
				else outgoingStreets = nextNode.getCrossingStreets();
				for(i = 0; i < outgoingStreets.length; ++i){
					tmpStreet2 = outgoingStreets[i];
					if(tmpStreet2 != tmpStreet){
						tmpStreet = tmpStreet2;
						if(lane > tmpStreet.getLanesCount()) break;
						if (tmpStreet2.getStartNode() == nextNode){
							tmpDirection = true;
							break;		// found street we want to => no need to look through others
						} else {
							tmpDirection = false;
							break;		// found street we want to => no need to look through others
						}
					}					
				}				

				if(!vehicleBehind){
					tmpLaneObject = tmpStreet.getFirstLaneObject(!tmpDirection);
					while(tmpLaneObject != null){
						if(tmpLaneObject.getCurLane() == lane && !tmpLaneObject.equals(this)){
						//	System.out.println("Fahrzeug hinter uns: (3. Versuch)" + tmpLaneObject.curPosition_);
							if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)tmpLaneObject).setColor(Color.cyan);
							b = (Vehicle)tmpLaneObject;
							counter = 3;
							break;
						}
						tmpLaneObject = tmpLaneObject.getNext();
					}
				}
				counter++;
			}
		}
		
		
*/
		boolean vehicleBehind2 = false;	
		Vehicle b2 = null; 
		double distance = 0;
		
		// check the lane object then before us (on our street)
		if(previous_ != null){
			if(previous_.getCurLane() == lane+1){	// is on the same lane
				vehicleBehind2 = true;
			//	System.out.println("!!!Fahrzeug dann hinter uns: (1. Versuch)" + previous_.curPosition_);
				if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)previous_).setColor(Color.cyan);
				b2 = (Vehicle)previous_;
			} else {	// need to search for the previous one which is on our lane
				LaneObject tmpLaneObject = previous_.getPrevious();
				while(tmpLaneObject != null){
					if(tmpLaneObject.getCurLane() == lane+1){
						vehicleBehind2 = true;
					//	System.out.println("!!!Fahrzeug dann hinter uns: (2. Versuch)" + previous_.curPosition_);
						if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)tmpLaneObject).setColor(Color.cyan);
						b2 = (Vehicle)tmpLaneObject;
						break;	// only check the first on our lane!
					}
					tmpLaneObject = tmpLaneObject.getPrevious();
				}
			}
		}
		if(vehicleBehind2){
			distance = Math.abs(curPosition_ - b2.curPosition_);
		}
		else{
			if(curDirection_) distance = curPosition_;
			else distance = curStreet_.getLength() - curPosition_;
		}
	
	//	System.out.println("distance: " + distance);
		
		if(!vehicleBehind2){
			Street[] outgoingStreets;
			Street tmpStreet = curStreet_, tmpStreet2;
			LaneObject tmpLaneObject;
			Node nextNode = null;
			boolean tmpDirection = curDirection_;
			int i;
			// check previous streets. We can't use the routing here so it's limited to streets with no junctions which makes it a bit simpler.
			int counter = 0;
			
			while(counter < 3){
				//System.out.println("counter: " + counter);
				if(tmpDirection) nextNode = tmpStreet.getStartNode();
				else nextNode = tmpStreet.getEndNode();
				if(nextNode.getCrossingStreetsCount() != 2) break;		// don't handle junctions!
				else outgoingStreets = nextNode.getCrossingStreets();
				for(i = 0; i < outgoingStreets.length; ++i){
					tmpStreet2 = outgoingStreets[i];
					if(tmpStreet2 != tmpStreet){
						tmpStreet = tmpStreet2;
						if(lane+1 > tmpStreet.getLanesCount()) break;
						if (tmpStreet2.getStartNode() == nextNode){
							tmpDirection = true;
							break;		// found street we want to => no need to look through others
						} else {
							tmpDirection = false;
							break;		// found street we want to => no need to look through others
						}
					}					
				}				

				if(!vehicleBehind2){
					tmpLaneObject = tmpStreet.getFirstLaneObject(!tmpDirection);
					while(tmpLaneObject != null){
						if(tmpLaneObject.getCurLane() == lane + 1 && !tmpLaneObject.equals(this)){
						//	System.out.println("!!!Fahrzeug dann hinter uns: (3. Versuch)" + tmpLaneObject.curPosition_);
							counter = 3;
							if(tmpDirection) distance =  tmpStreet.getLength()-tmpLaneObject.getCurPosition()+distance;
							else distance = tmpLaneObject.getCurPosition()+distance;
							
							if(this.equals(Renderer.getInstance().getMarkedVehicle())) ((Vehicle)tmpLaneObject).setColor(Color.cyan);
							b2 = (Vehicle)tmpLaneObject;

							break;
						}
						tmpLaneObject = tmpLaneObject.getNext();
					}
				}
				counter ++;
				if(counter < 3)distance += tmpStreet.getLength();
			}
		}

		if(b2 != null){
			if(curSpeed_ >= b2.curSpeed_){
				//System.out.println("hinterer langsamer");
				return true;
			}
			float t = (float) ((b2.curSpeed_ - curSpeed_)/accelerationRate_);
			//System.out.println("distanz ben�tigt: " + b2.curSpeed_ * t);
		//	System.out.println("distanz vorhanden: " + (distance-b2.curBrakingDistance_));
			//System.out.println("braking distance" + b2.curBrakingDistance_);
			if((distance-b2.curBrakingDistance_) > ((politeness_/100) * (b2.curSpeed_*t))) return true;
			else return false;

		}
		else return true;
		//code need, still developing
		/*
		float t = (float) ((maxSpeed_ - curSpeed_)/accelerationRate_);
		if(b2 != null){
			//if(b2.curPosition_ - curPosition_)
			System.out.println(b2.curSpeed_ * t);
		}
		
		System.out.println("**************");
		*/
		/*
		boolean foundNextVehicle = false;
		// check the lane object in front of us (on our street). This is separated from the loop beneath as this is done most of the time!
		if(next_ != null){
			if(next_.getCurLane() == lane){	// next one is on the same lane
				foundNextVehicle = true;
				if((curDirection_ && next_.getCurPosition()-curPosition_ < curBrakingDistance_) || (!curDirection_ && curPosition_-next_.getCurPosition() < curBrakingDistance_)){
					if(curSpeed_ > next_.getCurSpeed()-brakingRate_) return 1;
				}
			} else {	// need to search for the next which is on our lane
				LaneObject tmpLaneObject = next_.getNext();
				while(tmpLaneObject != null){
					if(tmpLaneObject.getCurLane() == lane){
						foundNextVehicle = true;
						if((curDirection_ && tmpLaneObject.getCurPosition()-curPosition_ < curBrakingDistance_) || (!curDirection_ && curPosition_-tmpLaneObject.getCurPosition() < curBrakingDistance_)){
							if(curSpeed_ > next_.getCurSpeed()-brakingRate_) return 1;
						}
						break;	// only check the first on our lane!
					}
					tmpLaneObject = tmpLaneObject.getNext();
				}
			}
		}
		// didn't need to brake because of vehicle directly in front of us
		double distance;
		if(curDirection_) distance = curStreet_.getLength() - curPosition_;
		else distance = curPosition_;
		// only do the big calculation if the current street is empty AND the remainder of the current street is shorter than the braking distance
		if(distance < curBrakingDistance_){
			Street tmpStreet = curStreet_;
			LaneObject tmpLaneObject;
			Node junctionNode, nextNode;
			boolean tmpDirection = curDirection_;
			boolean gotJunctionPermission = false;
			int tmpLane = lane;
			int i;
			int j = routeStreets_.length-1;

			// check next streets that we will visit. If routing is empty this automatically skips (which is what we actually want)
			for(i = routePosition_; i < j;){
				//check for junctions
				if(tmpDirection) junctionNode = tmpStreet.getEndNode();
				else junctionNode = tmpStreet.getStartNode();
				if(junctionNode.getJunction() != null){
					if(junctionAllowed_ != junctionNode){
						if(routeDirections_[i+1]) nextNode = routeStreets_[i+1].getEndNode();
						else nextNode = routeStreets_[i+1].getStartNode();
						if(junctionNode.isHasTrafficSignal_()){
							if(junctionNode.getJunction().canPassTrafficLight(this, tmpStreet, nextNode)){	
								junctionAllowed_ = junctionNode;
								gotJunctionPermission = true;
							}
							else {
								waitingForSignal_ = true;
								return 2;
							}
						}
						else{
							int priority;
							if(tmpDirection) priority = junctionNode.getJunction().getJunctionPriority(tmpStreet.getStartNode(), nextNode);
							else priority = junctionNode.getJunction().getJunctionPriority(tmpStreet.getEndNode(), nextNode);
							if(priority != 1){	// don't do anything on priority streets
								// don't turn off faster than about 35km/h
								if(curSpeed_ > 1000){
									return 2;
								} else if(priority > 2){
									junctionNode.getJunction().addWaitingVehicle(this, priority);
									if(!junctionNode.getJunction().canPassJunction(this, priority, nextNode)) return 2;
									else {
										junctionAllowed_ = junctionNode;
										gotJunctionPermission = true;
									}
								}
							}
						}
					}
				}
				//get next street
				++i;
				tmpDirection = routeDirections_[i];
				tmpStreet = routeStreets_[i];
				if(tmpLane > tmpStreet.getLanesCount()) tmpLane = tmpStreet.getLanesCount();

				// Check if next street has smaller speed limit
				if(tmpStreet.getSpeed() < curSpeed_) {
					if(gotJunctionPermission) {
						junctionAllowed_.getJunction().allowOtherVehicle();
						junctionAllowed_ = null;
					}					
					return 2;
				}

				// Check if first lane object of next street on our lane forces us to stop
				if(!foundNextVehicle){
					tmpLaneObject = tmpStreet.getFirstLaneObject(tmpDirection);
					while(tmpLaneObject != null){
						if(tmpLaneObject.getCurLane() == tmpLane){
							foundNextVehicle = true;
							if((tmpDirection && tmpLaneObject.getCurPosition()+distance < curBrakingDistance_) || (!tmpDirection && tmpStreet.getLength()-tmpLaneObject.getCurPosition()+distance < curBrakingDistance_)){
								if(curSpeed_ > tmpLaneObject.getCurSpeed()-brakingRate_){
									if(gotJunctionPermission) {
										junctionAllowed_.getJunction().allowOtherVehicle();
										junctionAllowed_ = null;
									}	
									return 1;
								}
							}
							break;
						}
						tmpLaneObject = tmpLaneObject.getNext();
					}
				}

				// calculate distance of the whole street.
				distance += tmpStreet.getLength();
				// We can stop processing the next one if we get longer than braking distance
				if(distance > curBrakingDistance_) break;
				if(tmpStreet == destinations_.peekFirst().getStreet()) break;

			}
		}
		return 0;
		*/
	}

	
	/**
	 * Check if braking is necessary on the specified lane.
	 * 
	 * @param lane	the lane to check
	 * 
	 * @return <code>0</code> if braking is not necessary, <code>1</code> if braking is necessary because of an object on a lane,
	 * 	<code>2</code> if braking is necessary because of a lower speed street or junction
	 */
	private final int checkCurrentBraking(int lane){
		boolean foundNextVehicle = false;
	
				
		// check the lane object in front of us (on our street). This is separated from the loop beneath as this is done most of the time!
		if(next_ != null){
			if(next_.getCurLane() == lane){	// next one is on the same lane
				foundNextVehicle = true;
				if((curDirection_ && next_.getCurPosition()-curPosition_ < curBrakingDistance_) || (!curDirection_ && curPosition_-next_.getCurPosition() < curBrakingDistance_)){
					//if(curSpeed_ > 3600 && knownPenalties_.streetIsBlocked(curStreet_, 3))return 1;
					if(curSpeed_ > next_.getCurSpeed()-brakingRate_){
						if(!next_.getClass().equals(BlockingObject.class)){
							if(emergencyVehicle_){
								if(lastEVAMessageCreated >= MESSAGE_INTERVAL){
									lastEVAMessageCreated = 0;
									// find the destination for the message. Will be sent to the next junction in FRONT of us!
									boolean tmpDirection2 = !curDirection_;
									Street tmpStreet2 = curStreet_;
									Street[] crossingStreets;
									Node tmpNode;
									int k, l = 0, destX = -1, destY = -1;
									do{
										++l;
										if(tmpDirection2){
											tmpNode = tmpStreet2.getStartNode();
										} else {
											tmpNode = tmpStreet2.getEndNode();
										}
										if(tmpNode.getJunction() != null){
											destX = tmpNode.getX();
											destY = tmpNode.getY();
											break;
										}
										crossingStreets = tmpNode.getCrossingStreets();
										// find next street behind of us
										if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
											destX = tmpNode.getX();
											destY = tmpNode.getY();
											break;
										}
										for(k = 0; k < crossingStreets.length; ++k){
											if(crossingStreets[k] != tmpStreet2){
												tmpStreet2 = crossingStreets[k];
												if(tmpStreet2.getStartNode() == tmpNode) tmpDirection2 = false;
												else tmpDirection2 = true;
												break;
											}
										}
									} while(tmpStreet2 != curStreet_ && l < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
									// found destination...now insert into messagequeue
									if(destX != -1 && destY != -1){
										int direction = -1;
										if(!curDirection_) direction = 1;
										int time = Renderer.getInstance().getTimePassed();
										PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_EVA_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, false, ID_, this, "EVA_EMERGENCY_ID", true, true);
										message.setFloodingMode(true);	// enable flooding mode if within distance!						
										knownMessages_.addMessage(message, false, true);
										
										++evaMessagesCreated_;
									}							
								}	

							}
							else if(((Vehicle)next_).isInTrafficJam_()  && !((Vehicle)next_).waitingForSignal_ && curSpeed_ > 0){
								
								if(lastPCNFORWARDMessageCreated >= MESSAGE_INTERVAL){
									lastPCNFORWARDMessageCreated = 0;
									// find the destination for the message. Will be sent to the next junction behind us!
									boolean tmpDirection = curDirection_;
									Street tmpStreet = curStreet_;
									Street[] crossingStreets;
									Node tmpNode;
									int i, j = 0, destX = -1, destY = -1;
									do{
										++j;
										if(tmpDirection){
											tmpNode = tmpStreet.getStartNode();
										} else {
											tmpNode = tmpStreet.getEndNode();
										}
										if(tmpNode.getJunction() != null){
											destX = tmpNode.getX();
											destY = tmpNode.getY();
											break;
										}
										crossingStreets = tmpNode.getCrossingStreets();
										// find next street behind of us
										if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
											destX = tmpNode.getX();
											destY = tmpNode.getY();
											break;
										}
										for(i = 0; i < crossingStreets.length; ++i){
											if(crossingStreets[i] != tmpStreet){
												tmpStreet = crossingStreets[i];
												if(tmpStreet.getStartNode() == tmpNode) tmpDirection = false;
												else tmpDirection = true;
												break;
											}
										}
									} while(tmpStreet != curStreet_ && j < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
									// found destination...now insert into messagequeue
									if(destX != -1 && destY != -1){
										int direction = -1;
										if(!curDirection_) direction = 1;
										int time = Renderer.getInstance().getTimePassed();
										PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, false, ID_, null,  "PCN_FORWARD", false, false);
										long dx = message.getDestinationX_() - curX_;
										long dy = message.getDestinationY_() - curY_;
										if((long)PENALTY_MESSAGE_RADIUS * PENALTY_MESSAGE_RADIUS >= (dx*dx + dy*dy)){
											message.setFloodingMode(true);	// enable flooding mode if within distance!
										}									
										knownMessages_.addMessage(message, false, true);

										++pcnForwardMessagesCreated_;
									}	
														
								}
							}
								
							return 1;
						}
						else if(((BlockingObject) next_).getPenaltyType_().equals("HUANG_RHCN") &&  curSpeed_ < 360) return 0;
						else {
							passingBlocking_ = true;
							if(((BlockingObject) next_).getPenaltyType_().equals("HUANG_RHCN"))	{
								if(lastRHCNMessageCreated >= MESSAGE_INTERVAL){
									if(waitToSendRHCNCounter_ < 0)waitToSendRHCNCounter_ = WAIT_TO_SEND_RHCN_;
									else if(waitToSendRHCNCounter_ > 0) waitToSendRHCNCounter_--;
									else{	
										waitToSendRHCNCounter_ = -1;	
										
									
										lastRHCNMessageCreated = 0;
										// find the destination for the message. Will be sent to the next junction behind us!
										boolean tmpDirection2 = curDirection_;
										Street tmpStreet2 = curStreet_;
										Street[] crossingStreets;
										Node tmpNode;
										int k, l = 0, destX = -1, destY = -1;
										do{
											++l;
											if(tmpDirection2){
												tmpNode = tmpStreet2.getStartNode();
											} else {
												tmpNode = tmpStreet2.getEndNode();
											}
											if(tmpNode.getJunction() != null){
												destX = tmpNode.getX();
												destY = tmpNode.getY();
												break;
											}
											crossingStreets = tmpNode.getCrossingStreets();
											// find next street behind of us
											if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
												destX = tmpNode.getX();
												destY = tmpNode.getY();
												break;
											}
											for(k = 0; k < crossingStreets.length; ++k){
												if(crossingStreets[k] != tmpStreet2){
													tmpStreet2 = crossingStreets[k];
													if(tmpStreet2.getStartNode() == tmpNode) tmpDirection2 = false;
													else tmpDirection2 = true;
													break;
												}
											}
										} while(tmpStreet2 != curStreet_ && l < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
										// found destination...now insert into messagequeue
										if(destX != -1 && destY != -1){
											int direction = -1;
											if(!curDirection_) direction = 1;
											int time = Renderer.getInstance().getTimePassed();
											PenaltyMessage message = new PenaltyMessage(((BlockingObject) next_).getX(), ((BlockingObject) next_).getY(), destX, destY, PENALTY_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, false, ID_, this, "HUANG_RHCN", false, true);
											long dx = message.getDestinationX_() - curX_;
											long dy = message.getDestinationY_() - curY_;
											if((long)PENALTY_MESSAGE_RADIUS * PENALTY_MESSAGE_RADIUS >= (dx*dx + dy*dy)){
												message.setFloodingMode(true);	// enable flooding mode if within distance!
											}								
											knownMessages_.addMessage(message, false, true);

											++rhcnMessagesCreated_;
										}	
									}						
								}								
							}
							return 1;
						}
					}	
					

				}
			} else {	// need to search for the next which is on our lane
				LaneObject tmpLaneObject = next_.getNext();
				while(tmpLaneObject != null){
					if(tmpLaneObject.getCurLane() == lane){
						foundNextVehicle = true;
						if((curDirection_ && tmpLaneObject.getCurPosition()-curPosition_ < curBrakingDistance_) || (!curDirection_ && curPosition_-tmpLaneObject.getCurPosition() < curBrakingDistance_)){
							if(curSpeed_ > next_.getCurSpeed()-brakingRate_){
								if(!next_.getClass().equals(BlockingObject.class)){
									if(emergencyVehicle_){
										if(lastEVAMessageCreated >= MESSAGE_INTERVAL){
											lastEVAMessageCreated = 0;
											// find the destination for the message. Will be sent to the next junction in FRONT of us!
											boolean tmpDirection2 = !curDirection_;
											Street tmpStreet2 = curStreet_;
											Street[] crossingStreets;
											Node tmpNode;
											int k, l = 0, destX = -1, destY = -1;
											do{
												++l;
												if(tmpDirection2){
													tmpNode = tmpStreet2.getStartNode();
												} else {
													tmpNode = tmpStreet2.getEndNode();
												}
												if(tmpNode.getJunction() != null){
													destX = tmpNode.getX();
													destY = tmpNode.getY();
													break;
												}
												crossingStreets = tmpNode.getCrossingStreets();
												// find next street behind of us
												if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
													destX = tmpNode.getX();
													destY = tmpNode.getY();
													break;
												}
												for(k = 0; k < crossingStreets.length; ++k){
													if(crossingStreets[k] != tmpStreet2){
														tmpStreet2 = crossingStreets[k];
														if(tmpStreet2.getStartNode() == tmpNode) tmpDirection2 = false;
														else tmpDirection2 = true;
														break;
													}
												}
											} while(tmpStreet2 != curStreet_ && l < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
											// found destination...now insert into messagequeue
											if(destX != -1 && destY != -1){
												int direction = -1;
												if(!curDirection_) direction = 1;
												int time = Renderer.getInstance().getTimePassed();
												
												PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_EVA_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, false, ID_, this, "EVA_EMERGENCY_ID", true, true);
												message.setFloodingMode(true);	// enable flooding mode if within distance!								
												knownMessages_.addMessage(message, false, true);
										

												++evaMessagesCreated_;
											}							
										}	
									}
									else if(((Vehicle)next_).isInTrafficJam_() && !((Vehicle)next_).waitingForSignal_ && curSpeed_ > 0){
										
										if(lastPCNFORWARDMessageCreated >= MESSAGE_INTERVAL){
											lastPCNFORWARDMessageCreated = 0;
											// find the destination for the message. Will be sent to the next junction behind us!
											boolean tmpDirection = curDirection_;
											Street tmpStreet = curStreet_;
											Street[] crossingStreets;
											Node tmpNode;
											int i, j = 0, destX = -1, destY = -1;
											do{
												++j;
												if(tmpDirection){
													tmpNode = tmpStreet.getStartNode();
												} else {
													tmpNode = tmpStreet.getEndNode();
												}
												if(tmpNode.getJunction() != null){
													destX = tmpNode.getX();
													destY = tmpNode.getY();
													break;
												}
												crossingStreets = tmpNode.getCrossingStreets();
												// find next street behind of us
												if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
													destX = tmpNode.getX();
													destY = tmpNode.getY();
													break;
												}
												for(i = 0; i < crossingStreets.length; ++i){
													if(crossingStreets[i] != tmpStreet){
														tmpStreet = crossingStreets[i];
														if(tmpStreet.getStartNode() == tmpNode) tmpDirection = false;
														else tmpDirection = true;
														break;
													}
												}
											} while(tmpStreet != curStreet_ && j < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
											// found destination...now insert into messagequeue
											if(destX != -1 && destY != -1){
												int direction = -1;
												if(!curDirection_) direction = 1;
												int time = Renderer.getInstance().getTimePassed();
												PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, false, ID_, this,  "PCN_FORWARD", false, false);
												long dx = message.getDestinationX_() - curX_;
												long dy = message.getDestinationY_() - curY_;
												if((long)PENALTY_MESSAGE_RADIUS * PENALTY_MESSAGE_RADIUS >= (dx*dx + dy*dy)){
													message.setFloodingMode(true);	// enable flooding mode if within distance!
												}							
												knownMessages_.addMessage(message, false, true);

												++pcnForwardMessagesCreated_;
											}															
										}
									}
									return 1;
								}
								else if(((BlockingObject) next_).getPenaltyType_().equals("HUANG_RHCN") &&  curSpeed_ < 360 ) return 0;
								else{
									//for(StartBlocking blocks:EventList.getInstance().getCurrentBlockingsArrayList()) if(blocks.getStreet().equals(curStreet_) && blocks.isFirst_() && blocks.getPenaltyType_().equals("HUANG_RHCN")){
										//blocks.setFirst_(false);
									passingBlocking_ = true;
									if(((BlockingObject) next_).getPenaltyType_().equals("HUANG_RHCN"))	{	
										if(lastRHCNMessageCreated >= MESSAGE_INTERVAL){
											lastRHCNMessageCreated = 0;
											// find the destination for the message. Will be sent to the next junction behind us!
											boolean tmpDirection2 = curDirection_;
											Street tmpStreet2 = curStreet_;
											Street[] crossingStreets;
											Node tmpNode;
											int k, l = 0, destX = -1, destY = -1;
											do{
												++l;
												if(tmpDirection2){
													tmpNode = tmpStreet2.getStartNode();
												} else {
													tmpNode = tmpStreet2.getEndNode();
												}
												if(tmpNode.getJunction() != null){
													destX = tmpNode.getX();
													destY = tmpNode.getY();
													break;
												}
												crossingStreets = tmpNode.getCrossingStreets();
												// find next street behind of us
												if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
													destX = tmpNode.getX();
													destY = tmpNode.getY();
													break;
												}
												for(k = 0; k < crossingStreets.length; ++k){
													if(crossingStreets[k] != tmpStreet2){
														tmpStreet2 = crossingStreets[k];
														if(tmpStreet2.getStartNode() == tmpNode) tmpDirection2 = false;
														else tmpDirection2 = true;
														break;
													}
												}
											} while(tmpStreet2 != curStreet_ && l < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
											// found destination...now insert into messagequeue
											if(destX != -1 && destY != -1){
												int direction = -1;
												if(!curDirection_) direction = 1;
												int time = Renderer.getInstance().getTimePassed();
												PenaltyMessage message = new PenaltyMessage(((BlockingObject) next_).getX(), ((BlockingObject) next_).getY(), destX, destY, PENALTY_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, false, ID_, this, "HUANG_RHCN", false, true);
												long dx = message.getDestinationX_() - curX_;
												long dy = message.getDestinationY_() - curY_;
												if((long)PENALTY_MESSAGE_RADIUS * PENALTY_MESSAGE_RADIUS >= (dx*dx + dy*dy)){
													message.setFloodingMode(true);	// enable flooding mode if within distance!
												}								
												knownMessages_.addMessage(message, false, true);
												++rhcnMessagesCreated_;

											}							
										}	
									}
									return 1;
								}
							}							}
						break;	// only check the first on our lane!
					}
					tmpLaneObject = tmpLaneObject.getNext();
				}
			}
		}
		// didn't need to brake because of vehicle directly in front of us
		double distance;
		if(curDirection_) distance = curStreet_.getLength() - curPosition_;
		else distance = curPosition_;
		// only do the big calculation if the current street is empty AND the remainder of the current street is shorter than the braking distance
		if(distance < curBrakingDistance_){
			Street tmpStreet = curStreet_;
			LaneObject tmpLaneObject;
			Node junctionNode, nextNode;
			boolean tmpDirection = curDirection_;
			boolean gotJunctionPermission = false;
			int tmpLane = lane;
			int i;
			int j = routeStreets_.length-1;

			// check next streets that we will visit. If routing is empty this automatically skips (which is what we actually want)
			for(i = routePosition_; i < j;){
				//check for junctions
				if(tmpDirection) junctionNode = tmpStreet.getEndNode();
				else junctionNode = tmpStreet.getStartNode();
				if(junctionNode.getJunction() != null){
	
					if(junctionAllowed_ != junctionNode){
						if(routeDirections_[i+1]) nextNode = routeStreets_[i+1].getEndNode();
						else nextNode = routeStreets_[i+1].getStartNode();
						if(junctionNode.isHasTrafficSignal_()){
							if(junctionNode.getJunction().canPassTrafficLight(this, tmpStreet, nextNode)){	
								junctionAllowed_ = junctionNode;
								gotJunctionPermission = true;
							}
							else {
								waitingForSignal_ = true;
								return 2;
							}
						}
						else{
							int priority;
							if(tmpDirection) priority = junctionNode.getJunction().getJunctionPriority(tmpStreet.getStartNode(), nextNode);
							else priority = junctionNode.getJunction().getJunctionPriority(tmpStreet.getEndNode(), nextNode);
							if(priority != 1){	// don't do anything on priority streets
								// don't turn off faster than about 35km/h
								if(curSpeed_ > 1000){
									return 2;
								} else if(priority > 2){
									junctionNode.getJunction().addWaitingVehicle(this, priority);
									if(!emergencyVehicle_ && !fakingMessages_ && !junctionNode.getJunction().canPassJunction(this, priority, nextNode)) return 2;
									else {
										junctionAllowed_ = junctionNode;
										gotJunctionPermission = true;
									}
								}
							}
						}
					}
				}
				//get next street
				++i;
				tmpDirection = routeDirections_[i];
				tmpStreet = routeStreets_[i];
				if(tmpLane > tmpStreet.getLanesCount()) tmpLane = tmpStreet.getLanesCount();

				// Check if next street has smaller speed limit
				if(tmpStreet.getSpeed() < curSpeed_) {
					if(gotJunctionPermission) {
						junctionAllowed_.getJunction().allowOtherVehicle();
						junctionAllowed_ = null;
					}					
					return 2;
				}

				// Check if first lane object of next street on our lane forces us to stop
				if(!foundNextVehicle){
					tmpLaneObject = tmpStreet.getFirstLaneObject(tmpDirection);
					while(tmpLaneObject != null){
						if(tmpLaneObject.getCurLane() == tmpLane){
							foundNextVehicle = true;
							if((tmpDirection && tmpLaneObject.getCurPosition()+distance < curBrakingDistance_) || (!tmpDirection && tmpStreet.getLength()-tmpLaneObject.getCurPosition()+distance < curBrakingDistance_)){
								if(curSpeed_ > tmpLaneObject.getCurSpeed()-brakingRate_){
									if(gotJunctionPermission) {
										junctionAllowed_.getJunction().allowOtherVehicle();
										junctionAllowed_ = null;
									}	
									return 1;
								}
							}
							break;
						}
						tmpLaneObject = tmpLaneObject.getNext();
					}
				}

				// calculate distance of the whole street.
				distance += tmpStreet.getLength();
				// We can stop processing the next one if we get longer than braking distance
				if(distance > curBrakingDistance_) break;
				if(tmpStreet == destinations_.peekFirst().getStreet()) break;

			}		
		}

	
		return 0;
	}
	
	/**
	 * Check if a lane is free. It is considered as free if
	 * <ul><li>in front of this vehicle there's minimum double the current braking distance space and</li>
	 * <li>behind there's at least the current braking distance + 10m space</li>
	 * </ul>
	 * 
	 * @param lane 	the lane to check
	 * 
	 * @return <code>true</code> if lane is free, else <code>false</code>
	 */
	
	private final boolean checkLaneFree(int lane){
		// ================================= 
		// Step 1: Check space in front
		// ================================= 
		boolean foundNextVehicle = false;
		int neededFreeDistance = curBrakingDistance_ / 2;
		// check the lane object in front of us (on our street)
		if(next_ != null){
			if(next_.getCurLane() == lane){	// next one is on the same lane
				foundNextVehicle = true;
				if((curDirection_ && next_.getCurPosition()-curPosition_ < neededFreeDistance) || (!curDirection_ && curPosition_-next_.getCurPosition() < neededFreeDistance)){
					if(curSpeed_ > next_.getCurSpeed()-brakingRate_) return false;
				}
			} else {	// need to search for the next which is on our lane
				LaneObject tmpLaneObject = next_.getNext();
				while(tmpLaneObject != null){
					if(tmpLaneObject.getCurLane() == lane){
						foundNextVehicle = true;
						if((curDirection_ && tmpLaneObject.getCurPosition()-curPosition_ < neededFreeDistance) || (!curDirection_ && curPosition_-tmpLaneObject.getCurPosition() < neededFreeDistance)){
							if(curSpeed_ > next_.getCurSpeed()-brakingRate_) return false;
						}
						break;	// only check the first on our lane!
					}
					tmpLaneObject = tmpLaneObject.getNext();
				}
			}
		}
		double distance;
		if(curDirection_) distance = curStreet_.getLength() - curPosition_;
		else distance = curPosition_;
		// only do the big calculation if the current street is empty AND the remainder of the current street is shorter than the braking distance
		if(!foundNextVehicle && distance < neededFreeDistance){
			Street tmpStreet = curStreet_;
			LaneObject tmpLaneObject;
			boolean tmpDirection = curDirection_;
			// check next streets that we will visit. If routing is empty this automatically skips (which is what we actually want)
			for(int i = routePosition_ + 1; i < routeStreets_.length; ++i){
				tmpDirection = routeDirections_[i];
				tmpStreet = routeStreets_[i];		

				if(!foundNextVehicle){
					tmpLaneObject = tmpStreet.getFirstLaneObject(tmpDirection);
					while(tmpLaneObject != null){
						if(tmpLaneObject.getCurLane() == lane){
							foundNextVehicle = true;
							if((tmpDirection && tmpLaneObject.getCurPosition()+distance < neededFreeDistance) || (!tmpDirection && tmpStreet.getLength()-tmpLaneObject.getCurPosition()+distance < neededFreeDistance)){
								if(curSpeed_ > tmpLaneObject.getCurSpeed()-brakingRate_) return false;
							}
							break;
						}
						tmpLaneObject = tmpLaneObject.getNext();
					}
				}

				// calculate distance of the whole street. We can stop processing the next one if we get longer than braking distance
				distance += tmpStreet.getLength();
				if(distance > neededFreeDistance) break;
			}
		}

		// ================================= 
		// Step 2: Check space behind
		// ================================= 
		neededFreeDistance = curBrakingDistance_;
		//neededFreeDistance = curBrakingDistance_ + 1000;
		boolean foundPreviousVehicle = false;
		// check the lane object before us (on our street)
		if(previous_ != null){
			if(previous_.getCurLane() == lane){	// is on the same lane
				foundPreviousVehicle = true;
				if((curDirection_ && curPosition_-previous_.getCurPosition() < neededFreeDistance) || (!curDirection_ && previous_.getCurPosition()-curPosition_ < neededFreeDistance)){
					if(curSpeed_ > previous_.getCurSpeed()-brakingRate_) return false;
				}
			} else {	// need to search for the previous one which is on our lane
				LaneObject tmpLaneObject = previous_.getPrevious();
				while(tmpLaneObject != null){
					if(tmpLaneObject.getCurLane() == lane){
						foundPreviousVehicle = true;
						if((curDirection_ && curPosition_-tmpLaneObject.getCurPosition() < neededFreeDistance) || (!curDirection_ && tmpLaneObject.getCurPosition()-curPosition_ < neededFreeDistance)){
							if(curSpeed_ > previous_.getCurSpeed()-brakingRate_) return false;
						}
						break;	// only check the first on our lane!
					}
					tmpLaneObject = tmpLaneObject.getPrevious();
				}
			}
		}
		if(curDirection_) distance = curPosition_;
		else distance = curStreet_.getLength() - curPosition_;
		// current street is not long enough. need to iterate backwards
		if(!foundPreviousVehicle && distance < neededFreeDistance){
			Street[] outgoingStreets;
			Street tmpStreet = curStreet_, tmpStreet2;
			LaneObject tmpLaneObject;
			Node nextNode = null;
			boolean tmpDirection = curDirection_;
			int i;
			// check previous streets. We can't use the routing here so it's limited to streets with no junctions which makes it a bit simpler.
			while(true){
				if(tmpDirection) nextNode = tmpStreet.getStartNode();
				else nextNode = tmpStreet.getEndNode();
				if(nextNode.getCrossingStreetsCount() != 2) return false;		// don't handle junctions!
				else outgoingStreets = nextNode.getCrossingStreets();
				for(i = 0; i < outgoingStreets.length; ++i){
					tmpStreet2 = outgoingStreets[i];
					if(tmpStreet2 != tmpStreet){
						tmpStreet = tmpStreet2;
						if(lane > tmpStreet.getLanesCount()) return false;
						if (tmpStreet2.getStartNode() == nextNode){
							tmpDirection = true;
							break;		// found street we want to => no need to look through others
						} else {
							tmpDirection = false;
							break;		// found street we want to => no need to look through others
						}
					}					
				}				

				if(!foundPreviousVehicle){
					tmpLaneObject = tmpStreet.getFirstLaneObject(tmpDirection);
					while(tmpLaneObject != null){
						if(tmpLaneObject.getCurLane() == lane){
							foundNextVehicle = true;
							if((tmpDirection && tmpStreet.getLength()-tmpLaneObject.getCurPosition()+distance < neededFreeDistance) || (!tmpDirection && tmpLaneObject.getCurPosition()+distance < neededFreeDistance)){
								if(curSpeed_ > tmpLaneObject.getCurSpeed()-brakingRate_) return false;
							}
							break;
						}
						tmpLaneObject = tmpLaneObject.getNext();
					}
				}
			
				// calculate distance of the whole street. We can stop processing the next one if we get longer than braking distance
				distance += tmpStreet.getLength();
				if(distance > neededFreeDistance) break;
			}
		}
		return true;
	}
	
	
	/**
	 * Find vehicles in neighborhood and give information to them. Please check the following conditions before calling this function:
	 * <ul>
	 * <li>communication is generally enabled</li>
	 * <li>if this vehicle is active</li>
	 * <li>if it has wifi</li> 
	 * <li>if the communication countdown is 0 or less</li>
	 * </ul>
	 */
	public void sendMessages(){
	
		//clean up old penalties 		
		//ArrayList<BlockingObject> tmpO = new ArrayList<BlockingObject>(tmpBlockings);
		//for(BlockingObject o:tmpO) if(o.removeFromLane(this, Renderer.getInstance().getTimePassed())) tmpBlockings.remove(o);
				
		communicationCountdown_ += communicationInterval_;
		if(beaconsEnabled_ && !isInMixZone_){
			Message[] messages = knownMessages_.getForwardMessages();
			int size = knownMessages_.getSize();
			Vehicle nearestVehicle;
			
			//send messages to all knownRSUs
			RSU nearestRSU;
			for(int i = size - 1; i > -1; --i){			
				KnownRSU[] rsuHeads = knownRSUsList_.getFirstKnownRSU();
				int sendCount = 0;
				KnownRSU rsuNext;
				long dx, dy, maxCommSquared = (long)maxCommDistance_ * maxCommDistance_;
				for(int j = 0; j < rsuHeads.length; ++j){
					rsuNext = rsuHeads[j];								
					while(rsuNext != null){
						++sendCount;
						nearestRSU = rsuNext.getRSU();
						dx = nearestRSU.getX() - curX_;
						dy = nearestRSU.getY() - curY_;
						if((dx * dx + dy * dy) < maxCommSquared && !nearestRSU.isEncrypted_()){	//check if vehicle really is in communication distance and it's no mix-zone rsu
							nearestRSU.receiveMessage(curX_, curY_, messages[i]);
						}
						rsuNext = rsuNext.getNext();
					}
				}
				
				// flooding mode => send to all known vehicles
				if(messages[i].getFloodingMode()){
					KnownVehicle[] heads = knownVehiclesList_.getFirstKnownVehicle();
					KnownVehicle next;
					for(int j = 0; j < heads.length; ++j){
						next = heads[j];								
						while(next != null){
							++sendCount;
							nearestVehicle = next.getVehicle();
							dx = nearestVehicle.getX() - curX_;
							dy = nearestVehicle.getY() - curY_;
							if((dx * dx + dy * dy) < maxCommSquared){	//check if vehicle really is in communication distance
								//nearestVehicle.setColor(Color.red);
								nearestVehicle.receiveMessage(curX_, curY_, messages[i]);
							}
							next = next.getNext();
						}
					}

					if(sendCount > 0) knownMessages_.deleteForwardMessage(i, true);
				// line based mode => only communicate with the nearest known vehicle to message destination
				} else {
					
					nearestVehicle = knownVehiclesList_.findNearestVehicle(curX_, curY_, messages[i].getDestinationX_(), messages[i].getDestinationY_(), maxCommDistance_);
					if(nearestVehicle != null){	// only communicate if a nearer vehicle was found!
						nearestVehicle.receiveMessage(curX_, curY_, messages[i]);
						//nearestVehicle.setColor(Color.green);
						knownMessages_.deleteForwardMessage(i, true);
					}
				}
			}
		} else if (!isInMixZone_ || mixZonesFallbackEnabled_){	
			Message[] messages = knownMessages_.getForwardMessages();
			int messageSize = knownMessages_.getSize();
			if(messageSize > 0){
				// only look through all vehicles if beacons are generally disabled and messages need to be sent in a bruteforce-mode or if the fallback mode in mix zones is enabled
				int MapMinX, MapMinY, MapMaxX, MapMaxY, RegionMinX, RegionMinY, RegionMaxX, RegionMaxY;
	
				// Minimum x coordinate to be considered for communication
				long tmp = curX_ - maxCommDistance_;
				if (tmp < 0) MapMinX = 0;	// Map stores only positive coordinates
				else if(tmp < Integer.MAX_VALUE) MapMinX = (int) tmp;
				else MapMinX = Integer.MAX_VALUE;
	
				// Maximum x coordinate to be considered for communication
				tmp = curX_ + (long)maxCommDistance_;
				if (tmp < 0) MapMaxX = 0;
				else if(tmp < Integer.MAX_VALUE) MapMaxX = (int) tmp;
				else MapMaxX = Integer.MAX_VALUE;
	
				// Minimum y coordinate to be considered for communication
				tmp = curY_ - maxCommDistance_;
				if (tmp < 0) MapMinY = 0;
				else if(tmp < Integer.MAX_VALUE) MapMinY = (int) tmp;
				else MapMinY = Integer.MAX_VALUE;
	
				// Maximum y coordinate to be considered for communication
				tmp = curY_ + (long)maxCommDistance_;
				if (tmp < 0) MapMaxY = 0;
				else if(tmp < Integer.MAX_VALUE) MapMaxY = (int) tmp;
				else MapMaxY = Integer.MAX_VALUE;
	
				// Get the regions to be considered for communication
				Region tmpregion = MAP.getRegionOfPoint(MapMinX, MapMinY);
				RegionMinX = tmpregion.getX();
				RegionMinY = tmpregion.getY();
	
				tmpregion = MAP.getRegionOfPoint(MapMaxX, MapMaxY);
				RegionMaxX = tmpregion.getX();
				RegionMaxY = tmpregion.getY();
				long maxCommDistance_square = (long)maxCommDistance_ * maxCommDistance_;
				long dx, dy, distance = 0;
				int i, j, k, l, size;
				Vehicle[] vehicles = null;
				Vehicle vehicle = null;
				
				RSU[] rsus = null;
				RSU rsu = null;
				
	
				// only iterate through those regions which are within the distance
				for(i = RegionMinX; i <= RegionMaxX; ++i){
					for(j = RegionMinY; j <= RegionMaxY; ++j){
						//send to vehicles
						vehicles = regions_[i][j].getVehicleArray();	//use the array as it's MUCH faster!
						size = vehicles.length;
						for(k = 0; k < size; ++k){
							vehicle = vehicles[k];
							// precheck if the vehicle is near enough and valid (check is not exact as its a rectangular box and not circle)
							if(vehicle.isWiFiEnabled() && vehicle.isActive() && vehicle != this && vehicle.getX() >= MapMinX && vehicle.getX() <= MapMaxX && vehicle.getY() >= MapMinY && vehicle.getY() <= MapMaxY){
								dx = vehicle.getX() - curX_;
								dy = vehicle.getY() - curY_;
								distance = dx * dx + dy * dy; 	// Pythagorean theorem: a^2 + b^2 = c^2 but without the needed Math.sqrt to save a little bit performance
								if(distance <= maxCommDistance_square){
									if(!isInMixZone_ || !mixZonesFallbackFloodingOnly_){
										for(l = 0; l < messageSize; ++l){
											vehicle.receiveMessage(curX_, curY_, messages[l]);
											//vehicle.setColor(Color.blue);
										}
									} else {
										for(l = 0; l < messageSize; ++l){
											if(messages[l].getFloodingMode()) {
												vehicle.receiveMessage(curX_, curY_, messages[l]);
												//vehicle.setColor(Color.cyan);
											}
										}
									}
								}
							}
						}
						
						//send to Road-Side-Units
						rsus = regions_[i][j].getRSUs();	//use the array as it's MUCH faster!
						size = rsus.length;
						for(k = 0; k < size; ++k){
							rsu = rsus[k];
							// precheck if the rsu is near enough and valid (check is not exact as its a rectangular box and not circle)
							if(rsu.getX() >= MapMinX && rsu.getX() <= MapMaxX && rsu.getY() >= MapMinY && rsu.getY() <= MapMaxY){
								dx = rsu.getX() - curX_;
								dy = rsu.getY() - curY_;
								distance = dx * dx + dy * dy; 	// Pythagorean theorem: a^2 + b^2 = c^2 but without the needed Math.sqrt to save a little bit performance
								if(distance <= maxCommDistance_square){
									if(!isInMixZone_ || !mixZonesFallbackFloodingOnly_){
										for(l = 0; l < messageSize; ++l){
											rsu.receiveMessage(curX_, curY_, messages[l]);
										}
									} else {
										for(l = 0; l < messageSize; ++l){
											if(messages[l].getFloodingMode()) rsu.receiveMessage(curX_, curY_, messages[l]);
										}
									}
								}
							}
						}
					}
				}
				if(!isInMixZone_ || !mixZonesFallbackFloodingOnly_) knownMessages_.deleteAllForwardMessages(true);
				else knownMessages_.deleteAllFloodingForwardMessages(true);
			}
		} 
	}
	
	/**
	 * Receive a message from another vehicle.
	 * 
	 * @param sourceX	the x coordinate of the other vehicle
	 * @param sourceY	the y coordinate of the other vehicle
	 * @param message	the message
	 */
	public final void receiveMessage(int sourceX, int sourceY, Message message){
		long dx = message.getDestinationX_() - curX_;
		long dy = message.getDestinationY_() - curY_;
		long distanceToDestinationSquared = dx*dx + dy*dy;
		
		if(message.getFloodingMode()){	// in flooding mode, vehicles only forward messages they got if they are within the target area
			if((message.getDestinationRadiusSquared() >= distanceToDestinationSquared) && !directCommunicationMode_){
				knownMessages_.addMessage(message, true, true);
			} else knownMessages_.addMessage(message, true, false);
		} else {	// line-based mode
			if(message.getDestinationRadiusSquared() >= distanceToDestinationSquared){
				message.setFloodingMode(true);	// enable flooding mode if within distance!
			}
			if(beaconsEnabled_){	// if beacons are enabled, we can be sure that we are nearer than the last vehicle
				if(directCommunicationMode_)knownMessages_.addMessage(message, true, false);
				else knownMessages_.addMessage(message, true, true);
			} else {
				// no beacons. check manually if we are nearer to the destination than the sending vehicle
				dx = message.getDestinationX_() - sourceX;
				dy = message.getDestinationY_() - sourceY;
				if(((dx * dx + dy * dy) > distanceToDestinationSquared)  && !directCommunicationMode_){
					knownMessages_.addMessage(message, true, true);
				} else knownMessages_.addMessage(message, true, false);
			}
		}
	}

	/**
	 * Find vehicles in neighborhood and send beacons to them. Please check the following conditions before calling this function:
	 * <ul>
	 * <li>communication is generally enabled</li>
	 * <li>beacons are generally enabled</li>
	 * <li>if this vehicle is active</li>
	 * <li>if it has wifi</li> 
	 * <li>if the beacon countdown is 0 or less</li>
	 * <li>if vehicle is not in a mix zone</li>
	 * </ul>
	 */
	public void sendBeacons(){
		beaconCountdown_ += beaconInterval_;
		
			
		if(isInSlow && !changedPseudonymInSlow && Renderer.getInstance().getTimePassed() >= (slowTimestamp + TIME_TO_PSEUDONYM_CHANGE - (2*beaconInterval_))){
			changedPseudonymInSlow = true;
			
			++IDsChanged_;
			ID_ = ownRandom_.nextLong();
		}
		
		if(slowOn){
			if(privacyDataLogged_ && isInSlow && !slowBeaconsLogged){
				slowBeaconsLogged = true;
				if(!vehicleJustStartedInSlow)PrivacyLogWriter.log(savedBeacon2.replace("%0%aa%0%", "IN")  + "\n" + savedBeacon1.replace("%0%aa%0%", "IN") );
			}
			
			else if(privacyDataLogged_ && !isInSlow && slowBeaconsLogged){
				slowBeaconsLogged = false;
				
				logNextBeacons = 2;
			}
		}
		/*
		if(((Renderer.getInstance().getTimePassed() - slowTimestamp) > 60000) && !vehicleCounted){
			vehicleCounted = true;
			vehiclesInSlow++;
		}
		if(Renderer.getInstance().getTimePassed()%24000 == 0)System.out.println(vehiclesInSlow);
		*/
		if(slowOn){
			if(!isInSlow && this.curSpeed_ <= SLOW_SPEED_LIMIT && logNextBeacons == 0){
				isInSlow = true;
				slowTimestamp = Renderer.getInstance().getTimePassed();
				changedPseudonymInSlow = false;

			}
			else if(isInSlow && this.curSpeed_ > SLOW_SPEED_LIMIT && (Renderer.getInstance().getTimePassed() - slowTimestamp) > (2*beaconInterval_)){
				isInSlow = false;
			}
		}


		
		if(silent_period != silentPeriod){ 
			silentPeriod = silent_period;
			
			if(!silent_period) logNextBeacons = 2;
			//log beacon
			if(silentPeriod && privacyDataLogged_ && isSilentPeriodsOn()) PrivacyLogWriter.log(savedBeacon2 + "\n" + savedBeacon1);
		}
		

		
		if(!silent_period && !isInSlow){
			//beaconCountdown_ += beaconInterval_;
			int i, j, k, size = 0, MapMinX, MapMinY, MapMaxX, MapMaxY, RegionMinX, RegionMinY, RegionMaxX, RegionMaxY;
			Vehicle[] vehicles = null;
			Vehicle vehicle = null;
		

			// Minimum x coordinate to be considered for sending beacons
			long tmp = curX_ - maxCommDistance_;
			if (tmp < 0) MapMinX = 0;	// Map stores only positive coordinates
			else if(tmp < Integer.MAX_VALUE) MapMinX = (int) tmp;
			else MapMinX = Integer.MAX_VALUE;

			// Maximum x coordinate to be considered for sending beacons
			tmp = curX_ + (long)maxCommDistance_;
			if (tmp < 0) MapMaxX = 0;
			else if(tmp < Integer.MAX_VALUE) MapMaxX = (int) tmp;
			else MapMaxX = Integer.MAX_VALUE;

			// Minimum y coordinate to be considered for sending beacons
			tmp = curY_ - maxCommDistance_;
			if (tmp < 0) MapMinY = 0;
			else if(tmp < Integer.MAX_VALUE) MapMinY = (int) tmp;
			else MapMinY = Integer.MAX_VALUE;

			// Maximum y coordinate to be considered for sending beacons
			tmp = curY_ + (long)maxCommDistance_;
			if (tmp < 0) MapMaxY = 0;
			else if(tmp < Integer.MAX_VALUE) MapMaxY = (int) tmp;
			else MapMaxY = Integer.MAX_VALUE;

			// Get the regions to be considered for sending beacons
			Region tmpregion = MAP.getRegionOfPoint(MapMinX, MapMinY);
			RegionMinX = tmpregion.getX();
			RegionMinY = tmpregion.getY();

			tmpregion = MAP.getRegionOfPoint(MapMaxX, MapMaxY);
			RegionMaxX = tmpregion.getX();
			RegionMaxY = tmpregion.getY();
			long maxCommDistanceSquared = (long)maxCommDistance_ * maxCommDistance_;
			long dx, dy;


			// only iterate through those regions which are within the distance
			for(i = RegionMinX; i <= RegionMaxX; ++i){
				for(j = RegionMinY; j <= RegionMaxY; ++j){
					vehicles = regions_[i][j].getVehicleArray();	//use the array as it's MUCH faster!
					size = vehicles.length;

					for(k = 0; k < size; ++k){
						vehicle = vehicles[k];
						// precheck if the vehicle is near enough and valid (check is not exact as its a rectangular box and not circle)
						if(vehicle.isWiFiEnabled() && vehicle.isActive() && vehicle != this && vehicle.getX() >= MapMinX && vehicle.getX() <= MapMaxX && vehicle.getY() >= MapMinY && vehicle.getY() <= MapMaxY){
							dx = vehicle.getX() - curX_;
							dy = vehicle.getY() - curY_;
							if((dx * dx + dy * dy) <= maxCommDistanceSquared){	// Pythagorean theorem: a^2 + b^2 = c^2 but without the needed Math.sqrt to save a little bit performance
								if(emergencyBeacons > 0){
									vehicle.getIdsProcessorList_().updateProcessor((ID_-1), curX_, curY_, curSpeed_, curLane_);
									vehicle.getKnownVehiclesList().updateVehicle(this, (ID_-1), curX_, curY_, curSpeed_, vehicle.getID(), false,false);
								}
								else if (emergencyBeacons == 0){
									//fake messages
									
									// find the destination for the message. Will be sent to the next junction behind us! (if its pcn we send it in front)
									boolean tmpDirection2 = curDirection_;
										
									Street tmpStreet2 = curStreet_;
									Street[] crossingStreets;
									Node tmpNode;
									int k1, l = 0, destX = -1, destY = -1;
									do{
										++l;
										if(tmpDirection2){
											tmpNode = tmpStreet2.getStartNode();
										} else {
											tmpNode = tmpStreet2.getEndNode();
										}
										if(tmpNode.getJunction() != null){
											destX = tmpNode.getX();
											destY = tmpNode.getY();
											break;
										}
										crossingStreets = tmpNode.getCrossingStreets();
										// find next street behind of us
										if(crossingStreets.length != 2){	// end of a street or some special case. don't forward any further
											destX = tmpNode.getX();
											destY = tmpNode.getY();
											break;
										}
										for(k1 = 0; k1 < crossingStreets.length; ++k1){
											if(crossingStreets[k1] != tmpStreet2){
												tmpStreet2 = crossingStreets[k1];
												if(tmpStreet2.getStartNode() == tmpNode) tmpDirection2 = false;
												else tmpDirection2 = true;
												break;
											}
										}
									} while(tmpStreet2 != curStreet_ && l < 10000);	//hard limit of 10000 nodes to maximally go back or if again arriving at source street (=>circle!)
									// found destination...now insert into messagequeue
									if(destX != -1 && destY != -1){
										int direction = -1;
										//if(!curDirection_) direction = 1;
										int time = Renderer.getInstance().getTimePassed();
										
										PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_FAKE_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, true, (ID_-1), this,  "EVA_EMERGENCY_ID", true, true);
										message.setFloodingMode(true);	// enable flooding mode if within distance!				
										knownMessages_.addMessage(message, false, true);	
										
										emergencyBeacons = -1;								
									}		
									++fakeMessagesCreated_;
									
									fakeMessageCounter_ = fakeMessageCounter_%fakeMessageTypesCount;
								}
								
								
									
							
								
								vehicle.getKnownVehiclesList().updateVehicle(this, ID_, curX_, curY_, curSpeed_, vehicle.getID(), false,false);
								vehicle.getIdsProcessorList_().updateProcessor(ID_, curX_, curY_, curSpeed_, curLane_);
							}
						}
					}
				}
			}
 
			if(emergencyBeacons >= 0) emergencyBeacons--;
				/*
				if(emergencyBeacons == 0){
					PenaltyMessage message = new PenaltyMessage(curX_, curY_, destX, destY, PENALTY_FAKE_MESSAGE_RADIUS, time + PENALTY_MESSAGE_VALID, curStreet_, curLane_, direction, PENALTY_MESSAGE_VALUE, time + PENALTY_VALID, true, ID_, this,  messageType, true, true);
					message.setFloodingMode(false);	// enable flooding mode if within distance!				
					knownMessages_.addMessage(message, true, true);
				}
				
			}
			*/
			// allow beacon monitoring
			if(beaconMonitorEnabled_){
				if(curX_ >= beaconMonitorMinX_ && curX_ <= beaconMonitorMaxX_ && curY_ >= beaconMonitorMinY_ && curY_ <= beaconMonitorMaxY_){
					REPORT_PANEL.addBeacon(this, ID_, curX_, curY_, curSpeed_, false);
				}
			}
				
			GeneralLogWriter.log(ID_ + ":" + curX_ + ":" +  curY_ + ":" +  curSpeed_); 
			
			
			if(logBeaconsAfterEvent_){
				amountOfLoggedBeacons_++;
				if(amountOfLoggedBeacons_ == 10){
					beaconString_ += "," + curX_ + "," + curY_ + "," + curSpeed_;
					if(!beaconString_.equals(","))GeneralLogWriter.log(beaconString_); 
					logBeaconsAfterEvent_ = false;
				}
				else{
					beaconString_ += "," + curX_ + "," + curY_ + "," + curSpeed_;
				}
			}
			
			// interception of Beacons by ARSUs
			AttackRSU[] tempARSUList = getArsuList();
			if(tempARSUList.length>0){
			    for(int l = 0; l < tempARSUList.length;l++) {
			    	dx = tempARSUList[l].getX() - curX_;
			    	dy = tempARSUList[l].getY() - curY_;
			    	
			   
					if((dx * dx + dy * dy) <= maxCommDistanceSquared){	// Pythagorean theorem: a^2 + b^2 = c^2 but without the needed Math.sqrt to save a little bit performance
						if(Renderer.getInstance().getAttackerVehicle() != null && !Renderer.getInstance().getAttackerVehicle().equals(this))Renderer.getInstance().getAttackerVehicle().getKnownVehiclesList().updateVehicle(this, ID_, curX_, curY_, curSpeed_, tempARSUList[l].getArsuID_(), false, true);
					}	    	
			      }

			}

			if(privacyDataLogged_ && (silentPeriodsOn || slowOn)){				
				savedBeacon2 = savedBeacon1;
				savedBeacon1 = Renderer.getInstance().getTimePassed() + ":Steady ID:" + this.steadyID_ + ":Pseudonym:" + Long.toHexString(this.ID_) + ":TraveledDistance:" + totalTravelDistance_ + ":TraveledTime:" + totalTravelTime_ + ":Node ID:None" + ":Direction:%0%aa%0%" + ":Street:" + this.getCurStreet().getName() + ":StreetSpeed:" + this.getCurStreet().getSpeed() + ":VehicleSpeed:" + this.getCurSpeed() +  ":x:" + this.curX_ + ":y:" + this.curY_;
			
				if(logNextBeacons == 1){
					logNextBeacons = 0;
					if(!slowOn || !vehicleJustStartedInSlow)PrivacyLogWriter.log(savedBeacon2.replace("%0%aa%0%", "OUT") + ":TimeInSlow:" +  (Renderer.getInstance().getTimePassed() - slowTimestamp) + "\n" + savedBeacon1.replace("%0%aa%0%", "OUT"));
					if(vehicleJustStartedInSlow) vehicleJustStartedInSlow = false;
				}
				else if(logNextBeacons == 2){
					logNextBeacons--;
				}
			}
		}
	}
	
	/**
	 * Find vehicles nearest in neighborhood and send encrypted beacons to them. Please check the following conditions before calling this function:
	 * <ul>
	 * <li>communication is generally enabled</li>
	 * <li>beacons are generally enabled</li>
	 * <li>if this vehicle is active</li>
	 * <li>if it has wifi</li> 
	 * <li>if the beacon countdown is 0 or less</li>
	 * <li>if vehicle is in a mix zone</li>
	 * <li>if encryptedBeaconsInMix is enabled</li>
	 * </ul>
	 */
	public void sendEncryptedBeacons(){
		if(!silentPeriod){
			beaconCountdown_ += beaconInterval_;
			
			RSU tmpRSU = null;

			if(curMixNode_.getEncryptedRSU_() != null){
				tmpRSU = curMixNode_.getEncryptedRSU_();
				tmpRSU.getKnownVehiclesList_().updateVehicle(this, ID_, curX_, curY_, curSpeed_, tmpRSU.getRSUID(), true, false);

				// allow beacon monitoring
				if(beaconMonitorEnabled_){
					if(curX_ >= beaconMonitorMinX_ && curX_ <= beaconMonitorMaxX_ && curY_ >= beaconMonitorMinY_ && curY_ <= beaconMonitorMaxY_){
						REPORT_PANEL.addBeacon(this, ID_, curX_, curY_, curSpeed_, true);
					}
				}

			}	
		}
		
		//check if the static flag is the same as the object flag. If not a silent period is beginning or ending -> log
		if(silent_period != silentPeriod){ 
			silentPeriod = silent_period;
			
			//silent period did begin -> log
			if(silentPeriod){
				if(privacyDataLogged_) PrivacyLogWriter.log(Renderer.getInstance().getTimePassed() + ":Steady ID:" + this.steadyID_ + ":Pseudonym:" + Long.toHexString(this.ID_) + ":TraveledDistance:" + totalTravelDistance_ + ":TraveledTime:" + totalTravelTime_ + ":Node ID:none" + ":Direction:IN" +  ":x:" + this.curX_ + ":y:" + this.curY_);
			}
			//silent perdiod did end -> log and change pseudonym
			else{
				ID_ = ownRandom_.nextLong();
				if(privacyDataLogged_) PrivacyLogWriter.log(Renderer.getInstance().getTimePassed() + ":Steady ID:" + this.steadyID_ + ":Pseudonym:" + Long.toHexString(this.ID_) + ":TraveledDistance:" + totalTravelDistance_ + ":TraveledTime:" + totalTravelTime_ + ":Node ID:none" + ":Direction:OUT" +  ":x:" + this.curX_ + ":y:" + this.curY_);
			}
		}
	}

	/**
	 * Move the vehicle one step forward. Please check if the vehicle is active before calling this!
	 * 
	 * @param timePerStep	the time per step in milliseconds
	 */
	public void move(int timePerStep){
		if(curWaitTime_ == 0 && curStreet_ != null){

			curLane_ = newLane_;
			curSpeed_ = newSpeed_;

			// ================================= 
			// Step 1: Move the vehicle according to its speed
			// ================================= 
			double tmpPosition, newPosition = curPosition_, movement;
			WayPoint nextTarget;
			movement = curSpeed_ * (timePerStep/1000.0);
			totalTravelTime_ += timePerStep;
			totalTravelDistance_ += movement;	// not totally precise when reaching destination but should be enough as long as timePerStep is less than a second...
			Street oldStreet = curStreet_;
			boolean oldDirection = curDirection_;
			while(movement > 0){
				if(curDirection_) tmpPosition = newPosition + movement;
				else tmpPosition = newPosition - movement;
				// no more routing points and on the street specified by the waypoint
				if(routePosition_ == routeStreets_.length-1 && destinations_.peekFirst().getStreet() == curStreet_){
					nextTarget = destinations_.peekFirst();	//doing this intentionally after the if! The peekFirst() is performed twice but in almost always the first one isn't even reached!
					if((curDirection_ && nextTarget.getPositionOnStreet() < tmpPosition) || (!curDirection_ && nextTarget.getPositionOnStreet() > tmpPosition)){		// gone over the position specified by the waypoint
						//we're on the last street of a routing but we still got more waypoints
						movement = tmpPosition - nextTarget.getPositionOnStreet();
						newPosition = nextTarget.getPositionOnStreet();
						do{
							destinations_.poll();
							if(destinations_.isEmpty()) break;
							curWaitTime_ = destinations_.peekFirst().getWaittime();
						} while(!calculateRoute(true, false));
						if(destinations_.isEmpty()){
							//if logging and slow is active write in privacy log to flag that the last slow won't be counted
							if(slowOn)PrivacyLogWriter.log("VehicleReachedDestination:" + this.steadyID_ + ":" + Long.toHexString(this.ID_));
							
							active_ = false;	//found no new destination where we can route to
							curWaitTime_ = Integer.MIN_VALUE;
							if(totalTravelTime_ >= minTravelTimeForRecycling_) mayBeRecycled_ = true;
							break;	
						} else brakeForDestinationCountdown_ = Integer.MAX_VALUE;
						if(curWaitTime_ > 0){
							curSpeed_ = 0;
							break;		//movement to next destination shall begin after some waiting on the current location
						} else brakeForDestination_ = false;		//stop braking for destination
					} else {
						newPosition = tmpPosition;
						movement = 0;
					}
				// leaving current street as movement is larger than the street length!
				} else if((curDirection_ && tmpPosition > curStreet_.getLength()) || (!curDirection_ && tmpPosition < 0)){
					if(curDirection_) movement = tmpPosition - curStreet_.getLength();
					else movement = -tmpPosition;
					++routePosition_;
					if(routePosition_ >= routeStreets_.length){	//no more routing entries
						//create a correct last position if routing to a next waypoint fails later
						if(curDirection_) newPosition = curStreet_.getLength();
						else newPosition = 0;
						do{
							destinations_.poll();
							if(destinations_.isEmpty()) break;
							curWaitTime_ = destinations_.peekFirst().getWaittime();
						} while(!calculateRoute(true, false));
						if(destinations_.isEmpty()){
							active_ = false;	//found no new destination where we can route to
							curWaitTime_ = Integer.MIN_VALUE;
							if(totalTravelTime_ >= minTravelTimeForRecycling_) mayBeRecycled_ = true;
							break;	
						} else brakeForDestinationCountdown_ = Integer.MAX_VALUE;
						if(curWaitTime_ > 0){
							curSpeed_ = 0;
							break;		//movement to next destination shall begin after some waiting on the current location
						} else brakeForDestination_ = false;	//stop braking for destination
					} 
					curDirection_ = routeDirections_[routePosition_];
					/*
					//use this to log vehicles passing junctions. To count the different routes and their frequency.
					Node tmpNode = null;
					int street1 = -1;
					int street2 = -1;
 					if(curDirection_ && routeStreets_[routePosition_].getStartNode().getCrossingStreetsCount() > 2){
 						tmpNode = routeStreets_[routePosition_].getStartNode();
 						for(int b = 0; b < tmpNode.getCrossingStreetsCount(); b++){
 							if(tmpNode.getCrossingStreets()[b].equals(curStreet_)) street1 = b;
 							if(tmpNode.getCrossingStreets()[b].equals(routeStreets_[routePosition_])) street2 = b;
 						}
 						GeneralLogWriter.log(tmpNode.getNodeID() + ":" + street1 + ":" + street2);
					}
					else if(!curDirection_ && routeStreets_[routePosition_].getEndNode().getCrossingStreetsCount() > 2) {
						tmpNode = routeStreets_[routePosition_].getEndNode();
 						for(int b = 0; b < tmpNode.getCrossingStreetsCount(); b++){
 							if(tmpNode.getCrossingStreets()[b].equals(curStreet_)) street1 = b;
 							if(tmpNode.getCrossingStreets()[b].equals(routeStreets_[routePosition_])) street2 = b;
 						}
 						GeneralLogWriter.log(tmpNode.getNodeID() + ":" + street1 + ":" + street2);
					}

*/
					curStreet_ = routeStreets_[routePosition_];	
					
					if(curDirection_){						
						if(curStreet_.getStartNode() == junctionAllowed_){
							junctionAllowed_.getJunction().allowOtherVehicle();
							junctionAllowed_ = null;
						}
						newPosition = 0;
					} else {				
						if(curStreet_.getEndNode() == junctionAllowed_){
							junctionAllowed_.getJunction().allowOtherVehicle();
							junctionAllowed_ = null;
						}
						newPosition = curStreet_.getLength();
					}
				} else {
					newPosition = tmpPosition;
					movement = 0;
				}
			}
			if(!active_ || curWaitTime_ != 0) {
				oldStreet.delLaneObject(this, oldDirection);
				curPosition_ = newPosition;
			}
			else if(curStreet_ != oldStreet || curDirection_ != oldDirection){
				if(curStreet_.getLanesCount() < curLane_){
					curLane_ = curStreet_.getLanesCount();
					newLane_ = curLane_;
				}
				oldStreet.delLaneObject(this, oldDirection);
				curPosition_ = newPosition;
				curStreet_.addLaneObject(this, curDirection_);
			} else if (curLane_ > 1 || drivingOnTheSide_ || passingBlocking_){	// all vehicles which are on multilanes and which did not change street need to call the update method in the LaneContainer to preserve order!
				passingBlocking_ = false;
				curStreet_.updateLaneObject(this, curDirection_, newPosition);	// updates curPosition_ in the synchronized method!
			} else {
				curPosition_ = newPosition;
			}

			// ================================= 
			// Step 2: Recalculate values
			// ================================= 

			// recalculate position on map
			if(curStreet_ != null){
				calculatePosition();
			}

			// recalculate region
			if(curX_ < curRegion_.getLeftBoundary() || curX_ > curRegion_.getRightBoundary() || curY_ < curRegion_.getUpperBoundary() || curY_ > curRegion_.getLowerBoundary()){
				curRegion_.delVehicle(this);
				curRegion_ = MAP.getRegionOfPoint(curX_, curY_);
				curRegion_.addVehicle(this, false);
			}			
		}

	}

	
	/**
	 * Move Attacker. 
	 */
	public final void moveAttacker(){
		Vehicle tmpAttacked = Renderer.getInstance().getAttackedVehicle();
		//Save if attacker is in mix-zone
		if(isInMixZone_ && firstContact) attackerWasInMix = true;
		
		//If attacked vehicle drives in mix-zone set it null and save information that the attacked vehicle was in mix-zone
		if(tmpAttacked != null && tmpAttacked.isInMixZone_ && firstContact) {
			Renderer.getInstance().setAttackedVehicle(null);
			Vehicle.setAttackedVehicleID_(0);
			attackedWasInMix = true;
			newSpeed_ = curStreet_.getSpeed();
			//Make route to leave the mix-zone
			searchAttackedVehicle_();
		}
 
		//If attacker has left the mix-zone look for the attacked vehicle. Note that it is just plain guessing here.
		if(attackedWasInMix && attackerWasInMix && !isInMixZone_ && firstContact){
			if(getKnownVehiclesList().findNearestVehicle(0, 0, curX_, curY_, 10000000) != null){
				Vehicle.setAttackedVehicleID_(getKnownVehiclesList().findNearestVehicle(0, 0, curX_, curY_, 10000000).getID());
				Renderer.getInstance().setAttackedVehicle(getKnownVehiclesList().findNearestVehicle(0, 0, curX_, curY_, 10000000));
				attackedWasInMix = false;
			}
		}
		
		//Attacker knows attacked Vehicle: follow it
		if(attackedVehicleID_ != 0) {
			reRouteTime_--;
			if(reRouteTime_ < 0){
				reRouteTime_=ATTACKER_INTERVAL;
				long dx, dy, dg;
				KnownVehicle[] heads = knownVehiclesList_.getFirstKnownVehicle();
				KnownVehicle next;
				
				//traverse all vehicle which sent beacons
				for(int l = 0; l < heads.length; ++l){
					next = heads[l];								
					while(next != null){
						//Find the attacker data
						if(next.getVehicle().getID() == attackedVehicleID_){
							firstContact = true;
							
							dx = next.getVehicle().getX() - curX_;
							dy = next.getVehicle().getY() - curY_;
							dg = (dx * dx + dy * dy);
	
							//update speed if attacker is to near / to far
							if(dg > 60000000) newSpeed_ = maxSpeed_;
							else if(dg > 20000000 && dg < 60000000) newSpeed_ = Renderer.getInstance().getAttackedVehicle().getCurSpeed();
							else if(dg < 20000000) newSpeed_ = 0;
							
							//clear destinations and add new ones (only if the attacker is far enough away
							//from the attacked vehicle. Otherwise the attacker would reach the final destination
							//until it gets a new one)
							if(dg > 10000000){
							getDestinations().clear();
								try {
									getDestinations().add(new WayPoint(next.getX(),next.getY(),0));
									getDestinations().add(new WayPoint(next.getX(),next.getY(),0));
									calculateRoute(false, true);
									brakeForDestination_ = false;
									brakeForDestinationCountdown_ = 1000;
								} catch (ParseException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							}
						next = null;
						}
						else next = next.getNext();
					}
				}
			}
		}
	}


	/**
	 * The attacked vehicle can�t be found. But it can�t be far because we received a beacon the last time.
	 * This method is only evoked one time per mix-zone so it doesn't need to be that efficient
	 */
	public void searchAttackedVehicle_(){
		boolean NodeFound = false;
		Node tempNode = curStreet_.getStartNode();
		if(!curDirection_) tempNode = curStreet_.getEndNode();
		
		int guessX = curX_ - destinations_.peekFirst().getX();
		int guessY = curY_ - destinations_.peekFirst().getY();
		
		//Traverse the street nodes 
		//The vehicle try's to conserve the current vector
		for(int i = 0;i < tempNode.getCrossingStreets().length; i++){
			Node tempNode2;
			if(tempNode.equals(tempNode.getCrossingStreets()[i].getEndNode())) tempNode2 = tempNode.getCrossingStreets()[i].getStartNode();
			else tempNode2 = tempNode.getCrossingStreets()[i].getEndNode();
			
			if(guessX < 0 && guessY < 0) if(curX_ < tempNode2.getX() && curY_ < tempNode2.getY()) {
				NodeFound = true;
				tempNode = tempNode2;
				i=10;
			}
			else if(guessX < 0 && guessY > 0) if(curX_ < tempNode2.getX() && curY_ > tempNode2.getY() ) {
				NodeFound = true;
				tempNode = tempNode2;
				i=10;
			}
			else if(guessX > 0 && guessY > 0) if(curX_ > tempNode2.getX() && curY_ > tempNode2.getY() ) {
				NodeFound = true;
				tempNode = tempNode2;
				i=10;
			}
			else if(guessX > 0 && guessY < 0) if(curX_ > tempNode2.getX() && curY_ < tempNode2.getY() )  {
				NodeFound = true;
				tempNode = tempNode2;
				i=10;
			}
		}
		
		//if it can't be found (e.g. because the street changes direction) we look for a node that isn't as perfect but still works
		if(!NodeFound){
			for(int i = 0;i < tempNode.getCrossingStreets().length; i++){
				Node tempNode2;
				if(tempNode.equals(tempNode.getCrossingStreets()[i].getEndNode())) tempNode2 = tempNode.getCrossingStreets()[i].getStartNode();
				else tempNode2 = tempNode.getCrossingStreets()[i].getEndNode();
				
				if(Math.abs(guessX) > Math.abs(guessY)){
					if(guessX < 0){
						if(curX_  < tempNode2.getX()) {
							tempNode = tempNode2;
							i=10;
						}
					}
					else if(guessX > 0){
						if(curX_  > tempNode2.getX()) {
							tempNode = tempNode2;
							i=10;
						}
					}
				}
				else{
					if(guessY < 0){
						if(curY_ < tempNode2.getY()){
							tempNode = tempNode2;
							i=10;				
						}
					}
					else if(guessY > 0){
						if(curY_ > tempNode2.getY()) {
							tempNode = tempNode2;
							i=10;				
						}
					}
				}
			}
		}
		
		//we search for 30 more nodes to make sure to drive longer than the mix zone. This is not really performant but this function is only evoked on time per mix-zone
		for(int j = 0;j < 30;j++){
			NodeFound = false;

			for(int i = 0;i < tempNode.getCrossingStreets().length; i++){
				Node tempNode2;
				if(tempNode.equals(tempNode.getCrossingStreets()[i].getEndNode())) tempNode2 = tempNode.getCrossingStreets()[i].getStartNode();
				else tempNode2 = tempNode.getCrossingStreets()[i].getEndNode();
				
				if(guessX < 0 && guessY < 0) if(tempNode.getX() < tempNode2.getX() && tempNode.getY() < tempNode2.getY()) {
					NodeFound = true;
					tempNode = tempNode2;
					i=10;
				}
				else if(guessX < 0 && guessY > 0) if(tempNode.getX() < tempNode2.getX() && tempNode.getY() > tempNode2.getY() ) {
					NodeFound = true;
					tempNode = tempNode2;
					i=10;
				}
				else if(guessX > 0 && guessY > 0) if(tempNode.getX() > tempNode2.getX() && tempNode.getY() > tempNode2.getY() ) {
					NodeFound = true;
					tempNode = tempNode2;
					i=10;
				}
				else if(guessX > 0 && guessY < 0) if(tempNode.getX() > tempNode2.getX() && tempNode.getY() < tempNode2.getY() )  {
					NodeFound = true;
					tempNode = tempNode2;
					i=10;
				}
			}
			
			if(!NodeFound){
				for(int i = 0;i < tempNode.getCrossingStreets().length; i++){
					Node tempNode2;
					if(tempNode.equals(tempNode.getCrossingStreets()[i].getEndNode())) tempNode2 = tempNode.getCrossingStreets()[i].getStartNode();
					else tempNode2 = tempNode.getCrossingStreets()[i].getEndNode();
					
					if(Math.abs(guessX) > Math.abs(guessY)){
						if(guessX < 0){
							if(tempNode.getX()  < tempNode2.getX()) {
								tempNode = tempNode2;
								i=10;
							}
						}
						else if(guessX > 0){
							if(tempNode.getX()  > tempNode2.getX()) {
								tempNode = tempNode2;
								i=10;
							}
						}
					}
					else{
						if(guessY < 0){
							if(tempNode.getY() < tempNode2.getY()){
								tempNode = tempNode2;
								i=10;				
							}
						}
						else if(guessY > 0){
							if(tempNode.getY() > tempNode2.getY()) {
								tempNode = tempNode2;
								i=10;				
							}
						}
					}
				}
				}
		}

				
		//clear the old destinations and save the new ones.
		getDestinations().clear();
		try {
			getDestinations().add(new WayPoint(tempNode.getX(),tempNode.getY(),0));
			getDestinations().add(new WayPoint(tempNode.getX(),tempNode.getY(),0));
			calculateRoute(false, true);
			brakeForDestination_ = false;
			brakeForDestinationCountdown_ = 1000;
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Resets this vehicle so that it can be reused. It will travel on the same route as last time!
	 */
	public void reset(){
		//ArrayList<BlockingObject> tmpO = new ArrayList<BlockingObject>(tmpBlockings);
		//for(BlockingObject o:tmpO) tmpBlockings.remove(o);
		
		//reset countdowns and other variables
		ID_ = ownRandom_.nextLong();
		steadyID_ = steadyIDCounter++;
		curSpeed_ = brakingRate_/2;
		newSpeed_ = curSpeed_;
		totalTravelTime_ = 0;
		totalTravelDistance_ = 0;
		newLane_ = 1;
		active_ = false;
		speedAtLastBrakingDistanceCalculation_ = 0;
		isInMixZone_ = false;
		junctionAllowed_ = null;			
		brakeForDestination_ = false;
		brakeForDestinationCountdown_ = Integer.MAX_VALUE;
		destinationCheckCountdown_ = 0;
		laneChangeCountdown = 0;
		communicationCountdown_ = 0;
		knownVehiclesTimeoutCountdown_ = 0;
		knownPenaltiesTimeoutCountdown_ = 0;
		beaconCountdown_ = (int)Math.round(curPosition_)%beaconInterval_;
		communicationCountdown_ = (int)Math.round(curPosition_)%communicationInterval_;
		mixCheckCountdown_ = (int)Math.round(curPosition_)%MIX_CHECK_INTERVAL;
		emergencyBrakingCountdown_ = ownRandom_.nextInt(emergencyBrakingInterval_)+1;
	//	lastMessageCreated = 0;
		lastRHCNMessageCreated = 0;		
		lastPCNMessageCreated = 0;
		lastPCNFORWARDMessageCreated = 0;
		lastEVAMessageCreated = 0;
		//lastEVAFORWARDMessageCreated = 0;
		//lastEEBLMessageCreated = 0;
		//lastFAKEMessageCreated = 0;
		stopTime_ = 0;
		passingBlocking_ = false;
		//don't set them 0, because we wan't the count of all messages created (included the deleted ones)
		/*
		pcnMessagesCreated_ = 0;
		pcnForwardMessagesCreated_ = 0;
		evaForwardMessagesCreated_ = 0;
		evaMessagesCreated_ = 0;
		rhcnMessagesCreated_ = 0;
		eeblMessagesCreated_ = 0;
		fakeMessagesCreated_ = 0;
		*/
		IDsChanged_ = 0;
		
		//slow model
		isInSlow = false;
		changedPseudonymInSlow = false;
		slowBeaconsLogged = false;
		vehicleJustStartedInSlow = true;
		
		//reset communication info
		knownVehiclesList_.clear();
		knownPenalties_.clear();
		knownMessages_.clear();
		idsProcessorList_.clear();
		
		//reset RSU infos
		knownRSUsList_.clear();
		knownRSUsTimeoutCountdown_ = 0;
		
		speedFluctuationCountdown_ = (int)Math.round(curPosition_)%SPEED_FLUCTUATION_CHECKINTERVAL;
		
		//reset known event sources
		knownEventSourcesList_.clear();
		
		// reset position
		curX_ = startingWayPoint_.getX();
		curY_ = startingWayPoint_.getY();
		curPosition_ = startingWayPoint_.getPositionOnStreet();
		curStreet_ = startingWayPoint_.getStreet();
		curWaitTime_ = startingWayPoint_.getWaittime();

		// recalculate routing information
		destinations_ = originalDestinations_.clone();
		if(curStreet_.isOneway()){
			while(!destinations_.isEmpty() && (!calculateRoute(false, false) || destinations_.peekFirst().getStreet() == curStreet_)){
				curWaitTime_ = destinations_.pollFirst().getWaittime();
			}
		} else {
			while(!destinations_.isEmpty() && (!calculateRoute(false, false) || destinations_.peekFirst().getStreet() == curStreet_)){
				curWaitTime_ = destinations_.pollFirst().getWaittime();
			}
		}
		if(curWaitTime_ == 0){
			active_ = true;
			curStreet_.addLaneObject(this, curDirection_);
		}
		calculatePosition();
		
		//reset region
		curRegion_.delVehicle(this);
		curRegion_ = MAP.getRegionOfPoint(curX_, curY_);
		curRegion_.addVehicle(this, false);
		
		mayBeRecycled_ = false;
	}
	
	/**
	 * Resets the global random number generator
	 */
	public static void resetGlobalRandomGenerator(){
		RANDOM.setSeed(1L);
	}
	

	/**
	 * Gets a substring of the Vehicle ID. Used in the EditOneVehicleControl.java chooseVehicle_ JCombobox.
	 */
	public String toString(){
		return Long.toHexString(ID_).substring(0,5);		
	}

	
	/**
	 * Indicates if this vehicle may be reset and reused.
	 * 
	 * @return	<code>true</code> if it may be reused, else <code>false</code>
	 */
	public boolean getMayBeRecycled(){
		return mayBeRecycled_;
	}

	/**
	 * Gets an array with all streets which will be visited until arriving at the next destination.
	 * 
	 * @return the array with all streets
	 */
	public Street[] getRouteStreets(){
		return routeStreets_;
	}

	/**
	 * Get the directions corresponding to the array returned <code>getRouteStreets()</code>.
	 * 
	 * @return the array with all directions
	 */
	public boolean[] getRouteDirections(){
		return routeDirections_;
	}

	/**
	 * Gets the current position in the array returned by <code>getRouteStreets()</code> and <code>getRouteDirections()</code>.
	 * 
	 * @return the position
	 */
	public int getRoutePosition(){
		return routePosition_;
	}


	/**
	 * Gets the current ID of this vehicle. This ID might change if mixing is enabled!
	 * 
	 * @return the ID
	 */
	public int getVehicleID(){
		return 0;
	}

	/**
	 * Gets the current communication countdown
	 * 
	 * @return the communication countdown
	 */
	public int getCommunicationCountdown(){
		return communicationCountdown_;
	}

	/**
	 * Gets the current beacon countdown
	 * 
	 * @return the beacon countdown
	 */
	public int getBeaconCountdown(){
		return beaconCountdown_;
	}

	/**
	 * Gets the starting point of the vehicle.
	 * 
	 * @return the <code>WayPoint</code>
	 */
	public WayPoint getStartPoint() {
		return startingWayPoint_;
	}

	/**
	 * Gets the regions x-coordinate in which this vehicle is found.
	 * 
	 * @return An Integer representing the x-coordinate of the Region
	 */
	public int getRegionX() {
		return curRegion_.getX();
	}

	/**
	 * Gets the regions y-coordinate in which this vehicle is found.
	 * 
	 * @return An Integer representing the y-coordinate of the Region
	 */
	public int getRegionY() {
		return curRegion_.getY();
	}

	/**
	 * Gets the maximum speed of this vehicle.
	 * 
	 * @return the maximum speed in cm/s
	 */
	public int getMaxSpeed(){
		return maxSpeed_;
	}

	/**
	 * Gets the current waittime.
	 * 
	 * @return the current waittime in milliseconds
	 */
	public int getWaittime(){
		if(curWaitTime_ < 0) return 0; 
		else return curWaitTime_;
	}

	/**
	 * Gets the destinations of this vehicle.
	 * 
	 * @return the <code>ArrayDeque</code> with all destinations
	 */
	public ArrayDeque<WayPoint> getDestinations(){
		return destinations_;
	}

	/**
	 * Gets the maximum communication distance of this vehicle.
	 * 
	 * @return the distance in cm
	 */
	public int getMaxCommDistance(){
		return maxCommDistance_;
	}

	/**
	 * Returns if this vehicle is currently active.
	 * 
	 * @return <code>true</code> if it's active
	 */
	public boolean isActive(){
		return active_;
	}

	/**
	 * Returns if this vehicle is currently in a mix zone.
	 * 
	 * @return <code>true</code> if it's in a mix zone
	 */
	public boolean isInMixZone(){
		return isInMixZone_;
	}

	/**
	 * Returns if this vehicle has WiFi functionality.
	 * 
	 * @return <code>true</code> if it has WiFi
	 */
	public boolean isWiFiEnabled(){
		return wiFiEnabled_;
	}

	/**
	 * Gets the special data structure with all known messages.
	 * 
	 * @return the data structure
	 */
	public KnownMessages getKnownMessages(){
		return knownMessages_;
	}
	
	/**
	 * Gets the special data structure with all known penalties.
	 * 
	 * @return the data structure
	 */
	public KnownPenalties getKnownPenalties(){
		return knownPenalties_;
	}

	/**
	 * Gets the special data structure with all known vehicles.
	 * 
	 * @return the data structure
	 */
	public KnownVehiclesList getKnownVehiclesList(){
		return knownVehiclesList_;
	}
	
	/**
	 * Gets the special data structure with all known RSUs.
	 * 
	 * @return the data structure
	 */
	public KnownRSUsList getKnownRSUsList(){
		return knownRSUsList_;
	}
	

	/**
	 * Returns how often this vehicle has changed it's ID (excluding the initial ID).
	 * 
	 * @return the amount
	 */
	public int getIDsChanged(){
		return IDsChanged_;
	}

	/**
	 * Gets how long this vehicle traveled. This excludes predefined waiting times but includes
	 * all other stops.
	 * 
	 * @return the total time in milliseconds
	 */
	public int getTotalTravelTime(){
		return totalTravelTime_;
	}

	/**
	 * The total distance traveled. This is not completely exact but should suffice in most cases.
	 * Small aberration from the real value occur if this vehicle reaches a destination (which should 
	 * not happen too often). 
	 * 
	 * @return the total distance in cm
	 */
	public long getTotalTravelDistance(){
		return totalTravelDistance_;
	}

	/**
	 * Sets the region in which this vehicle is found.
	 * 
	 * @param region	the region
	 */
	public void setRegion(Region region) {
		curRegion_ = region;
	}

	/**
	 * Gets the ID used in beacons encoded in HEX so that it's shorter. If the vehicle is not wifi
	 * enabled, brackets are used to indicate this.
	 * 
	 * @return the ID as an hex string
	 */
	public String getHexID(){
		if(wiFiEnabled_) return Long.toHexString(ID_);
		else return "(" + Long.toHexString(ID_) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
	}
	
	/**
	 * Returns the interval between messages.
	 * 
	 * @return the interval in milliseconds
	 */
	public static int getCommunicationInterval(){
		return communicationInterval_;
	}

	/**
	 * Returns the interval between beacons.
	 * 
	 * @return the interval in milliseconds
	 */
	public static int getBeaconInterval(){
		return beaconInterval_;
	}


	/**
	 * Signals if communication is enabled.
	 * 
	 * @return	<code>true</code> if communication is enabled, else <code>false</code>
	 */
	public static boolean getCommunicationEnabled(){
		return communicationEnabled_;
	}
	
	/**
	 * Signals if recycling of vehicles is enabled or not
	 * 
	 * @return	<code>true</code> if recycling is enabled, else <code>false</code>
	 */
	public static boolean getRecyclingEnabled(){
		return recyclingEnabled_;
	}

	/**
	 * Signals if beacons are enabled.
	 * 
	 * @return <code>true</code> if beacons are enabled, else <code>false</code>
	 */
	public static boolean getBeaconsEnabled(){
		return beaconsEnabled_;
	}
	
	/**
	 * Signals if mix zones are enabled.
	 * 
	 * @return <code>true</code> if mix zones are enabled, else <code>false</code>
	 */
	public static boolean getMixZonesEnabled(){
		return mixZonesEnabled_;
	}
	
	/**
	 * If the fallback mode shall be enabled in mix zones. This fallback mode enables the beaconless
	 * communication inside mix zones.
	 * 
	 * @return <code>true</code> if the fallback mode is enabled, else <code>false</code>
	 */
	public static boolean getMixZonesFallbackEnabled(){
		return mixZonesFallbackEnabled_;
	}
	
	/**
	 * If the fallback mode only sends messages which are in flooding/broadcast mode.
	 * 
	 * @return <code>true</code> if only flooding messages are sent, else <code>false</code>
	 */
	public static boolean getMixZonesFallbackFloodingOnly(){
		return mixZonesFallbackFloodingOnly_;
	}

	/**
	 * Returns the current routing mode.
	 * 
	 * @return the routing mode
	 */
	public static int getRoutingMode(){
		return routingMode_;
	}

	/**
	 * Returns the maximum communication distance.
	 * 
	 * @return the maximum communication distance in cm
	 */
	public static int getMaximumCommunicationDistance(){
		return maximumCommunicationDistance_;
	}
	
	/**
	 * Gets the minimum time a vehicle needs to have traveled in order to be able to be recycled. Vehicles
	 * which travel shorter than this time will NOT get recycled. 
	 * 
	 * @return the time in milliseconds
	 */
	public static int getMinTravelTimeForRecycling(){
		return minTravelTimeForRecycling_;
	}

	/**
	 * Returns the radius of the mix zones.
	 * 
	 * @return the mix zone radius in cm
	 */
	public static int getMixZoneRadius(){
		return mixZoneRadius_;
	}

	/**
	 * Set the maximum radius of the mix zones.
	 * 
	 * @param maxMixZoneRadius	the maximum radius of the mix zones in cm
	 */
	public static void setMaxMixZoneRadius(int maxMixZoneRadius) {
		maxMixZoneRadius_ = maxMixZoneRadius;
	}

	/**
	 * Gets the maximum mix zone radius used in the scenario.
	 * 
	 * @return maxMixZoneRadius_ the maximum mix zone radius in cm
	 */
	public static int getMaxMixZoneRadius() {
		return maxMixZoneRadius_;
	}


	/**
	 * Set the default radius of the mix zones (in the common settings panel).
	 * 
	 * @param mixZoneRadius	the radius of the mix zones in cm
	 */
	public static void setMixZoneRadius(int mixZoneRadius){
		mixZoneRadius_ = mixZoneRadius;
	}
	
	/**
	 * Sets the minimum time a vehicle needs to have traveled in order to be able to be recycled. Vehicles
	 * which travel shorter than this time will NOT get recycled. 
	 * 
	 * @param minTravelTimeForRecycling	the time in milliseconds
	 */
	public static void setMinTravelTimeForRecycling(int minTravelTimeForRecycling){
		minTravelTimeForRecycling_ = minTravelTimeForRecycling;
	}

	/**
	 * Set the maximum communication distance.
	 * 
	 * @param maximumCommunicationDistance	the maximum communication distance in cm
	 */
	public static void setMaximumCommunicationDistance(int maximumCommunicationDistance){
		maximumCommunicationDistance_ = maximumCommunicationDistance;
	}

	/**
	 * Sets the reference to all regions. Call this on map reload!
	 * 
	 * @param regions	the array with all regions
	 */
	public static void setRegions(Region[][] regions){
		regions_ = regions;
	}

	/**
	 * Sets a new value for the communication interval. Common to all vehicles.
	 * 
	 * @param communicationInterval	the new value 
	 */
	public static void setCommunicationInterval(int communicationInterval){
		communicationInterval_ = communicationInterval;
	}

	/**
	 * Sets a new value for the beacon interval. Common to all vehicles.
	 * 
	 * @param beaconInterval	the new value 
	 */
	public static void setBeaconInterval(int beaconInterval){
		beaconInterval_ = beaconInterval;
	}

	/**
	 * Sets if communication is enabled or not. Common to all vehicles.
	 * 
	 * @param state	<code>true</code> to enable communication, else <code>false</code> 
	 */
	public static void setCommunicationEnabled(boolean state){
		RSU.setCommunicationEnabled(state);
		communicationEnabled_ = state;
	}
	
	/**
	 * Sets if recycling of vehicles is enabled or not. Common to all vehicles.
	 * 
	 * @param state	<code>true</code> to enable recycling, else <code>false</code> 
	 */
	public static void setRecyclingEnabled(boolean state){
		recyclingEnabled_ = state;
	}

	/**
	 * Sets if beacons are enabled or not. Common to all vehicles.
	 * 
	 * @param state	<code>true</code> to enable beacons, else <code>false</code> 
	 */
	public static void setBeaconsEnabled(boolean state){
		RSU.setBeaconsEnabled(state);
		beaconsEnabled_ = state;
	}

	/**
	 * Sets if mix zones are enabled or not. Common to all vehicles.
	 * 
	 * @param state	<code>true</code> to enable mix zones, else <code>false</code> 
	 */
	public static void setMixZonesEnabled(boolean state){
		mixZonesEnabled_ = state;
	}
	
	/**
	 * Sets if the fallback mode shall be enabled in mix zones. This fallback mode enables the beaconless
	 * communication inside mix zones.
	 * 
	 * @param state	<code>true</code> if the fallback mode is enabled, else <code>false</code>
	 */
	public static void setMixZonesFallbackEnabled(boolean state){
		mixZonesFallbackEnabled_ = state;
	}
	
	/**
	 * Sets if the fallback mode only sends messages which are in flooding/broadcast mode.
	 * 
	 * @param state	<code>true</code> if only flooding messages are sent, else <code>false</code>
	 */
	public static void setMixZonesFallbackFloodingOnly(boolean state){
		mixZonesFallbackFloodingOnly_ = state;
	}
	
	/**
	 * Sets if beacon zones should be monitored or not. Common to all vehicles.
	 * 
	 * @param beaconMonitorEnabled	<code>true</code> to enable monitoring mix zones, else <code>false</code> 
	 */
	public static void setBeaconMonitorZoneEnabled(boolean beaconMonitorEnabled){
		beaconMonitorEnabled_ = beaconMonitorEnabled;
		RSU.setBeaconMonitorZoneEnabled(beaconMonitorEnabled);
	}
	
	/**
	 * Gets beacon monitor status
	 * 
	 * @return beaconMonitorEnabled_ <code>true</code> if beacon monitor is enabled
	 */
	public static boolean getbeaconMonitorEnabled() {
		return beaconMonitorEnabled_;
	}
	
	/**
	 * Sets the values for the monitored beacon zone. A rectangular bounding box within the specified coordinates
	 * is monitored if {@link #setBeaconMonitorZoneEnabled(boolean)} is set to <code>true</code>.
	 * 
	 * @param beaconMonitorMinX	the minimum x coordinate
	 * @param beaconMonitorMaxX	the maximum x coordinate
	 * @param beaconMonitorMinY	the minimum y coordinate
	 * @param beaconMonitorMaxY	the maximum y coordinate
	 */
	public static void setMonitoredMixZoneVariables(int beaconMonitorMinX, int beaconMonitorMaxX, int beaconMonitorMinY, int beaconMonitorMaxY){
		beaconMonitorMinX_ = beaconMonitorMinX;
		beaconMonitorMaxX_ = beaconMonitorMaxX;
		beaconMonitorMinY_ = beaconMonitorMinY;
		beaconMonitorMaxY_ = beaconMonitorMaxY;		
		RSU.setMonitoredMixZoneVariables(beaconMonitorMinX,beaconMonitorMaxX,beaconMonitorMinY,beaconMonitorMaxY);
	}
	
	/**
	 * Gets beacon monitor minX coordinate
	 * 
	 * @return beaconMonitorMinX_ the minX coordinate of the beacon monitor window
	 */
	public static int getbeaconMonitorMinX() {
		return beaconMonitorMinX_;
	}
	
	/**
	 * Gets beacon monitor maxX coordinate
	 * 
	 * @return beaconMonitorMaxX_ the maxX coordinate of the beacon monitor window
	 */
	public static int getbeaconMonitorMaxX() {
		return beaconMonitorMaxX_;
	}
	
	/**
	 * Gets beacon monitor minY coordinate
	 * 
	 * @return beaconMonitorMinY_ the minY coordinate of the beacon monitor window
	 */
	public static int getbeaconMonitorMinY() {
		return beaconMonitorMinX_;
	}
	
	/**
	 * Gets beacon monitor maxY coordinate
	 * 
	 * @return beaconMonitorMaxY_ the maxY coordinate of the beacon monitor window
	 */
	public static int getbeaconMonitorMaxY() {
		return beaconMonitorMaxY_;
	}

	/**
	 * Gets the report panel for beacon monitoring
	 * 
	 * @return REPORT_PANEL the beacon report panel
	 */
	public static ReportingControlPanel getREPORT_PANEL() {
		return REPORT_PANEL;
	}
	/**
	 * Sets a new routing mode. See the A_Star_Algor for details. Common to all vehicles.
	 * 
	 * @param mode	the new routing mode
	 */
	public static void setRoutingMode(int mode){
		routingMode_ = mode;
	}

	/**
	 * Gets the vehicle ID
	 * 
	 * @return ID_ the vehicle ID
	 */
	public long getID() {
		return ID_;
	}

	/**
	 * Set vehicle WiFi
	 * 
	 * @param wiFiEnabled <code>true</code> to enable WiFi for the vehicle
	 */
	public void setWiFiEnabled(boolean wiFiEnabled) {
		wiFiEnabled_ = wiFiEnabled;
	}


	/**
	 * Sets the maximum speed
	 * 
	 * @param maxSpeed the speed in cm/s
	 */
	public void setMaxSpeed(int maxSpeed) {
		maxSpeed_ = maxSpeed;
	}

	/**
	 * Sets the maximum communication distance
	 * 
	 * @param maxCommDistance the maximum communication distance in cm
	 */
	public void setMaxCommDistance(int maxCommDistance) {
		maxCommDistance_ = maxCommDistance;
	}

	/**
	 * Sets the current wait time.
	 * 
	 * @param curWaitTime the current wait time in ms.
	 */
	public void setCurWaitTime(int curWaitTime) {
		curWaitTime_ = curWaitTime;
	}

	/**
	 * Gets the current wait time
	 * 
	 * @return curWaitTime_ the current wait time
	 */
	public int getCurWaitTime() {
		return curWaitTime_;
	}

	/**
	 * Sets the color
	 * 
	 * @param color the new color
	 */
	public void setColor(Color color) {
		color_ = color;
	}

	/**
	 * Gets the vehicle color.
	 * 
	 * @return color_ the vehicle color.
	 */
	public Color getColor() {
		return color_;
	}

	/**
	 * Sets the braking rate
	 * 
	 * @param brakingRate the braking rate in cm/s^2
	 */
	public void setBrakingRate(int brakingRate) {
		if(brakingRate <= 0) brakingRate_ = 300;
		else brakingRate_ = brakingRate;	
	}

	/**
	 * Gets the braking rate
	 * 
	 * @return brakingRate_ the braking rate in cm/s^2
	 */
	public int getBrakingRate() {
		return brakingRate_;
	}

	/**
	 * Sets the acceleration rate
	 * 
	 * @param accelerationRate the acceleration rate in cm/s^2
	 */
	public void setAccelerationRate(int accelerationRate) {
		if(accelerationRate <= 0) accelerationRate_ = 800;
		else accelerationRate_ = accelerationRate;	
	}

	/**
	 * Gets the acceleration rate
	 * 
	 * @return accelerationRate_ the acceleration rate in cm/s^2
	 */
	public int getAccelerationRate() {
		return accelerationRate_;
	}

	/**
	 * Sets the emergency vehicle mode
	 * 
	 * @param emergencyVehicle <code>true</code> to enable emergency mode
	 */
	public void setEmergencyVehicle(boolean emergencyVehicle) {
		emergencyVehicle_ = emergencyVehicle;
	}

	/**
	 * Signals if vehicle is an emergency vehicle
	 * 
	 * @return <code>true</code> if vehicle is an emergency vehicle
	 */
	public boolean isEmergencyVehicle() {
		return emergencyVehicle_;
	}

	/**
	 * Sets the vehicle length
	 * 
	 * @param vehicleLength the vehicle length in cm.
	 */ 
	public void setVehicleLength(int vehicleLength) {
		vehicleLength_ = vehicleLength;
	}

	/**
	 * Gets the vehicle length.
	 * 
	 * @return vehicleLength_ the vehicle length in cm.
	 */
	public int getVehicleLength() {
		return vehicleLength_;
	}


	public static AttackRSU[] getArsuList() {
		return arsuList;
	}


	public static void setArsuList(AttackRSU[] arsuList) {
		Vehicle.arsuList = arsuList;
	}


	public static boolean isAttackerDataLogged_() {
		return attackerDataLogged_;
	}


	public static void setAttackerDataLogged_(boolean attackerDataLogged_) {
		Vehicle.attackerDataLogged_ = attackerDataLogged_;
	}


	public static long getAttackedVehicleID_() {
		return attackedVehicleID_;
	}


	public static void setAttackedVehicleID_(long attackedVehicleID_) {
		Vehicle.attackedVehicleID_ = attackedVehicleID_;
	}


	public static boolean isEncryptedBeaconsInMix_() {
		return encryptedBeaconsInMix_;
	}


	public static void setEncryptedBeaconsInMix_(boolean encryptedBeaconsInMix_) {
		Vehicle.encryptedBeaconsInMix_ = encryptedBeaconsInMix_;
	}


	public static boolean isAttackerEncryptedDataLogged_() {
		return attackerEncryptedDataLogged_;
	}


	public static void setAttackerEncryptedDataLogged_(
			boolean attackerEncryptedDataLogged_) {
		Vehicle.attackerEncryptedDataLogged_ = attackerEncryptedDataLogged_;
	}


	public Node getCurMixNode_() {
		return curMixNode_;
	}


	public void setCurMixNode_(Node curMixNode_) {
		this.curMixNode_ = curMixNode_;
	}

	public static ReportingControlPanel getReportingPanel(){
		if(Renderer.getInstance().isConsoleStart()) return null;
		else return VanetSimStart.getMainControlPanel().getReportingPanel();
	}


	/**
	 * @return the waitingForSignal_
	 */
	public boolean isWaitingForSignal_() {
		return waitingForSignal_;
	}


	/**
	 * @param waitingForSignal_ the waitingForSignal_ to set
	 */
	public void setWaitingForSignal_(boolean waitingForSignal_) {
		this.waitingForSignal_ = waitingForSignal_;
	}


	public static boolean isPrivacyDataLogged_() {
		return privacyDataLogged_;
	}


	public static void setPrivacyDataLogged_(boolean privacyDataLogged_) {
		Vehicle.privacyDataLogged_ = privacyDataLogged_;
	}

	public void setTimeDistance(int timeDistance) {
		timeDistance_ = timeDistance;
	}


	public int getTimeDistance() {
		return timeDistance_;
	}

	public void setPoliteness(int politeness_) {
		this.politeness_ = politeness_;
	}


	public int getPoliteness() {
		return politeness_;
	}

	public static int getTIME_BETWEEN_SILENT_PERIODS() {
		return TIME_BETWEEN_SILENT_PERIODS;
	}

	public static void setTIME_BETWEEN_SILENT_PERIODS(int i){
		TIME_BETWEEN_SILENT_PERIODS = i;
	}

	public static int getTIME_OF_SILENT_PERIODS() {
		return TIME_OF_SILENT_PERIODS;
	}

	public static void setTIME_OF_SILENT_PERIODS(int i){
		TIME_OF_SILENT_PERIODS = i;
	}

	public static boolean isSilent_period() {
		return silent_period;
	}


	public static void setSilent_period(boolean silent_period) {
		Vehicle.silent_period = silent_period;
	}


	public static boolean isSilentPeriodsOn() {
		return silentPeriodsOn;
	}


	public static void setSilentPeriodsOn(boolean silentPeriodsOn) {
		Vehicle.silentPeriodsOn = silentPeriodsOn;
	}


	/**
	 * @return the tIME_TO_PSEUDONYM_CHANGE
	 */
	public static int getTIME_TO_PSEUDONYM_CHANGE() {
		return TIME_TO_PSEUDONYM_CHANGE;
	}


	/**
	 * @param tIME_TO_PSEUDONYM_CHANGE the tIME_TO_PSEUDONYM_CHANGE to set
	 */
	public static void setTIME_TO_PSEUDONYM_CHANGE(int tIME_TO_PSEUDONYM_CHANGE) {
		TIME_TO_PSEUDONYM_CHANGE = tIME_TO_PSEUDONYM_CHANGE;
	}


	/**
	 * @return the sLOW_SPEED_LIMIT
	 */
	public static int getSLOW_SPEED_LIMIT() {
		return SLOW_SPEED_LIMIT;
	}


	/**
	 * @param sLOW_SPEED_LIMIT the sLOW_SPEED_LIMIT to set
	 */
	public static void setSLOW_SPEED_LIMIT(int sLOW_SPEED_LIMIT) {
		SLOW_SPEED_LIMIT = sLOW_SPEED_LIMIT;
	}


	/**
	 * @return the slowOn
	 */
	public static boolean isSlowOn() {
		return slowOn;
	}


	/**
	 * @param slowOn the slowOn to set
	 */
	public static void setSlowOn(boolean slowOn) {
		Vehicle.slowOn = slowOn;
	}


	/**
	 * @return the isInSlow
	 */
	public boolean isInSlow() {
		return isInSlow;
	}


	/**
	 * @param isInSlow the isInSlow to set
	 */
	public void setInSlow(boolean isInSlow) {
		this.isInSlow = isInSlow;
	}


	/**
	 * @return the changedPseudonymInSlow
	 */
	public boolean isChangedPseudonymInSlow() {
		return changedPseudonymInSlow;
	}


	/**
	 * @param changedPseudonymInSlow the changedPseudonymInSlow to set
	 */
	public void setChangedPseudonymInSlow(boolean changedPseudonymInSlow) {
		this.changedPseudonymInSlow = changedPseudonymInSlow;
	}


	/**
	 * @return the slowTimestamp
	 */
	public int getSlowTimestamp() {
		return slowTimestamp;
	}


	/**
	 * @param slowTimestamp the slowTimestamp to set
	 */
	public void setSlowTimestamp(int slowTimestamp) {
		this.slowTimestamp = slowTimestamp;
	}


	/**
	 * @return the speedDeviation_
	 */
	public int getSpeedDeviation_() {
		return speedDeviation_;
	}


	/**
	 * @param speedDeviation_ the speedDeviation_ to set
	 */
	public void setSpeedDeviation_(int speedDeviation_) {
		this.speedDeviation_ = speedDeviation_;
	}


	/**
	 * @return the idsActivated
	 */
	public static boolean isIdsActivated() {
		return idsActivated;
	}


	/**
	 * @param idsActivated the idsActivated to set
	 */
	public static void setIdsActivated(boolean idsActivated) {
		Vehicle.idsActivated = idsActivated;
	}


	/**
	 * @return the moveOutOfTheWay
	 */
	public boolean isMoveOutOfTheWay_() {
		return moveOutOfTheWay_;
	}


	/**
	 * @param moveOutOfTheWay the moveOutOfTheWay to set
	 */
	public void setMoveOutOfTheWay_(boolean moveOutOfTheWay) {
		moveOutOfTheWay_ = moveOutOfTheWay;
	}


	/**
	 * @return the fakingMessages_
	 */
	public boolean isFakingMessages() {
		return fakingMessages_;
	}


	/**
	 * @param fakingMessages_ the fakingMessages_ to set
	 */
	public void setFakingMessages(boolean fakingMessages) {
		fakingMessages_ = fakingMessages;
	}


	/**
	 * @return the fakeMessageType
	 */
	public String getFakeMessageType() {
		return fakeMessageType_;
	}


	/**
	 * @param fakeMessageType the fakeMessageType to set
	 */
	public void setFakeMessageType(String fakeMessageType) {
		fakeMessageType_ = fakeMessageType;
	}


	/**
	 * @return the fakeMessagesInterval_
	 */
	public static int getFakeMessagesInterval_() {
		return fakeMessagesInterval_;
	}


	/**
	 * @param fakeMessagesInterval_ the fakeMessagesInterval_ to set
	 */
	public static void setFakeMessagesInterval_(int fakeMessagesInterval_) {
		Vehicle.fakeMessagesInterval_ = fakeMessagesInterval_;
	}



	/**
	 * @return the emergencyBrakingCountdown_
	 */
	public long getEmergencyBrakingCountdown_() {
		return emergencyBrakingCountdown_;
	}


	/**
	 * @param emergencyBrakingCountdown_ the emergencyBrakingCountdown_ to set
	 */
	public void setEmergencyBrakingCountdown_(int emergencyBrakingCountdown_) {
		this.emergencyBrakingCountdown_ = emergencyBrakingCountdown_;
	}


	/**
	 * @return the emergencyBrakingDuration_
	 */
	public int getEmergencyBrakingDuration_() {
		return emergencyBrakingDuration_;
	}


	/**
	 * @param emergencyBrakingDuration_ the emergencyBrakingDuration_ to set
	 */
	public void setEmergencyBrakingDuration_(int emergencyBrakingDuration_) {
		this.emergencyBrakingDuration_ = emergencyBrakingDuration_;
	}


	/**
	 * @return the stopTime_
	 */
	public int getStopTime_() {
		return stopTime_;
	}


	/**
	 * @param forwardMessage_ the forwardMessage_ to set
	 */
	public void setForwardMessage_(boolean forwardMessage_) {
		this.forwardMessage_ = forwardMessage_;
	}


	/**
	 * @return the random
	 */
	public static Random getRandom() {
		return RANDOM;
	}


	/**
	 * @return the doNotRecycle_
	 */
	public boolean isDoNotRecycle_() {
		return doNotRecycle_;
	}


	/**
	 * @param doNotRecycle_ the doNotRecycle_ to set
	 */
	public void setDoNotRecycle_(boolean doNotRecycle_) {
		this.doNotRecycle_ = doNotRecycle_;
	}


	/**
	 * @return the emergencyBeacons
	 */
	public int getEmergencyBeacons() {
		return emergencyBeacons;
	}


	/**
	 * @param emergencyBeacons the emergencyBeacons to set
	 */
	public void setEmergencyBeacons(int emergencyBeacons) {
		this.emergencyBeacons = emergencyBeacons;
	}


	/**
	 * @return the waitingForVehicle_
	 */
	public Vehicle getWaitingForVehicle_() {
		return waitingForVehicle_;
	}


	/**
	 * @param waitingForVehicle_ the waitingForVehicle_ to set
	 */
	public void setWaitingForVehicle_(Vehicle waitingForVehicle_) {
		this.waitingForVehicle_ = waitingForVehicle_;
	}


	/**
	 * @return the drivingOnTheSide_
	 */
	public boolean isDrivingOnTheSide_() {
		return drivingOnTheSide_;
	}


	/**
	 * @return the idsProcessorList_
	 */
	public IDSProcessorList getIdsProcessorList_() {
		return idsProcessorList_;
	}


	/**
	 * @return the passingBlocking_
	 */
	public boolean isPassingBlocking_() {
		return passingBlocking_;
	}


	/**
	 * @param passingBlocking_ the passingBlocking_ to set
	 */
	public void setPassingBlocking_(boolean passingBlocking_) {
		this.passingBlocking_ = passingBlocking_;
	}


	/**
	 * @return the pcnMessagesCreated_
	 */
	public int getPcnMessagesCreated_() {
		return pcnMessagesCreated_;
	}


	/**
	 * @return the pcnForwardMessagesCreated_
	 */
	public int getPcnForwardMessagesCreated_() {
		return pcnForwardMessagesCreated_;
	}


	/**
	 * @return the evaMessagesCreated_
	 */
	public int getEvaMessagesCreated_() {
		return evaMessagesCreated_;
	}


	/**
	 * @return the evaForwardMessagesCreated_
	 */
	public int getEvaForwardMessagesCreated_() {
		return evaForwardMessagesCreated_;
	}


	/**
	 * @return the rhcnMessagesCreated_
	 */
	public int getRhcnMessagesCreated_() {
		return rhcnMessagesCreated_;
	}


	/**
	 * @return the eeblMessagesCreated_
	 */
	public int getEeblMessagesCreated_() {
		return eeblMessagesCreated_;
	}


	/**
	 * @return the fakeMessagesCreated_
	 */
	public int getFakeMessagesCreated_() {
		return fakeMessagesCreated_;
	}


	public boolean isInTrafficJam_() {
		return inTrafficJam_;
	}


	public void setInTrafficJam_(boolean inTrafficJam_) {
		this.inTrafficJam_ = inTrafficJam_;
	}


	/**
	 * @return the checkIDSProcessors_
	 */
	public boolean isCheckIDSProcessors_() {
		return checkIDSProcessors_;
	}


	/**
	 * @param checkIDSProcessors_ the checkIDSProcessors_ to set
	 */
	public void setCheckIDSProcessors_(boolean checkIDSProcessors_) {
		this.checkIDSProcessors_ = checkIDSProcessors_;
	}


	public void setEmergencyBraking_(boolean emergencyBraking_) {
		this.emergencyBraking_ = emergencyBraking_;
	}


	public boolean isEmergencyBraking_() {
		return emergencyBraking_;
	}


	public boolean isEEBLmessageIsCreated_() {
		return EEBLmessageIsCreated_;
	}


	public void setEEBLmessageIsCreated_(boolean eEBLmessageIsCreated_) {
		EEBLmessageIsCreated_ = eEBLmessageIsCreated_;
	}


	public KnownEventSourcesList getKnownEventSourcesList_() {
		return knownEventSourcesList_;
	}


	public int getSpamCounter_() {
		return spamCounter_;
	}


	public void setSpamCounter_(int spamCounter_) {
		this.spamCounter_ = spamCounter_;
	}


	public int getMessagesCounter_() {
		return messagesCounter_;
	}


	public void setMessagesCounter_(int messagesCounter_) {
		this.messagesCounter_ = messagesCounter_;
	}


	public Random getOwnRandom_() {
		return ownRandom_;
	}


	public int getEVAMessageDelay_() {
		return EVAMessageDelay_;
	}

	public void setEVAMessageDelay_(int eVAMessageDelay_) {
		EVAMessageDelay_ = eVAMessageDelay_;
	}
	
	public static int getMaxEVAMessageDelay_() {
		return maxEVAMessageDelay_;
	}

	public static void setMaxEVAMessageDelay_(int theMaxEVAMessageDelay_) {
		maxEVAMessageDelay_ = theMaxEVAMessageDelay_;
	}


	public boolean isLogBeaconsAfterEvent_() {
		return logBeaconsAfterEvent_;
	}


	public void setLogBeaconsAfterEvent_(boolean logBeaconsAfterEvent_) {
		this.logBeaconsAfterEvent_ = logBeaconsAfterEvent_;
	}


	public int getAmountOfLoggedBeacons_() {
		return amountOfLoggedBeacons_;
	}


	public void setAmountOfLoggedBeacons_(int amountOfLoggedBeacons_) {
		this.amountOfLoggedBeacons_ = amountOfLoggedBeacons_;
	}


	public String getBeaconString_() {
		return beaconString_;
	}


	public void setBeaconString_(String beaconString_) {
		this.beaconString_ = beaconString_;
	}

} public class Cluster {
	private String clusterID_;
	
	private String eventType_;
	private int minX_ = Integer.MAX_VALUE;
	private int minY_ = Integer.MAX_VALUE;
	private int maxX_ = 0;
	private int maxY_ = 0;
	private int size_ = 0;
	private Color clusterColor = Color.black;

	private ArrayList<Integer> xCoords_ = new ArrayList<Integer>();


	private ArrayList<Integer> yCoords_ = new ArrayList<Integer>();

	
	public boolean fillCluster(String filePath, String clusterID){		
		String searchKey = "@data";
		
		clusterID_ = clusterID;
		
		try{
			// Open the file that is the first 
			// command line parameter
			FileInputStream fstream = new FileInputStream(filePath);
			// Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			String[] splitLine;
			//Read file line By line
			
			boolean found = false;
			
			while ((strLine = br.readLine()) != null) {
				if(strLine.equals(searchKey)) found = true;
				
				if(found){			
					//System.out.println(strLine):

					
				
					splitLine = strLine.split(",");
					
					if(splitLine[splitLine.length-1].equals("cluster" + clusterID_)){
						size_++;
						eventType_ = splitLine[1];
						
						if(eventType_.equals("HUANG_PCN")) clusterColor = Color.green;
						if(eventType_.equals("HUANG_EEBL")) clusterColor = Color.blue;
						if(eventType_.equals("PCN_FORWARD")) clusterColor = Color.pink;
						if(eventType_.equals("HUANG_RHCN")) clusterColor = Color.gray;
						if(eventType_.equals("EVA_FORWARD")) clusterColor = Color.cyan;
						if(eventType_.equals("HUANG_EVA")) clusterColor = Color.magenta;
						
						xCoords_.add(Integer.parseInt(splitLine[2]));
						yCoords_.add(Integer.parseInt(splitLine[3]));
						if(Integer.parseInt(splitLine[2]) > maxX_) maxX_ = Integer.parseInt(splitLine[2]);
						if(Integer.parseInt(splitLine[2]) < minX_) minX_ = Integer.parseInt(splitLine[2]);
						
						if(Integer.parseInt(splitLine[3]) > maxY_) maxY_ = Integer.parseInt(splitLine[3]);
						if(Integer.parseInt(splitLine[3]) < minY_) minY_ = Integer.parseInt(splitLine[3]);
					}
					

				}	
			}
			  
			//Close the input stream
			in.close();
		}catch (Exception e){//Catch exception if any
			System.err.println("Error: " + e.getMessage());
		}
		
		
		if(size_ > 0) return true;
		else return false;
	}
	
	public String toString(){
		return "cluster - " + clusterID_;
	}

	public String getEventType_() {
		return eventType_;
	}

	public int getMinX_() {
		return minX_;
	}

	public int getMinY_() {
		return minY_;
	}

	public int getMaxX_() {
		return maxX_;
	}

	public int getMaxY_() {
		return maxY_;
	}

	public int getSize_() {
		return size_;
	}

	public Color getClusterColor() {
		return clusterColor;
	}
	
	public ArrayList<Integer> getxCoords_() {
		return xCoords_;
	}

	public ArrayList<Integer> getyCoords_() {
		return yCoords_;
	}

	public String getClusterID_() {
		return clusterID_;
	}

	public void setClusterID_(String clusterID_) {
		this.clusterID_ = clusterID_;
	}
} public class EventSpot{
	
	/** Frequency the spot gets active. */
	private int frequency_;
	
	/** radius of the spot */
	private  int radius_;
	
	/** the x coordinate of the event spot */
	private int x_;
	
	/** the y coordinate of the event spot */
	private int y_;
	
	/** the type of event spot */
	private String eventSpotType_;
	
	/** the color of event spot */
	private Color eventSpotColor_;
	
	/** the next eventSpot timing */
	private int eventSpotTiming_;
	
	/** a reference to the next eventspot in the list */
	private EventSpot next_ = null;
	
	/** The hospital color */
	private static Color hospitalColor = Color.magenta;
	
	/** The icy road color */
	private static Color icyRoadColor = Color.cyan;
	
	/** The damaged road color */
	private static Color damagedRoadColor = Color.gray;
	
	/** The school zone color */
	private static Color schoolColor = Color.yellow;
	
	/** The fire station zone color */
	private static Color fire_stationColor = Color.red;
	
	/** The police zone color */
	private static Color policeColor = Color.blue;
	
	/** The school zone color */
	private static Color kindergartenColor = Color.green;
	
	/** A reference to the map so that we don't need to call this over and over again. */
	private static final Map MAP = Map.getInstance();

	/** the map regions */
	private static Region[][] regions_;
	
	/** the random number generator seed */
	private long seed_;
	
	/** A random number generator used for the creation of emergency vehicles. */
	private Random random_ = null;
	
	/** EventSpot Multiplier */
	private boolean multiplier_ = true;
	
	public EventSpot(int x, int y, int frequency, int radius, String eventSpotType, long seed){
		seed_ = seed;
		x_ = x;
		y_ = y;
		frequency_ = frequency;
		if(seed != -1)	random_ = new Random(seed);
		eventSpotTiming_ = random_.nextInt(frequency) + 1;
		if(SimulationMaster.getEventSpotCountdown_() > eventSpotTiming_) SimulationMaster.setEventSpotCountdown_(eventSpotTiming_); 
		radius_ = radius;
		eventSpotType_ = eventSpotType;
		if(eventSpotType.equals("hospital")) eventSpotColor_ = hospitalColor;
		else if(eventSpotType.equals("ice")) eventSpotColor_ = icyRoadColor;
		else if(eventSpotType.equals("streetDamage")) eventSpotColor_ = damagedRoadColor;
		else if(eventSpotType.equals("school")) eventSpotColor_ = schoolColor;
		else if(eventSpotType.equals("fire_station")) eventSpotColor_ = fire_stationColor;
		else if(eventSpotType.equals("police")) eventSpotColor_ = policeColor;
		else if(eventSpotType.equals("kindergarten")) eventSpotColor_ = kindergartenColor;

	}
	
	public void execute(int timePassed){
		eventSpotTiming_ += frequency_;
		
		if(eventSpotType_.equals("hospital") || eventSpotType_.equals("police") || eventSpotType_.equals("fire_station")){
			int maxX = x_ + radius_;
			int maxY = y_ + radius_;
			int minX = x_ - radius_;
			int minY = y_ - radius_;
	    	long dx = 0;
	    	long dy = 0;
	    	long maxCommDistanceSquared = (long)radius_*(long)radius_;
	    	int randomX = 0;
	    	int randomY = 0;
	   	    	
	    	
	    	WayPoint tmpWayPoint = null;

			ArrayDeque<WayPoint> destinations = null;
			Vehicle tmpVehicle;
			
			int k = 0;	
			int j = 0;
			destinations = new ArrayDeque<WayPoint>(2);	
			
			try {
				destinations.add(new WayPoint(x_,y_,0));
			} catch (ParseException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

			while(j < 1 && k < 99999){	// if snapping fails more than 99999 times break
				try{
					++k;
					
					while(true){
						randomX = minX + random_.nextInt((maxX - minX));
						randomY = minY + random_.nextInt((maxY - minY));
				    	dx = x_ - randomX;
				    	dy = y_ - randomY;
				    	if((dx * dx + dy * dy) <= maxCommDistanceSquared)	break;

					}
					
					tmpWayPoint = new WayPoint(randomX, randomY, 0);
					j++;
					destinations.add(tmpWayPoint);
				} catch (Exception e) {}
			}
			
			try {
				destinations.add(new WayPoint(x_,y_,0));
			} catch (ParseException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

			if(k < 99999) {
				try {
					tmpVehicle = new Vehicle(destinations, 600, 4722, 10000, true, true, 800, 300, 100, 0, 833,  new Color(124,148,235), false, "");
					tmpVehicle.setDoNotRecycle_(true);
					Map.getInstance().addVehicle(tmpVehicle);
				} catch (Exception e) {e.printStackTrace();}				
			}
		}
		else if(eventSpotType_.equals("ice") || eventSpotType_.equals("streetDamage")){
			int maxX = x_ + radius_;
			int maxY = y_ + radius_;
			int minX = x_ - radius_;
			int minY = y_ - radius_;
	    	long dx = 0;
	    	long dy = 0;
	    	long maxCommDistanceSquared = (long)radius_*(long)radius_;
	    	int randomX = 0;
	    	int randomY = 0;

			int k = 0;	
			int j = 0;
	
			while(j < 1 && k < 99999){	// if snapping fails more than 99999 times break
				try{
					++k;
					
					while(true){
						randomX = minX + random_.nextInt((maxX - minX));
						randomY = minY + random_.nextInt((maxY - minY));
				    	dx = x_ - randomX;
				    	dy = y_ - randomY;
				    	if((dx * dx + dy * dy) <= maxCommDistanceSquared){
				    		break;
				    	}
					}
					try{
						EventList.getInstance().addEvent(new StartBlocking((timePassed), randomX, randomY, 0, 99, false, "HUANG_RHCN")); //$NON-NLS-1$
						EventList.getInstance().addEvent(new StopBlocking((timePassed + frequency_),randomX,randomY)); //$NON-NLS-1$

						j++;
					}catch (Exception e2) { ErrorLog.log(Messages.getString("EditEventControlPanel.errorCreatingEvent"), 6, getClass().getName(), "actionPerformed", e2);} //$NON-NLS-1$ //$NON-NLS-2$
				} catch (Exception e) {}
			}
			Renderer.getInstance().ReRender(false, false);
		}
		else if(eventSpotType_.equals("school") || eventSpotType_.equals("kindergarten")){
			int RegionMinX, RegionMinY, RegionMaxX, RegionMaxY;
			long dx, dy, maxDistanceSquared = (long)radius_ * radius_;

			// Get the regions to be considered
			Region tmpregion = MAP.getRegionOfPoint((x_ - radius_), (y_ - radius_));
			RegionMinX = tmpregion.getX();
			RegionMinY = tmpregion.getY();

			tmpregion = MAP.getRegionOfPoint((x_ + radius_), (y_ + radius_));
			RegionMaxX = tmpregion.getX();
			RegionMaxY = tmpregion.getY();
			
			int i = 0;
			Vehicle tmpVehicle = null;
			while(i < 100){		//there could be some vehicles in the region but no vehicle in the zone!
				i++;
				Vehicle[] tmpVehicleArray = regions_[(RegionMinX + random_.nextInt(RegionMaxX - RegionMinX + 1))][(RegionMinY + random_.nextInt(RegionMaxY - RegionMinY + 1))].getVehicleArray();
				tmpVehicle = tmpVehicleArray[random_.nextInt(tmpVehicleArray.length)];
				
				//check if vehicle is driving. Otherwise a emergency braking does not make any sense!
				if(tmpVehicle.getCurSpeed() != 0){
					dx = tmpVehicle.getX() - x_;
					dy = tmpVehicle.getY() - y_;
					
					if((dx*dx + dy*dy) <= maxDistanceSquared) {

						PenaltyMessage message = new PenaltyMessage(tmpVehicle.getX(), tmpVehicle.getY(), tmpVehicle.getX(), tmpVehicle.getY(), 50000, (timePassed + 2000), tmpVehicle.getCurStreet(), tmpVehicle.getCurLane(), (int) tmpVehicle.getCurPosition(), 500, (timePassed + 2000), false, tmpVehicle.getID(), null,  "HUANG_EEBL", false, true);							
						tmpVehicle.getKnownMessages().addMessage(message, false, true);
						message.setFloodingMode(true);	// enable flooding mode if within distance!				
						//tmpVehicle.getTmpBlockings().add(new BlockingObject(tmpVehicle.getCurLane(), tmpVehicle.getCurDirection(), tmpVehicle.getCurStreet(),  tmpVehicle.getCurPosition(), "HUANG_EEBL", (timePassed + 2000)));
						//Renderer.getInstance().setMarkedVehicle(tmpVehicle);
					
						tmpVehicle.setEmergencyBraking_(true);
						tmpVehicle.setEEBLmessageIsCreated_(true);
						tmpVehicle.setEmergencyBrakingCountdown_(tmpVehicle.getEmergencyBrakingDuration_());
						if(!multiplier_) i = 100;
					}
				}
			}
		}
	}
	
	/**
	 * Gets the frequency of the event
	 * 
	 * @return the frequency
	 */
	public int getFrequency_(){
		return frequency_;
	}
	
	/**
	 * Gets the radius of the event spot
	 * 
	 * @return the radius of the spot
	 * 
	 * @see vanetsim.scenario.events.EventSpot#getRadius_()
	 */
	public int getRadius_() {
		return radius_;
	}

	/**
	 * @return the next_
	 */
	public EventSpot getNext_() {
		return next_;
	}

	/**
	 * @param next_ the next_ to set
	 */
	public void setNext_(EventSpot next_) {
		this.next_ = next_;
	}

	/**
	 * @return the x_
	 */
	public int getX_() {
		return x_;
	}

	/**
	 * @param x_ the x_ to set
	 */
	public void setX_(int x_) {
		this.x_ = x_;
	}

	/**
	 * @return the y_
	 */
	public int getY_() {
		return y_;
	}

	/**
	 * @param y_ the y_ to set
	 */
	public void setY_(int y_) {
		this.y_ = y_;
	}

	/**
	 * @return the eventSpotType_
	 */
	public String getEventSpotType_() {
		return eventSpotType_;
	}

	/**
	 * @param eventSpotType_ the eventSpotType_ to set
	 */
	public void setEventSpotType_(String eventSpotType_) {
		this.eventSpotType_ = eventSpotType_;
	}

	/**
	 * @return the eventSpotColor_
	 */
	public Color getEventSpotColor_() {
		return eventSpotColor_;
	}

	/**
	 * @param eventSpotColor_ the eventSpotColor_ to set
	 */
	public void setEventSpotColor_(Color eventSpotColor_) {
		this.eventSpotColor_ = eventSpotColor_;
	}

	/**
	 * @return the eventSpotTiming_
	 */
	public int getEventSpotTiming_() {
		return eventSpotTiming_;
	}

	/**
	 * @param eventSpotTiming_ the eventSpotTiming_ to set
	 */
	public void setEventSpotTiming_(int eventSpotTiming_) {
		this.eventSpotTiming_ = eventSpotTiming_;
	}

	/**
	 * @param regions_ the regions_ to set
	 */
	public static void setRegions_(Region[][] regions) {
		regions_ = regions;
	}

	public long getSeed_() {
		return seed_;
	}

	public  void setSeed_(long seed) {
		seed_ = seed;
	}
} public final class EventSpotList{

	/** The only instance of this class (singleton). */
	private static final EventSpotList INSTANCE = new EventSpotList();

	
	/** the head of the event spot */
	private EventSpot head_ = null;

	/** the grids for the event probabiltites */
	int[][] gridEEBL_ = null;
	int[][] gridPCN_ = null;
	int[][] gridPCNFORWARD_ = null;
	int[][] gridRHCN_ = null;
	int[][] gridEVAFORWARD_ = null;
	int[][] gridEVA_ = null;


	/**
	 * Empty, private constructor in order to disable instancing.
	 */
	private EventSpotList(){
	}	

	/**
	 * Gets the single instance of this EventList.
	 * 
	 * @return single instance of this EventList
	 */
	public static EventSpotList getInstance(){
		return INSTANCE;
	}

	/**
	 * Adds an event.
	 * 
	 * @param event the event
	 */
	public void addEventSpot(EventSpot event){
		if(head_ == null) head_ = event;
		else{
			// test if event spot already exists
			EventSpot tmpEvent = head_;
			while(tmpEvent.getNext_() != null) {
				if(tmpEvent.getX_() == event.getX_() &&tmpEvent.getY_() == event.getY_())return;
				tmpEvent = tmpEvent.getNext_();
			}
			
			
			tmpEvent = head_;
			while(tmpEvent.getNext_() != null) tmpEvent = tmpEvent.getNext_();
			tmpEvent.setNext_(event);
		}
	}

	/**
	 * Removes an event.
	 * 
	 * @param event the event
	 */
	public void delEventSpot(EventSpot event){
		EventSpot tmpEvent = head_;
		if(tmpEvent.equals(event)) head_ = head_.getNext_();
		else{
			while(tmpEvent.getNext_() != null){
				if(tmpEvent.getNext_().equals(event)){
					tmpEvent.setNext_(tmpEvent.getNext_().getNext_());
				}
				tmpEvent = tmpEvent.getNext_();
			}
		}
	}

	/**
	 * find event with coordinates
	 */
	public EventSpot findEventSpot(int x, int y){
		EventSpot tmpSpot = head_;
		
		while(tmpSpot != null){
			if(tmpSpot.getX_() > (x - 100) && tmpSpot.getX_() < (x + 100) && tmpSpot.getY_() > (y - 100) && tmpSpot.getY_() < (y + 100)) return tmpSpot;
			tmpSpot = tmpSpot.getNext_();
		}
			
		return null;
	}
	
	/**
	 * find next event spot timing
	 */
	public int findEventSpotTiming(){
		int returnValue = 999999999;
		EventSpot tmpSpot = head_;
		while(tmpSpot != null){
			if(returnValue > tmpSpot.getEventSpotTiming_()) returnValue = tmpSpot.getEventSpotTiming_();
			tmpSpot = tmpSpot.getNext_();
		}
		return returnValue;
	}
	
	/**
	 * runs next simulation step
	 */
	public int doStep(int time){
		if(head_ != null){
			EventSpot tmpSpot = head_;
			while(tmpSpot != null){
				if(tmpSpot.getEventSpotTiming_() <= time) {
					
					tmpSpot.execute(Renderer.getInstance().getTimePassed());
				}
				tmpSpot = tmpSpot.getNext_();
			}
			return findEventSpotTiming();
		}
		return 999999999;
	}
	
	/**
	 * Removes all events.
	 */
	public void clearEvents(){
		head_ = null;
	}

	/**
	 * @return the head_
	 */
	public EventSpot getHead_() {
		return head_;
	}

	/**
	 * @param head_ the head_ to set
	 */
	public void setHead_(EventSpot head_) {
		this.head_ = head_;
	}
	
	
	/**
	 * calculates the probabilities of events
	 */
	public void calculateGrid(double gridSize, String filePath, String selectedGrid){
		int cellAmountX = (int) Math.ceil((double)Map.getInstance().getMapWidth()/gridSize);
		int cellAmountY = (int) Math.ceil((double)Map.getInstance().getMapHeight()/gridSize);
		gridEVA_ = new int[cellAmountX][cellAmountY];
		gridPCN_ = new int[cellAmountX][cellAmountY];
		gridEEBL_ = new int[cellAmountX][cellAmountY];
		gridPCNFORWARD_ = new int[cellAmountX][cellAmountY];
		gridRHCN_ = new int[cellAmountX][cellAmountY];
		gridEVAFORWARD_ = new int[cellAmountX][cellAmountY];
		System.out.println(Map.getInstance().getMapWidth() + ":cellAX:" + cellAmountX + " :cellAY:" + cellAmountY);
		
		try{
			// Open the file that is the first 
			// command line parameter
			FileInputStream fstream = new FileInputStream(filePath);
			// Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			String[] strParse = null;
			ArrayList<String> sendersOfThisRound = new ArrayList<String>();
			String currentTime = "0";
			//Read file line By line
			while ((strLine = br.readLine()) != null) {
			//	System.out.println(strLine);
				strParse = strLine.split(":");
				if(Integer.parseInt(strParse[4]) > 0 && Integer.parseInt(strParse[6]) > 0){
					if(!currentTime.equals(strParse[0])){
						sendersOfThisRound.clear();
						currentTime = strParse[0];
					}
					
					if(!sendersOfThisRound.contains(strParse[8])){
						if(strParse[2].equals("HUANG_PCN")){
							gridPCN_[((int) Math.ceil(Integer.parseInt(strParse[4])/gridSize))-1][((int) Math.ceil(Integer.parseInt(strParse[6])/gridSize))-1]++;
						}
						else if(strParse[2].equals("HUANG_EEBL")){
							gridEEBL_[((int) Math.ceil(Integer.parseInt(strParse[4])/gridSize))-1][((int) Math.ceil(Integer.parseInt(strParse[6])/gridSize))-1]++;
						}
						else if(strParse[2].equals("PCN_FORWARD")){
							gridPCNFORWARD_[((int) Math.ceil(Integer.parseInt(strParse[4])/gridSize))-1][((int) Math.ceil(Integer.parseInt(strParse[6])/gridSize))-1]++;
						}
						else if(strParse[2].equals("HUANG_RHCN")){
							gridRHCN_[((int) Math.ceil(Integer.parseInt(strParse[4])/gridSize))-1][((int) Math.ceil(Integer.parseInt(strParse[6])/gridSize))-1]++;
						}
						else if(strParse[2].equals("HUANG_EVA_FORWARD")){
							gridEVAFORWARD_[((int) Math.ceil(Integer.parseInt(strParse[4])/gridSize))-1][((int) Math.ceil(Integer.parseInt(strParse[6])/gridSize))-1]++;
						}
						else if(strParse[2].equals("EVA_EMERGENCY_ID")){
							gridEVA_[((int) Math.ceil(Integer.parseInt(strParse[4])/gridSize))-1][((int) Math.ceil(Integer.parseInt(strParse[6])/gridSize))-1]++;
						}
						
						sendersOfThisRound.add(strParse[8]);
					}

				}

			}

			showGrid(selectedGrid, (int)gridSize);
			saveGrid(filePath.substring(0, filePath.length() - 4) + "grid_" + gridSize + ".log", (int)gridSize);
			//Close the input stream
			in.close();
		}catch (Exception e){//Catch exception if any
			e.printStackTrace();
		}
	}

	public void createIntelligentStreetCluster(){
		//use to find nearest street
		//MapHelper.findNearestStreet(x,y, 10000, new double[1], nearestpoint);	// search in 100m radius for the starting street
	}
	/**
	 * save the grid to file
	 */
	public void saveGrid(String filePath, int gridSize){
		try{
			// Create file 
			FileWriter fstream = new FileWriter(filePath);
			BufferedWriter out = new BufferedWriter(fstream);
			out.write("****:" + gridEEBL_.length + ":" + gridEEBL_[0].length + ":" + gridSize + "\n");
			out.write("HUANG_EEBL\n");
			for(int i = 0; i < gridEEBL_.length; i++){
				for(int j = 0; j < gridEEBL_[0].length - 1; j++){
					out.write(gridEEBL_[i][j] + ":");
				}
				out.write(gridEEBL_[i][gridEEBL_[0].length-1] + "\n");
			}
			out.write("HUANG_PCN\n");
			for(int i = 0; i < gridPCN_.length; i++){
				for(int j = 0; j < gridPCN_[0].length - 1; j++){
					out.write(gridPCN_[i][j] + ":");
				}
				out.write(gridPCN_[i][gridPCN_[0].length-1] + "\n");
			}
			out.write("PCN_FORWARD\n");
			for(int i = 0; i < gridPCNFORWARD_.length; i++){
				for(int j = 0; j < gridPCNFORWARD_[0].length - 1; j++){
					out.write(gridPCNFORWARD_[i][j] + ":");
				}
				out.write(gridPCNFORWARD_[i][gridPCNFORWARD_[0].length-1] + "\n");
			}
			out.write("HUANG_RHCN\n");
			for(int i = 0; i < gridRHCN_.length; i++){
				for(int j = 0; j < gridRHCN_[0].length - 1; j++){
					out.write(gridRHCN_[i][j] + ":");
				}
				out.write(gridRHCN_[i][gridRHCN_[0].length-1] + "\n");
			}
			out.write("HUANG_EVA_FORWARD\n");
			for(int i = 0; i < gridEVAFORWARD_.length; i++){
				for(int j = 0; j < gridEVAFORWARD_[0].length - 1; j++){
					out.write(gridEVAFORWARD_[i][j] + ":");
				}
				out.write(gridEVAFORWARD_[i][gridEVAFORWARD_[0].length-1] + "\n");
			}
			out.write("EVA_EMERGENCY_ID\n");
			for(int i = 0; i < gridEVA_.length; i++){
				for(int j = 0; j < gridEVA_[0].length - 1; j++){
					out.write(gridEVA_[i][j] + ":");
				}
				out.write(gridEVA_[i][gridEVA_[0].length-1] + "\n");
			}
			//Close the output stream
			out.close();
		}catch (Exception e){//Catch exception if any
			System.err.println("Error: " + e.getMessage());
	  }
	}
	
	/**
	 * load the grid
	 */
	public void loadGrid(String filepath, String selectedGrid){	
		  int sizeX = 0;
		  int sizeY = 0;
		  int gridSize = 0;
		  int[][] grid = null;
		  int counter = 0;
		  
		  try{
			  // Open the file that is the first 
			  	// command line parameter
			  FileInputStream fstream = new FileInputStream(filepath);
			  // Get the object of DataInputStream
			  DataInputStream in = new DataInputStream(fstream);
			  BufferedReader br = new BufferedReader(new InputStreamReader(in));
			  String strLine;

			  //Read File Line By Line
			  while ((strLine = br.readLine()) != null)   {
				  if(strLine.length() > 4){
					  if(strLine.substring(0, 4).equals("****")){
						  sizeX = Integer.parseInt(strLine.split(":")[1]);
						  sizeY = Integer.parseInt(strLine.split(":")[2]);
						  gridSize = Integer.parseInt(strLine.split(":")[3]);
					  }
					  else if(strLine.equals("HUANG_PCN")){
						  gridPCN_ = new int[sizeX][sizeY];
						  grid = gridPCN_;
						  counter = 0;
					  }
					  else if(strLine.equals("HUANG_EEBL")){
						  gridEEBL_ = new int[sizeX][sizeY];
						  grid = gridEEBL_;
						  counter = 0;
					  }
					  else if(strLine.equals("PCN_FORWARD")){
						  gridPCNFORWARD_ = new int[sizeX][sizeY];
						  grid = gridPCNFORWARD_;
						  counter = 0;
					  }
					  else if(strLine.equals("HUANG_RHCN")){
						  gridRHCN_ = new int[sizeX][sizeY];
						  grid = gridRHCN_;
						  counter = 0;
					  }
					  else if(strLine.equals("HUANG_EVA_FORWARD")){
						  gridEVAFORWARD_ = new int[sizeX][sizeY];
						  grid = gridEVAFORWARD_;
						  counter = 0;
					  }
					  else if(strLine.equals("EVA_EMERGENCY_ID")){
						  gridEVA_ = new int[sizeX][sizeY];
						  grid = gridEVA_;
						  counter = 0;
					  }
					  else{
						  for(int i = 0; i < strLine.split(":").length; i++){
							  grid[counter][i] = Integer.parseInt(strLine.split(":")[i]);
						  }
						  counter++;
					  }
				  }
			  }
			  //Close the input stream
			  in.close();
		  }catch (Exception e){//Catch exception if any
			  System.err.println("Error: " + e.getMessage());
		  }
		  VanetSimStart.getMainControlPanel().getEditPanel().getEditEventSpotsPanel_().getGridSize_().setValue(gridSize);
		  showGrid(selectedGrid, gridSize);
	}
	
	/**
	 * shows the selected grid on the map
	 */
	public void showGrid(String selectedGrid, int gridSize){
		if(selectedGrid.equals("HUANG_PCN")) Renderer.getInstance().setGrid(gridPCN_);
		else if(selectedGrid.equals("HUANG_EEBL")) Renderer.getInstance().setGrid(gridEEBL_);
		if(selectedGrid.equals("PCN_FORWARD")) Renderer.getInstance().setGrid(gridPCNFORWARD_);
		if(selectedGrid.equals("HUANG_RHCN")) Renderer.getInstance().setGrid(gridRHCN_);
		if(selectedGrid.equals("HUANG_EVA_FORWARD")) Renderer.getInstance().setGrid(gridEVAFORWARD_);
		if(selectedGrid.equals("EVA_EMERGENCY_ID")) Renderer.getInstance().setGrid(gridEVA_);

		int[][] grid = Renderer.getInstance().getGrid();
		int min = 999999999;
		int max = 0;
		
		for(int i = 0; i < grid.length;i++){
			for(int j = 0; j < grid[0].length; j++){
				if(grid[i][j] > max) max = grid[i][j];
				if(grid[i][j] < min) min = grid[i][j];
			}
		}
		
		Renderer.getInstance().setMinGridValue(min);
		Renderer.getInstance().setMaxGridValue(max);
		Renderer.getInstance().setGridSize_(gridSize);
		
		Renderer.getInstance().ReRender(true, true);
	}
	
	
	
	/**
	 * @return the gridEVA_
	 */
	public int[][] getGridEVA_() {
		return gridEVA_;
	}

	/**
	 * @param gridEVA_ the gridEVA_ to set
	 */
	public void setGridEVA_(int[][] gridEVA_) {
		this.gridEVA_ = gridEVA_;
	}



}
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.NumberFormat;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.filechooser.FileFilter;
import vanetsim.gui.Renderer;
import vanetsim.gui.helpers.ButtonCreator;
import vanetsim.gui.helpers.WekaHelper;
import vanetsim.localization.Messages;
import vanetsim.map.Map;
import vanetsim.map.Node;
import vanetsim.scenario.IDSProcessor;
import vanetsim.scenario.Vehicle;
import vanetsim.scenario.events.Cluster;
import vanetsim.scenario.events.EventSpot;
import vanetsim.scenario.events.EventSpotList;

		JButton createEventProbabilities = new JButton(Messages.getString("EditEventSpotsControlPanel.createProbabilities"));
		createEventProbabilities.setActionCommand("createProbabilities");
		createEventProbabilities.setPreferredSize(new Dimension(200,20));
		createEventProbabilities.addActionListener(this);
		add(createEventProbabilities,c);
		
		//open Event probabilities
		++c.gridy;
		c.gridx = 0;
		JButton openEventProbabilities = new JButton(Messages.getString("EditEventSpotsControlPanel.openProbabilities"));
		openEventProbabilities.setActionCommand("openProbabilities");
		openEventProbabilities.setPreferredSize(new Dimension(200,20));
		openEventProbabilities.addActionListener(this);
		add(openEventProbabilities,c);
		
		//import centroids from weka
		++c.gridy;
		c.gridx = 0;
		JButton importCentroids = new JButton(Messages.getString("EditEventSpotsControlPanel.importCentroids"));
		importCentroids.setActionCommand("importCentroids");
		importCentroids.setPreferredSize(new Dimension(200,20));
		importCentroids.addActionListener(this);
		add(importCentroids,c);
		
		//import clusters from weka
		++c.gridy;
		c.gridx = 0;
		JButton importClusters = new JButton(Messages.getString("EditEventSpotsControlPanel.importClusters"));
		importClusters.setActionCommand("importClusters");
		importClusters.setPreferredSize(new Dimension(200,20));
		importClusters.addActionListener(this);
		add(importClusters,c);
		
		//select cluster
		c.gridx = 0;
		label = new JLabel(Messages.getString("EditEventSpotsControlPanel.selectCluster")); //$NON-NLS-1$
		++c.gridy;
		add(label,c);
		clusterSelection_ = new JComboBox<Cluster>();
		clusterSelection_.setActionCommand("selectCluster");
		clusterSelection_.addActionListener(this);
		//for(int i = 0; i < IDSProcessor.getIdsData_().length; i++) eventType_.addItem(IDSProcessor.getIdsData_()[i]);

		c.gridx = 1;
		add(clusterSelection_, c);
		
		c.gridx = 0;
		label = new JLabel(Messages.getString("EditEventSpotsControlPanel.eventSpotGrid")); //$NON-NLS-1$
		++c.gridy;
		add(label,c);		
		gridSize_ = new JFormattedTextField(NumberFormat.getIntegerInstance());
		gridSize_.setValue(10000);
		gridSize_.setPreferredSize(new Dimension(60,20));
		c.gridx = 1;
		add(gridSize_,c);
		
		
		//select event type
		c.gridx = 0;
		label = new JLabel(Messages.getString("EditEventSpotsControlPanel.eventTypes")); //$NON-NLS-1$
		++c.gridy;
		add(label,c);
		eventType_ = new JComboBox<String>();
		for(int i = 0; i < IDSProcessor.getIdsData_().length; i++) eventType_.addItem(IDSProcessor.getIdsData_()[i]);

		c.gridx = 1;
		add(eventType_, c);
		
		//load Event probabilities
		++c.gridy;
		c.gridx = 0;
		JButton loadEventProbabilities = new JButton(Messages.getString("EditEventSpotsControlPanel.refreshProbabilities"));
		loadEventProbabilities.setActionCommand("refreshProbabilities");
		loadEventProbabilities.setPreferredSize(new Dimension(200,20));
		loadEventProbabilities.addActionListener(this);
		add(loadEventProbabilities,c);
		

		

		
		++c.gridy;
		c.gridwidth = 2;
		c.gridx = 0;	
		add(new JSeparator(SwingConstants.HORIZONTAL),c);
		
		c.gridx = 0;
		c.gridwidth = 2;
		++c.gridy;
		deleteAllSpots_ = ButtonCreator.getJButton("deleteAll.png", "clearSpots", Messages.getString("EditEventSpotsControlPanel.deleteAllSpots"), this);
		add(deleteAllSpots_,c);	
		
		
		//define FileFilter for fileChooser
		logFileFilter_ = new FileFilter(){
			public boolean accept(File f) {
				if (f.isDirectory()) return true;
				return f.getName().toLowerCase().endsWith(".log"); //$NON-NLS-1$
			}
			public String getDescription () { 
				return Messages.getString("LogAnalyserDialog.logFiles") + " (*.log)"; //$NON-NLS-1$ //$NON-NLS-2$
			}
		};
		
		//to consume the rest of the space
		c.weighty = 1.0;
		++c.gridy;
		space_ = new JPanel();
		space_.setOpaque(false);
		add(space_, c);
	}
	
	/**
	 * Receives a mouse event.
	 * 
	 * @param x	the x coordinate (in map scale)
	 * @param y	the y coordinate (in map scale)
	 */
	public void receiveMouseEvent(int x, int y){
		if(addItem_.isSelected()){

				EventSpotList.getInstance().addEventSpot(new EventSpot(x,y, ((Number)eventSpotFrequency_.getValue()).intValue()*1000, ((Number)eventSpotRadius_.getValue()).intValue()*100, returnAmenityCode(eventSpotType_.getSelectedItem().toString()), Vehicle.getRandom().nextLong()));
