/********************************************/
/* Dijkstra.java                            */
/* Copyright (C) 1997, 1998, 2000  K. Ikeda */
/********************************************/

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.URL;

class Node {
	int	x;
	int	y;
	int	delta_plus;	/* edge starts from this node */
	int	delta_minus;	/* edge terminates at this node */
	int	dist;		/* distance from the start node */
	int	prev;		/* previous node of the shortest path */
	int	succ,pred;	/* node in Sbar with finite dist. */
	int	w;
	int	h;
	int	pw;
	int	dx;
	int	dy;
	String	name;
}

class Edge {
	int	rndd_plus;	/* initial vertex of this edge */
	int	rndd_minus;	/* terminal vertex of this edge */
	int	delta_plus;	/* edge starts from rndd_plus */
	int	delta_minus;	/* edge terminates at rndd_minus */
	int	len;		/* length */
	String	name;
}

public class Dijkstra extends Applet implements MouseListener {
	int	n,m;
	int	u,snode;	/* start node */
	int	pre_s_first, pre_s_last;
	boolean	isdigraph;
	int	iteration, step;
	Node	v[] = new Node[100];
	Edge	e[] = new Edge[200];

	int findNode(String name) {
		for (int i=0; i<n; i++)
			if (v[i].name.equals(name))
				return i;
		return -1;
	}

	void input_graph(InputStream is) throws IOException {
		int x,y,l;
		String	s;

		Reader r = new BufferedReader(new InputStreamReader(is));
		StreamTokenizer st = new StreamTokenizer(r);
		st.commentChar('#');
		st.nextToken();	n = (int)st.nval;
		st.nextToken();	m = (int)st.nval;
		st.nextToken();	s = st.sval;
		isdigraph = "digraph".equals(s);
		for (int i = 0; i<n; i++) {
			Node node = new Node();
			st.nextToken();	node.name = st.sval;
			st.nextToken();	node.x = (int)st.nval;
			st.nextToken();	node.y = (int)st.nval;
			v[i] = node;
		}
		for (int i = 0; i<m; i++) {
			Edge edge = new Edge();
			st.nextToken();	edge.name = st.sval;
			switch (st.nextToken()) {
			case StreamTokenizer.TT_NUMBER:
				edge.rndd_plus = (int)st.nval;
				break;
			case StreamTokenizer.TT_WORD:
				edge.rndd_plus = findNode(st.sval);
				break;
			default:
				break;
			}
			switch (st.nextToken()) {
			case StreamTokenizer.TT_NUMBER:
				edge.rndd_minus = (int)st.nval;
				break;
			case StreamTokenizer.TT_WORD:
				edge.rndd_minus = findNode(st.sval);
				break;
			default:
				break;
			}
			st.nextToken();	edge.len = (int)st.nval;
			e[i] = edge;
		}
		for (int i=0; i<n; i++) {
			v[i].succ = v[i].pred = -2;
			v[i].prev = v[i].dist = -1;
			v[i].pw = 0;
		}
		iteration = step = 0;
	}

	void rdb() {
		int	i,k;

		for (i=0; i<n; i++)
			v[i].delta_plus = v[i].delta_minus = -1;
		for (i=0; i<m; i++)
			e[i].delta_plus = e[i].delta_minus = -1;
		for (i=0; i<m; i++) {
			k = e[i].rndd_plus;
			if (v[k].delta_plus == -1)
				v[k].delta_plus = i;
			else {
				k = v[k].delta_plus;
				while(e[k].delta_plus >= 0)
					k = e[k].delta_plus;
				e[k].delta_plus = i;
			}
			k = e[i].rndd_minus;
			if (v[k].delta_minus == -1)
				v[k].delta_minus = i;
			else {
				k = v[k].delta_minus;
				while(e[k].delta_minus >= 0)
					k = e[k].delta_minus;
				e[k].delta_minus = i;
			}
		}
	}

	void append_pre_s(int i) {
		v[i].succ = v[i].pred = -1;
		if (pre_s_first<0)
			pre_s_first = i;
		else
			v[pre_s_last].succ = i;
		v[i].pred = pre_s_last;
		pre_s_last = i;
	}

	void remove_pre_s(int i) {
		int	succ = v[i].succ;
		int	pred = v[i].pred;

		if (succ>=0)
			v[succ].pred = pred;
		else
			pre_s_last = pred;
		if (pred>=0)
			v[pred].succ = succ;
		else
			pre_s_first = succ;
	}

