content: package aima.gui.swing.applications.agent.map;
  
  import java.awt.Color;
  import java.util.ArrayList;
  import java.util.Hashtable;
  import java.util.List;
  
  import aima.core.agent.Action;
  import aima.core.agent.Agent;
  import aima.core.agent.Environment;
  import aima.core.agent.Percept;
  import aima.core.agent.impl.DynamicPercept;
  import aima.core.environment.map.Map;
  import aima.core.environment.map.MapEnvironment;
  import aima.core.environment.map.MoveToAction;
  import aima.core.util.math.geom.shapes.Point2D;
  import aima.gui.swing.framework.EmptyEnvironmentView;
  
  /**
   * General graphical environment view implementation for map agent applications.
   * This view requires the used environment to be of type {@link MapEnvironment}.
   * All agents are tracked but only the track of the first agent is shown.
   * 
   * @author Ruediger Lunde
   */
  public class MapAgentView extends EmptyEnvironmentView<DynamicPercept, MoveToAction> {
  
  	private static final long serialVersionUID = 1L;
  	/** Stores for each agent the locations, it has already visited. */
  	private final Hashtable<Agent, List<String>> agentTracks = new Hashtable<>();
  
  	protected MapEnvironment getMapEnv() {
  		return (MapEnvironment) env;
  	}
  
  	/** Returns a list of all already visited agent locations. */
  	public List<String> getTrack(Agent agent) {
  		return agentTracks.get(agent);
  	}
  
  	/** Clears the list of already visited locations. */
  	public void clearTracks() {
  		agentTracks.clear();
  	}
  
  	/**
  	 * Reacts on environment changes and updates the agent tracks.
  	 */
  	@Override
  	public void agentAdded(Agent<?, ?> agent, Environment<?, ?> source) {
  		updateTracks();
  		super.agentAdded(agent, source);
  	}
  
  	/**
  	 * Reacts on environment changes and updates the agent tracks. The command
  	 * is always send to the message logger as string.
  	 */
  	@Override
  	public void agentActed(Agent agent, DynamicPercept percept, MoveToAction command, Environment source) {
  		MapEnvironment mEnv = getMapEnv();
  		String msg = "";
  		if (mEnv.getAgents().size() > 1)
  			msg = "A" + mEnv.getAgents().indexOf(agent) + ": ";
  		notify(msg + command.toString());
  		updateTracks();
  		repaint();
  	}
  
  	/**
  	 * Clears the panel, displays the map, the current agent locations, and the
  	 * track of the first agent.
  	 */
  	@Override
  	public void paintComponent(java.awt.Graphics g) {
  		super.paintComponent(g);
  		if (env != null) {
  			Map map = getMapEnv().getMap();
  			if (!map.getLocations().isEmpty()) {
  				updateTracks(); // safety first!
  				java.awt.Graphics2D g2 = (java.awt.Graphics2D) g;
  				adjustTransformation();
  				paintMap(g2);
  				for (Agent a : env.getAgents())
  					paintTrack(g2, a);
  				for (String loc : map.getLocations())
  					paintLoc(g2, loc);
  			}
  		}
  	}
  
  	/** Updates all tracks with respect to the current agent locations. */
  	protected void updateTracks() {
  		MapEnvironment mEnv = getMapEnv();
  		if (mEnv != null)
  			for (Agent a : mEnv.getAgents()) {
  				List<String> aTrack = getTrack(a);
  				String aLoc = mEnv.getAgentLocation(a);
  				if (aTrack == null) {
  					aTrack = new ArrayList<String>();
  					agentTracks.put(a, aTrack);
  				}
  				if (aTrack.isEmpty() || !aTrack.get(aTrack.size() - 1).equals(aLoc))
  					aTrack.add(aLoc);
  			}
  	}
  
  	/** Returns the locations of all agents. */
  	protected List<String> getAgentLocs() {
  		List<String> result = new ArrayList<String>();
  		MapEnvironment mEnv = getMapEnv();
  		for (Agent a : mEnv.getAgents())
  			result.add(mEnv.getAgentLocation(a));
  		return result;
  	}
  
  	/**
  	 * Adjusts offsets and scale so that the whole map fits on the view without
  	 * scrolling.
  	 */
  	private void adjustTransformation() {
  		Map map = getMapEnv().getMap();
  		List<String> locs = map.getLocations();
  		// adjust coordinates relative to the left upper corner of the graph
  		// area
  		double minX = Double.POSITIVE_INFINITY;
  		double minY = Double.POSITIVE_INFINITY;
  		double maxX = Double.NEGATIVE_INFINITY;
  		double maxY = Double.NEGATIVE_INFINITY;
  		for (String loc : locs) {
  			Point2D xy = map.getPosition(loc);
  			if (xy.getX() < minX)
  				minX = xy.getX();
  			if (xy.getY() < minY)
  				minY = xy.getY();
  			if (xy.getX() > maxX)
  				maxX = xy.getX();
  			if (xy.getY() > maxY)
  				maxY = xy.getY();
  		}
  		this.setBorder(20, 20, 20, 100);
  		adjustTransformation(minX, minY, maxX, maxY);
  	}
  
  	/**
  	 * Represents roads by lines and locations by name-labeled points.
  	 */
  	protected void paintMap(java.awt.Graphics2D g2) {
  		Map envMap = getMapEnv().getMap();
  		for (String l1 : envMap.getLocations()) {
  			Point2D pt1 = envMap.getPosition(l1);
  			List<String> linkedLocs = envMap.getPossibleNextLocations(l1);
  			for (String l2 : linkedLocs) {
  				Point2D pt2 = envMap.getPosition(l2);
  				g2.setColor(Color.lightGray);
  				g2.drawLine(x(pt1), y(pt1), x(pt2), y(pt2));
  			}
  		}
  	}
  
  	/** The track of the agent is visualized with red lines. */
  	private void paintTrack(java.awt.Graphics2D g2, Agent a) {
  		Map map = getMapEnv().getMap();
  		Point2D lastPt = null;
  		g2.setColor(Color.red);
  		for (String loc : getTrack(a)) {
  			Point2D pt = map.getPosition(loc);
  			if (pt != null && lastPt != null) {
  				g2.drawLine(x(pt), y(pt), x(lastPt), y(lastPt));
  			}
  			lastPt = pt;
  		}
  	}
  
  	protected void paintLoc(java.awt.Graphics2D g2, String loc) {
  		Map map = getMapEnv().getMap();
  		Point2D pt = map.getPosition(loc);
  		if (pt != null) {
  			int x = x(pt);
  			int y = y(pt);
  			String info = "";
  			List<String> track = new ArrayList<String>();
  			if (!env.getAgents().isEmpty())
  				// show details only for track of first agent...
  				track = getTrack(env.getAgents().get(0));
  			ArrayList<Integer> list = new ArrayList<Integer>();
  			for (int i = 0; i < track.size(); i++)
  				if (track.get(i).equals(loc))
  					list.add(i + 1);
  			if (!list.isEmpty())
  				info = list.toString();
  			if (getAgentLocs().contains(loc)) {
  				g2.setColor(Color.red);
  				g2.fillOval(x - 4, y - 4, 8, 8);
  			}
  			if (track.contains(loc))
  				g2.setColor(Color.black);
  			else
  				g2.setColor(Color.gray);
  			g2.drawString(loc + info, x, y);
  		}
  	}
  }