package se.oru.coordination.coordination_oru.util;

import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.metacsp.multi.spatial.DE9IM.GeometricShapeDomain;
import org.metacsp.multi.spatioTemporal.paths.Pose;
import org.metacsp.multi.spatioTemporal.paths.TrajectoryEnvelope;
import org.ros.RosCore;
import org.ros.concurrent.CancellableLoop;
import org.ros.message.Duration;
import org.ros.namespace.GraphName;
import org.ros.node.ConnectedNode;
import org.ros.node.DefaultNodeMainExecutor;
import org.ros.node.Node;
import org.ros.node.NodeConfiguration;
import org.ros.node.NodeMain;
import org.ros.node.NodeMainExecutor;
import org.ros.node.topic.Publisher;

import com.vividsolutions.jts.geom.Coordinate;

import geometry_msgs.Transform;
import nav_msgs.OccupancyGrid;
import se.oru.coordination.coordination_oru.RobotReport;
import se.oru.coordination.coordination_oru.util.FleetVisualization;
import se.oru.coordination.coordination_oru.util.Missions;
import com.vividsolutions.jts.geom.Polygon;
//import org.ros.visualization_msgs.MarkerArray;
import visualization_msgs.MarkerArray;

public class RVizVisualization implements FleetVisualization, NodeMain {

	//private final String mapFrameID = "/map_laser2d";
	private String mapFrameID = "/map";
	private ConnectedNode node = null;
	private HashMap<String,Publisher<visualization_msgs.MarkerArray>> boxMarkerPublishers = null;
	private HashMap<String,ArrayList<visualization_msgs.Marker>> boxMarkerMarkers = null;

	private HashMap<Integer,Publisher<visualization_msgs.MarkerArray>> robotStatusPublishers = null;
	private HashMap<Integer,Publisher<visualization_msgs.MarkerArray>> dependencyPublishers = null;
	private HashMap<Integer,ArrayList<visualization_msgs.Marker>> robotStatusMarkers = null;
	private HashMap<Integer,ArrayList<visualization_msgs.Marker>> dependencyMarkers = null;
	private HashMap<Integer,visualization_msgs.Marker> envelopeMarkers = null;
	private boolean ready = false;
	private String mapFileName = null;
	private boolean darkColors = true;
	
	private static String rvizEntry = ""+
			"    - Class: rviz/MarkerArray\n" + 
			"      Enabled: true\n" + 
			"      Marker Topic: /ROBOTi/deps\n" + 
			"      Name: MarkerArray\n" + 
			"      Namespaces:\n" + 
			"        {}\n" + 
			"      Queue Size: 100\n" + 
			"      Value: true\n" + 
			"    - Class: rviz/MarkerArray\n" + 
			"      Enabled: true\n" + 
			"      Marker Topic: /ROBOTi/status\n" + 
			"      Name: MarkerArray\n" + 
			"      Namespaces:\n" + 
			"        {}\n" + 
			"      Queue Size: 100\n" + 
			"      Value: true\n";

	public static void writeRVizConfigFile(int ... robotIDs) {
		try {
			File file = new File(System.getProperty("user.home")+File.separator+"config.rviz");
			
			ClassLoader loader = Thread.currentThread().getContextClassLoader();
			
			//Read pre
			InputStream is = loader.getResourceAsStream("coordinator_default_config_pre.rviz");
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			String output = "";
			String oneLine = null;
			while ((oneLine = br.readLine()) != null) {
				output += (oneLine+"\n");  
			}
			br.close();
			is.close();
			
			//Make robot specific entries
			for (int robotID : robotIDs) {
            	output += rvizEntry.replaceAll("ROBOTi", "robot"+robotID);
            }
			
			//Read post
			is = loader.getResourceAsStream("coordinator_default_config_post.rviz");
			br = new BufferedReader(new InputStreamReader(is));
			oneLine = null;
			while ((oneLine = br.readLine()) != null) {
				output += (oneLine+"\n");  
			}
			br.close();
			is.close();
			
			//Dump it out
            PrintWriter writer = new PrintWriter(file);
            writer.write(output);
            writer.close();
		}
		catch (IOException e) { e.printStackTrace(); }

	}
	
	public RVizVisualization() {
		this("/map");
	}
	