	void step1() {		/* initialize */
		u = snode;
		for (int i=0; i<n; i++) {
			v[i].succ = v[i].pred = -2;
			v[i].prev = v[i].dist = -1;
		}
		v[u].succ = -3;
		v[u].dist = 0;
		pre_s_first = pre_s_last = -1;
	}

	void step2() {		/* replace labels */
		int i,j;

		j = v[u].delta_plus;
		while (j>=0) {
			i = e[j].rndd_minus;
			if ((v[i].succ>=-2)&&((v[i].dist<0)||
				(v[i].dist>v[u].dist+e[j].len))) {
				if (v[i].dist<0)
					append_pre_s(i);
				v[i].dist = v[u].dist + e[j].len;
				v[i].prev = u;	  /* label */
			}
			j = e[j].delta_plus;
		}
		if (!isdigraph) {
		j = v[u].delta_minus;
		while (j>=0) {
			i = e[j].rndd_plus;
			if ((v[i].succ>=-2)&&((v[i].dist<0)||
				(v[i].dist>v[u].dist+e[j].len))) {
				if (v[i].dist<0)
					append_pre_s(i);
				v[i].dist = v[u].dist + e[j].len;
				v[i].prev = u;	  /* label */
			}
			j = e[j].delta_minus;
		}
		}
		v[u].succ = -4;
	}

	void step3() {		/* find the shortest node in Sbar */
		int i,rho;

		rho = -1;
		for (i = pre_s_first; i>=0; i = v[i].succ) {
			if ((rho < 0)||(rho>v[i].dist)) {
				rho = v[i].dist;
				u = i;
			}
		}
		remove_pre_s(u);
		v[u].succ = -3;
	}

	void step4() {
		v[u].succ = -4;
	}

	double weight(Node n, double x, double y) {
		double	w,z,xx,yy;

		w = 0;
		for (int j = n.delta_plus; j>=0; j=e[j].delta_plus) {
			xx = (double)(v[e[j].rndd_minus].x - n.x);
			yy = (double)(v[e[j].rndd_minus].y - n.y);
			z = (x*xx+y*yy)/Math.sqrt((x*x+y*y)*(xx*xx+yy*yy))+1.0;
			w += z*z*z*z;
		}
		for (int j = n.delta_minus; j>=0; j=e[j].delta_minus) {
			xx = (double)(v[e[j].rndd_plus].x - n.x);
			yy = (double)(v[e[j].rndd_plus].y - n.y);
			z = (x*xx+y*yy)/Math.sqrt((x*x+y*y)*(xx*xx+yy*yy))+1.0;
			w += z*z*z*z;
		}
		return w;
	}

	void init_sub() {
		int x[] = {1, 0, -1, 1, 0, -1};
		int y[] = {1, 1, 1, -1, -1, -1};
		int 	i,j,k;
		double	w,z;

		for (i=0; i<n; i++) {
			k=0;
			w=weight(v[i],(double)x[0],(double)y[0]);
			for (j=1; j<6; j++) {
				z=weight(v[i],(double)x[j],(double)y[j]);
				if (z<w) {
					w = z;
					k = j;
				}
			}
			v[i].dx = x[k];
			v[i].dy = y[k];
		}
	}

	public void init() {
		String mdname = getParameter("inputfile");
		try {
			InputStream is;

			is = new URL(getDocumentBase(),mdname).openStream();
			input_graph(is);
			try {
				if (is != null)
					is.close();
				} catch(Exception e) {
			}
		} catch (FileNotFoundException e) {
			System.err.println("File not found.");
		} catch (IOException e) {
			System.err.println("Cannot access file.");
		}

		String s = getParameter("start");
		if (s != null)
			snode = Integer.parseInt(s);
		else
			snode = 0;

		setBackground(Color.white);
		rdb();
		init_sub();
		addMouseListener(this);
	}