	public RVizVisualization(String mapFrameID) {
		this(true,mapFrameID);
	}

	public RVizVisualization(ConnectedNode node) {
		this(node,"/map");
	}
	
	public RVizVisualization(ConnectedNode node, String mapFrameID) {
		this(false, mapFrameID);
		this.node = node;
		this.ready = true;
	}

	public RVizVisualization(boolean startROSCore) {
		this(startROSCore,"/map");
	}
	
	public RVizVisualization(boolean startROSCore, String mapFrameID) {
		this.mapFrameID = mapFrameID;
		this.robotStatusPublishers = new HashMap<Integer,Publisher<visualization_msgs.MarkerArray>>();
		this.dependencyPublishers = new HashMap<Integer,Publisher<visualization_msgs.MarkerArray>>();
		this.robotStatusMarkers = new HashMap<Integer,ArrayList<visualization_msgs.Marker>>();
		this.dependencyMarkers = new HashMap<Integer,ArrayList<visualization_msgs.Marker>>();

		this.boxMarkerPublishers = new HashMap<String,Publisher<visualization_msgs.MarkerArray>>();
		this.boxMarkerMarkers = new HashMap<String,ArrayList<visualization_msgs.Marker>>();
		
		if (startROSCore) {
			NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic("localhost");
			NodeMainExecutor executor = DefaultNodeMainExecutor.newDefault();
			executor.execute(this, nodeConfiguration);
	
			RosCore mRosCore = RosCore.newPublic("localhost", 11311);
			mRosCore.start();
			try {
				mRosCore.awaitStart(5, TimeUnit.SECONDS);
			}
			catch (InterruptedException e) { e.printStackTrace(); }
			System.out.println("ROS-core started");
		}
	}
	
	public void setDarkColors(boolean dark) {
		this.darkColors = dark;
	}

	private BufferedImage toGrayScale(BufferedImage imgIn) {
		BufferedImage img = new BufferedImage(imgIn.getWidth(), imgIn.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
        ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
        op.filter(imgIn, img);
        return img;
	}
	
	private BufferedImage toBlackAndWhite(BufferedImage image, int threshold) {
	    BufferedImage result = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
	    result.getGraphics().drawImage(image, 0, 0, null);
	    WritableRaster raster = result.getRaster();
	    int[] pixels = new int[image.getWidth()];
	    for (int y = 0; y < image.getHeight(); y++) {
	        raster.getPixels(0, y, image.getWidth(), 1, pixels);
	        for (int i = 0; i < pixels.length; i++) {
	            if (pixels[i] < threshold) pixels[i] = 0;
	            else pixels[i] = 255;
	        }
	        raster.setPixels(0, y, image.getWidth(), 1, pixels);
	    }
	    return result;
	}
	
	private BufferedImage flipVertically(BufferedImage imgIn) {
        AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
        tx.translate(0, -imgIn.getHeight(null));
        AffineTransformOp op1 = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
        imgIn = op1.filter(imgIn, null);
        return imgIn;
	}

	private BufferedImage flipHorizontally(BufferedImage imgIn) {
		AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
		tx.translate(-imgIn.getWidth(null), 0);
		AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
		imgIn = op.filter(imgIn, null);        
        return imgIn;
	}

	@Override
	public void setMap(String yamlFile) {
		String prefix = yamlFile.substring(0, yamlFile.indexOf(File.separator));
		this.setMapFileName(yamlFile, prefix, false, true);
	}
	
	public void setMapFileName(String mapYAMLFile, String prefix, boolean flipHorizontally, boolean flipVertically) {
		this.mapFileName = Missions.getProperty("image", mapYAMLFile);
		if (prefix != null) this.mapFileName = prefix + File.separator + this.mapFileName;
		while (!ready) {
			try { Thread.sleep(100); }
			catch (InterruptedException e) { e.printStackTrace(); }
		}
		if (mapFileName != null) {
			try {
				final OccupancyGrid occMap = node.getTopicMessageFactory().newFromType(OccupancyGrid._TYPE);
				BufferedImage img = ImageIO.read(new File(mapFileName));
				//img = toGrayScale(img);
				img = toBlackAndWhite(img, 128);
				if (flipHorizontally) img = flipHorizontally(img);
				if (flipVertically) img = flipVertically(img);
				System.out.println("Loaded map: " + img.getHeight() + "x" + img.getWidth());
				WritableRaster raster = img.getRaster();
				DataBufferByte data = (DataBufferByte)raster.getDataBuffer();
				ChannelBuffer buffer = ChannelBuffers.copiedBuffer(ByteOrder.nativeOrder(), data.getData());
				occMap.setData(buffer);
				occMap.getHeader().setFrameId(mapFrameID);
				occMap.getInfo().setHeight((int)(img.getHeight()));
				occMap.getInfo().setWidth((int)(img.getWidth()));
				geometry_msgs.Pose pose = node.getTopicMessageFactory().newFromType(geometry_msgs.Pose._TYPE);
				pose.getPosition().setX(0);
				pose.getPosition().setY(0);
				occMap.getInfo().setOrigin(pose);
				double res = Double.parseDouble(Missions.getProperty("resolution", mapYAMLFile));
				occMap.getInfo().setResolution((float)res);
				final Publisher<OccupancyGrid> publisher = node.newPublisher("/map", OccupancyGrid._TYPE);
				node.executeCancellableLoop(new CancellableLoop() {
					@Override
					protected void loop() throws InterruptedException {
						publisher.publish(occMap);
						Thread.sleep(1000);
					}
				});

			}
			catch (IOException e) { e.printStackTrace(); }	
		}

	}


	@Override
	public void displayRobotState(TrajectoryEnvelope te, RobotReport rr, String ... extraStatusInfo) {
		if (ready) {
			double x = rr.getPose().getX();
			double y = rr.getPose().getY();
			double theta = rr.getPose().getTheta();

			visualization_msgs.Marker marker = node.getTopicMessageFactory().newFromType(visualization_msgs.Marker._TYPE);
			marker.getHeader().setFrameId(mapFrameID);
			marker.getScale().setX(0.2f);
			marker.getColor().setR(100f);
			marker.getColor().setG(0.0f);
			marker.getColor().setB(0.0f);
			marker.getColor().setA(0.8f);
			marker.setAction(visualization_msgs.Marker.ADD);                                
			marker.setNs("current_pose");
			marker.setType(visualization_msgs.Marker.LINE_STRIP);
			marker.setId(rr.getRobotID());
			marker.setLifetime(new Duration(10.0));

			ArrayList<geometry_msgs.Point> points = new ArrayList<geometry_msgs.Point>();
			Coordinate[] coords = TrajectoryEnvelope.getFootprint(te.getFootprint(), x, y, theta).getCoordinates();
			for (Coordinate coord : coords) {
				geometry_msgs.Point point = node.getTopicMessageFactory().newFromType(geometry_msgs.Point._TYPE);
				point.setX(coord.x);
				point.setY(coord.y);
				point.setZ(0.0);
				points.add(point);
			}
			points.add(points.get(0));
			marker.setPoints(points);
			if (!this.robotStatusPublishers.containsKey(rr.getRobotID())) {
				Publisher<visualization_msgs.MarkerArray> markerArrayPublisher = node.newPublisher("robot"+rr.getRobotID()+"/status", visualization_msgs.MarkerArray._TYPE);
				this.robotStatusPublishers.put(rr.getRobotID(), markerArrayPublisher);
				synchronized(robotStatusMarkers) {
					this.robotStatusMarkers.put(rr.getRobotID(), new ArrayList<visualization_msgs.Marker>());
				}
			}
			synchronized(robotStatusMarkers) {
				this.robotStatusMarkers.get(rr.getRobotID()).add(marker);
			}

			//////////////
			visualization_msgs.Marker markerName = node.getTopicMessageFactory().newFromType(visualization_msgs.Marker._TYPE);
			markerName.getHeader().setFrameId(mapFrameID);
			markerName.getScale().setX(1.0f);
			markerName.getScale().setY(1.0f);
			markerName.getScale().setZ(1.0f);
			float R = 0.0f;
			float G = 0.0f;
			float B = 0.0f;
			float A = 1.0f;
			if (darkColors) {
				R = 1.0f;
				G = 1.0f;
				B = 1.0f;
				A = 0.8f;
			}
			markerName.getColor().setR(R);
            markerName.getColor().setG(G);
            markerName.getColor().setB(B);
            markerName.getColor().setA(A);
			markerName.setAction(visualization_msgs.Marker.ADD);                                
			markerName.setNs("robot_state");
			markerName.setType(visualization_msgs.Marker.TEXT_VIEW_FACING);
			//markerName.setId(te.getRobotID());
			markerName.setLifetime(new Duration(10.0));
			String markerText  = "R" + te.getRobotID() + ": " + rr.getPathIndex();
			if (extraStatusInfo != null) for (String extra : extraStatusInfo) markerText += "\n" + extra;
			markerName.setText(markerText);
			geometry_msgs.Pose pose = node.getTopicMessageFactory().newFromType(geometry_msgs.Pose._TYPE);
			geometry_msgs.Point pos = node.getTopicMessageFactory().newFromType(geometry_msgs.Point._TYPE);
			pos.setX(x);
			pos.setY(y);
			pos.setZ(0);
			pose.setPosition(pos);
			markerName.setPose(pose);
			synchronized(robotStatusMarkers) {
				this.robotStatusMarkers.get(rr.getRobotID()).add(markerName);
			}

		}
	}
	
	@Override
	public void displayRobotState(Polygon fp, RobotReport rr, String ... extraStatusInfo) {
		if (ready) {
			double x = rr.getPose().getX();
			double y = rr.getPose().getY();
			double theta = rr.getPose().getTheta();

			visualization_msgs.Marker marker = node.getTopicMessageFactory().newFromType(visualization_msgs.Marker._TYPE);
			marker.getHeader().setFrameId(mapFrameID);
			marker.getScale().setX(0.2f);
			marker.getColor().setR(100.0f);
			marker.getColor().setG(0.0f);
			marker.getColor().setB(0.0f);
			marker.getColor().setA(0.8f);
			marker.setAction(visualization_msgs.Marker.ADD);                                
			marker.setNs("current_pose");
			marker.setType(visualization_msgs.Marker.LINE_STRIP);
			marker.setId(rr.getRobotID());
			marker.setLifetime(new Duration(10.0));

			ArrayList<geometry_msgs.Point> points = new ArrayList<geometry_msgs.Point>();
			Coordinate[] coords = TrajectoryEnvelope.getFootprint(fp, x, y, theta).getCoordinates();
			for (Coordinate coord : coords) {
				geometry_msgs.Point point = node.getTopicMessageFactory().newFromType(geometry_msgs.Point._TYPE);
				point.setX(coord.x);
				point.setY(coord.y);
				point.setZ(0.0);
				points.add(point);
			}
			points.add(points.get(0));
			marker.setPoints(points);
			if (!this.robotStatusPublishers.containsKey(rr.getRobotID())) {
				Publisher<visualization_msgs.MarkerArray> markerArrayPublisher = node.newPublisher("robot"+rr.getRobotID()+"/status", visualization_msgs.MarkerArray._TYPE);
				this.robotStatusPublishers.put(rr.getRobotID(), markerArrayPublisher);
				synchronized(robotStatusMarkers) {
					this.robotStatusMarkers.put(rr.getRobotID(), new ArrayList<visualization_msgs.Marker>());
				}
			}
			synchronized(robotStatusMarkers) {
				this.robotStatusMarkers.get(rr.getRobotID()).add(marker);
			}

			//////////////
			visualization_msgs.Marker markerName = node.getTopicMessageFactory().newFromType(visualization_msgs.Marker._TYPE);
			markerName.getHeader().setFrameId(mapFrameID);
			markerName.getScale().setX(1.0f);
			markerName.getScale().setY(1.0f);
			markerName.getScale().setZ(1.0f);
			float R = 0.0f;
			float G = 0.0f;
			float B = 0.0f;
			float A = 1.0f;
			if (darkColors) {
				R = 1.0f;
				G = 1.0f;
				B = 1.0f;
				A = 0.8f;
			}
			markerName.getColor().setR(R);
            markerName.getColor().setG(G);
            markerName.getColor().setB(B);
            markerName.getColor().setA(A);
			markerName.setAction(visualization_msgs.Marker.ADD);                                
			markerName.setNs("robot_state");
			markerName.setType(visualization_msgs.Marker.TEXT_VIEW_FACING);
			//markerName.setId(te.getRobotID());
			markerName.setLifetime(new Duration(10.0));
			String markerText  = "R" + rr.getRobotID() + ": " + rr.getPathIndex();
			if (extraStatusInfo != null) for (String extra : extraStatusInfo) markerText += "\n" + extra;
			markerName.setText(markerText);
			geometry_msgs.Pose pose = node.getTopicMessageFactory().newFromType(geometry_msgs.Pose._TYPE);
			geometry_msgs.Point pos = node.getTopicMessageFactory().newFromType(geometry_msgs.Point._TYPE);
			pos.setX(x);
			pos.setY(y);
			pos.setZ(0);
			pose.setPosition(pos);
			markerName.setPose(pose);
			synchronized(robotStatusMarkers) {
				this.robotStatusMarkers.get(rr.getRobotID()).add(markerName);
			}

		}
	}
	
	public void displayBox(String markerLabel, Coordinate[] shape, int markerID, double x, double y, double durationInSeconds) {
		if (ready) {
			visualization_msgs.Marker marker = node.getTopicMessageFactory().newFromType(visualization_msgs.Marker._TYPE);
			marker.getHeader().setFrameId(mapFrameID);
			marker.getScale().setX(0.2f);
			marker.getColor().setR(0.0f);
			marker.getColor().setG(100.0f);
			marker.getColor().setB(0.0f);
			marker.getColor().setA(0.8f);
			marker.setAction(visualization_msgs.Marker.ADD);                                
			marker.setNs("box_marker");
			marker.setType(visualization_msgs.Marker.LINE_STRIP);
			marker.setId(markerID);
			marker.setLifetime(new Duration(durationInSeconds));

			ArrayList<geometry_msgs.Point> points = new ArrayList<geometry_msgs.Point>();
			for (Coordinate coord : shape) {
				geometry_msgs.Point point = node.getTopicMessageFactory().newFromType(geometry_msgs.Point._TYPE);
				point.setX(coord.x);
				point.setY(coord.y);
				point.setZ(0.0);
				points.add(point);
			}
			points.add(points.get(0));
			marker.setPoints(points);
			if (!this.boxMarkerPublishers.containsKey(markerLabel)) {
				Publisher<visualization_msgs.MarkerArray> markerArrayPublisher = node.newPublisher(markerLabel, visualization_msgs.MarkerArray._TYPE);
				this.boxMarkerPublishers.put(markerLabel, markerArrayPublisher);
				synchronized(boxMarkerMarkers) {
					this.boxMarkerMarkers.put(markerLabel, new ArrayList<visualization_msgs.Marker>());
				}
			}
			synchronized(boxMarkerMarkers) {
				this.boxMarkerMarkers.get(markerLabel).add(marker);
			}

			//////////////
			visualization_msgs.Marker markerName = node.getTopicMessageFactory().newFromType(visualization_msgs.Marker._TYPE);
			markerName.getHeader().setFrameId(mapFrameID);
			markerName.getScale().setX(1.0f);
			markerName.getScale().setY(1.0f);
			markerName.getScale().setZ(1.0f);
			markerName.getColor().setR(1.0f);
			markerName.getColor().setG(1.0f);
			markerName.getColor().setB(1.0f);
			markerName.getColor().setA(0.8f);
			markerName.setAction(visualization_msgs.Marker.ADD);                                
			markerName.setNs("label");
			markerName.setType(visualization_msgs.Marker.TEXT_VIEW_FACING);
			//markerName.setId(te.getRobotID());
			markerName.setLifetime(new Duration(durationInSeconds));
			markerName.setText(markerLabel);
			geometry_msgs.Pose pose = node.getTopicMessageFactory().newFromType(geometry_msgs.Pose._TYPE);
			geometry_msgs.Point pos = node.getTopicMessageFactory().newFromType(geometry_msgs.Point._TYPE);
			pos.setX(x);
			pos.setY(y);
			pos.setZ(0);
			pose.setPosition(pos);
			markerName.setPose(pose);
			synchronized(boxMarkerMarkers) {
				this.boxMarkerMarkers.get(markerLabel).add(markerName);
			}
		}
	}


	@Override
	public void displayDependency(RobotReport rrWaiting, RobotReport rrDriving, String dependencyDescriptor) {
		if(ready) {
			Pose from = rrWaiting.getPose();
			Pose to = rrDriving.getPose();
			visualization_msgs.Marker mArrow = node.getTopicMessageFactory().newFromType(visualization_msgs.Marker._TYPE);
			mArrow.setAction(visualization_msgs.Marker.ADD);
			mArrow.setNs(dependencyDescriptor);
			mArrow.setType(visualization_msgs.Marker.ARROW);
			ArrayList<geometry_msgs.Point> points = new ArrayList<geometry_msgs.Point>();		
			geometry_msgs.Point pointFrom = node.getTopicMessageFactory().newFromType(geometry_msgs.Point._TYPE);
			geometry_msgs.Point pointTo = node.getTopicMessageFactory().newFromType(geometry_msgs.Point._TYPE);
			pointFrom.setX(from.getX());
			pointFrom.setY(from.getY());
			pointFrom.setZ(0.0);
			points.add(pointFrom);
			pointTo.setX(to.getX());
			pointTo.setY(to.getY());
			pointTo.setZ(0.0);
			points.add(pointTo);
			mArrow.setPoints(points);
			mArrow.setId(dependencyDescriptor.hashCode());
			mArrow.getHeader().setFrameId(mapFrameID);
			mArrow.getScale().setX(0.4);
			mArrow.getScale().setY(1.0);
			mArrow.getScale().setZ(1.2);
			float R = 0.0f;
			float G = 0.0f;
			float B = 0.0f;
			float A = 0.3f;
			if (darkColors) {
				R = 15.0f;
				G = 100.0f;
				B = 200.0f;
				A = 0.2f;
			}
			mArrow.getColor().setR(R);
			mArrow.getColor().setG(G);
			mArrow.getColor().setB(B);
			mArrow.getColor().setA(A);
			mArrow.setLifetime(new Duration(1.0));
			if (!this.dependencyPublishers.containsKey(rrWaiting.getRobotID())) {
				Publisher<visualization_msgs.MarkerArray> markerArrayPublisher = node.newPublisher("robot"+rrWaiting.getRobotID()+"/deps", visualization_msgs.MarkerArray._TYPE);
				this.dependencyPublishers.put(rrWaiting.getRobotID(), markerArrayPublisher);
				synchronized(dependencyMarkers) {
					this.dependencyMarkers.put(rrWaiting.getRobotID(), new ArrayList<visualization_msgs.Marker>());
				}
			}
			synchronized(dependencyMarkers) {
				this.dependencyMarkers.get(rrWaiting.getRobotID()).add(mArrow);
			}
		}
	}

	@Override
	public void updateVisualization() {
		for (Entry<Integer, Publisher<MarkerArray>> entry : robotStatusPublishers.entrySet()) {
			synchronized(robotStatusMarkers) {
				if (!robotStatusMarkers.get(entry.getKey()).isEmpty()) { 
					visualization_msgs.MarkerArray ma = node.getTopicMessageFactory().newFromType(visualization_msgs.MarkerArray._TYPE);
					ArrayList<visualization_msgs.Marker> copy = new ArrayList<visualization_msgs.Marker>();
					for (visualization_msgs.Marker m : robotStatusMarkers.get(entry.getKey())) copy.add(m);
					if (envelopeMarkers != null) {
						synchronized(envelopeMarkers) {
							if (envelopeMarkers != null && envelopeMarkers.containsKey(entry.getKey())) copy.add(envelopeMarkers.get(entry.getKey()));
						}
					}
					ma.setMarkers(copy);
					entry.getValue().publish(ma);
					robotStatusMarkers.get(entry.getKey()).clear();
				}
			}
		}
		
		for (Entry<Integer, Publisher<MarkerArray>> entry : dependencyPublishers.entrySet()) {
			synchronized(dependencyMarkers) {
				if (!dependencyMarkers.get(entry.getKey()).isEmpty()) { 				
					visualization_msgs.MarkerArray ma = node.getTopicMessageFactory().newFromType(visualization_msgs.MarkerArray._TYPE);
					ArrayList<visualization_msgs.Marker> copy = new ArrayList<visualization_msgs.Marker>();
					for (visualization_msgs.Marker m : dependencyMarkers.get(entry.getKey())) copy.add(m);
					ma.setMarkers(copy);
					entry.getValue().publish(ma);
					dependencyMarkers.get(entry.getKey()).clear();
				}
			}
		}
		
		for (Entry<String, Publisher<MarkerArray>> entry : boxMarkerPublishers.entrySet()) {
			synchronized(boxMarkerMarkers) {
				if (!boxMarkerMarkers.get(entry.getKey()).isEmpty()) { 
					visualization_msgs.MarkerArray ma = node.getTopicMessageFactory().newFromType(visualization_msgs.MarkerArray._TYPE);
					ArrayList<visualization_msgs.Marker> copy = new ArrayList<visualization_msgs.Marker>();
					for (visualization_msgs.Marker m : boxMarkerMarkers.get(entry.getKey())) copy.add(m);
					ma.setMarkers(copy);
					entry.getValue().publish(ma);
					boxMarkerMarkers.get(entry.getKey()).clear();
				}
			}
		}

	}


	@Override
	public void onError(Node arg0, Throwable arg1) {
		// TODO Auto-generated method stub

	}


	@Override
	public void onShutdown(Node arg0) {
		// TODO Auto-generated method stub

	}


	@Override
	public void onShutdownComplete(Node arg0) {
		// TODO Auto-generated method stub

	}


	@Override
	public void onStart(ConnectedNode arg0) {
		this.node = arg0;		
		while (true) {
			try {
				node.getCurrentTime();
				break;
			}
			catch(NullPointerException e) { }
		}
		this.ready = true;
	}


	@Override
	public GraphName getDefaultNodeName() {
		return GraphName.of("coordinator_viz");
	}

	@Override
	public void addEnvelope(TrajectoryEnvelope te) {
		GeometricShapeDomain dom = (GeometricShapeDomain)te.getEnvelopeVariable().getDomain();
		Coordinate[] verts = dom.getGeometry().getCoordinates();

		visualization_msgs.Marker marker = node.getTopicMessageFactory().newFromType(visualization_msgs.Marker._TYPE);
		marker.getHeader().setFrameId(mapFrameID);
		marker.getScale().setX(0.1f);
		marker.getColor().setR(50f);
		marker.getColor().setG(50.0f);
		marker.getColor().setB(0.0f);
		marker.getColor().setA(0.8f);
		float R = 0.0f;
		float G = 0.0f;
		float B = 0.0f;
		float A = 0.7f;
		if (darkColors) {
			R = 50.0f;
			G = 50.0f;
			A = 0.8f;
		}
		marker.getColor().setR(R);
		marker.getColor().setG(G);
		marker.getColor().setB(B);
		marker.getColor().setA(A);
		marker.setAction(visualization_msgs.Marker.ADD);
		marker.setNs("current_envelope");
		marker.setType(visualization_msgs.Marker.LINE_STRIP);
		marker.setId(te.getRobotID());
		marker.setLifetime(new Duration(1.0));

		ArrayList<geometry_msgs.Point> points = new ArrayList<geometry_msgs.Point>();
		for (Coordinate coord : verts) {
			geometry_msgs.Point point = node.getTopicMessageFactory().newFromType(geometry_msgs.Point._TYPE);
			point.setX(coord.x);
			point.setY(coord.y);
			point.setZ(0.0);
			points.add(point);
		}
		points.add(points.get(0));
		marker.setPoints(points);

		if (this.envelopeMarkers == null) this.envelopeMarkers = new HashMap<Integer,visualization_msgs.Marker>();
		synchronized(envelopeMarkers) {
			this.envelopeMarkers.put(te.getRobotID(),marker);
		}

	}

	@Override
	public void removeEnvelope(TrajectoryEnvelope te) {
		if (this.envelopeMarkers == null) return;
		synchronized(envelopeMarkers) {
			this.envelopeMarkers.remove(te.getRobotID());
		}		
	}
	
	public static void main(String[] args) {
		System.out.println("test");
	}

	@Override
	public int periodicEnvelopeRefreshInMillis() {
		// TODO Auto-generated method stub
		return 0;
	}

}