	public void paintNode(Graphics g, Node n, FontMetrics fm) {
		String s;
		int x = n.x;
		int y = n.y;
		int w = fm.stringWidth(n.name) + 10;
		int h = fm.getHeight() + 4;
		n.w = w;
		n.h = h;

		if (n.succ<-2)
			g.setColor(Color.blue);
		else if (n.succ==-2)
			g.setColor(Color.gray);
		else
			g.setColor(Color.red);

		g.drawRect(x-w/2,y-h/2,w,h);

		if (n.succ==-4)
			g.setColor(Color.cyan);
		else if (n.succ==-3)
			g.setColor(Color.pink);
		else if (n.succ>-2)
			g.setColor(Color.yellow);
		else
			g.setColor(getBackground());

		g.fillRect(x-w/2+1,y-h/2+1,w-1,h-1);

		g.setColor(Color.black);
		g.drawString(n.name,x-(w-10)/2,(y-(h-4)/2)+fm.getAscent());


		if (n.dist<0)
			s = "";
		else
			s = ""+n.dist;
		w = fm.stringWidth(s) + 10;
		x += (h+1)*n.dx; y += (h+1)*n.dy;
		g.setColor(getBackground());
		g.fillRect(x-n.pw/2,y-h/2,n.pw,h);
		n.pw = w;
		if (n.succ<-2)
			g.setColor(Color.blue);
		else
			g.setColor(Color.red);
		g.drawString(s,x-(w-10)/2,y-(h-4)/2+fm.getAscent());
	}

	int [] xy(int a, int b, int w, int h) {
		int	x[] = new int[2];

		if (Math.abs(w*b)>=Math.abs(h*a)) {
			x[0] = ((b>=0)?1:-1)*a*h/b/2;
			x[1] = ((b>=0)?1:-1)*h/2;
		} else {
			x[0] = ((a>=0)?1:-1)*w/2;
			x[1] = ((a>=0)?1:-1)*b*w/a/2;
		}
		return x;
	}

	void drawArrow(Graphics g,int x1,int y1,int x2,int y2) {
		int	a = x1-x2;
		int	b = y1-y2;

		if (isdigraph) {
		double	aa = Math.sqrt(a*a+b*b)/16.0;
		double	bb = b/aa;
			aa = a/aa;
		g.drawLine(x2,y2,x2+(int)((aa*12+bb*5)/13),y2+(int)((-aa*5+bb*12)/13));
		g.drawLine(x2,y2,x2+(int)((aa*12-bb*5)/13),y2+(int)((aa*5+bb*12)/13));
		}
		g.drawLine(x1,y1,x2,y2);
	}

	public void paintEdge(Graphics g, Edge e, FontMetrics fm) {
		Node v1 = v[e.rndd_plus];
		Node v2 = v[e.rndd_minus];

		int a = v1.x-v2.x;
		int b = v1.y-v2.y;

		int x1[] = xy(-a,-b,v1.w,v1.h);
		int x2[] = xy(a,b,v2.w,v2.h);

		if (v2.prev == e.rndd_plus) {
			if ((v1.succ<-2)&&(v2.succ>=-2))
				g.setColor(Color.red);
			else
				g.setColor(Color.blue);
		} else {
			g.setColor(Color.lightGray);
		}
		if ((!isdigraph)&&(v1.prev == e.rndd_minus)) {
			if ((v2.succ<-2)&&(v1.succ>=-2))
				g.setColor(Color.red);
			else
				g.setColor(Color.blue);
		}
		drawArrow(g,v1.x+x1[0],v1.y+x1[1],v2.x+x2[0],v2.y+x2[1]);

		int w = fm.stringWidth("" + e.len);
		int h = fm.getHeight();

		g.setColor(getBackground());
		g.fillRect((v1.x+v2.x-w)/2,(v1.y+v2.y-h)/2,w,h);

		if ((v2.prev == e.rndd_plus)||
		    ((!isdigraph)&&(v1.prev == e.rndd_minus)))
			g.setColor(Color.black);
		else
			g.setColor(Color.lightGray);
		g.drawString("" + e.len,(v1.x+v2.x-w)/2,(v1.y+v2.y-h)/2+fm.getAscent());
	}

	public void paint(Graphics g) {
		FontMetrics fm = g.getFontMetrics();
		for (int i=0; i<n; i++)
			paintNode(g,v[i],fm);
		for (int i=0; i<m; i++)
			paintEdge(g,e[i],fm);
	}

	public void update(Graphics g) {
		paint(g);
	}

	public void mousePressed(MouseEvent ev) {
		if (iteration==0) {
			step1();
			iteration++;
			step = 2;
		} else if (iteration>=n) {
			step4();
			iteration = 0;
		} else {
			if (step == 2) {
				step2();
				step = 3;
			} else {
				step3();
				iteration++;
				step = 2;
			}
		}
		repaint();
	}
	public void mouseClicked(MouseEvent event) {}
	public void mouseReleased(MouseEvent event) {}
	public void mouseEntered(MouseEvent event) {}
	public void mouseExited(MouseEvent event) {}
}


