import java.applet.Applet;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/*
 * Created on Sep 11, 2004
 */
/**
 * a reimplementation of Rafi Nachmias'
 * <a href="http://clp.berkeley.edu/CLP/pages/e_polyline.htm">Polyline</a>
 * 
 * @author Mitch Skinner <polyline@arctur.us>
 */
public class PolyLine extends Applet {
	protected static final int BEGINNER_MODE = 0;
	protected static final int FULL_MODE = 1;
	
	PolyLineControls controls;
	PolyLineCanvas canvas;
	
	public void init() {
		setLayout(new BorderLayout());
		canvas = new PolyLineCanvas();
		add("Center", canvas);
		int mode;
		if ("beginner".equalsIgnoreCase(getParameter("MODE"))) {
			mode = BEGINNER_MODE;
		} else {
			mode = FULL_MODE;
		}
		
		add("South", controls = new PolyLineControls(canvas, mode));
	}
	
	public void destroy() {
		remove(controls);
		remove(canvas);
	}
	
	public void start() {
		controls.setEnabled(true);
	}
	
	public void stop() {
		controls.setEnabled(false);
	}
	
	public void processEvent(AWTEvent e) {
		if (e.getID() == Event.WINDOW_DESTROY) {
			System.exit(0);
		}
	}
	
	public static void main(String args[]) {
		Frame f = new Frame("PolyLine");
		PolyLine pl = new PolyLine();
		
		pl.init();
		pl.start();
		
		f.add("Center", pl);
		f.setSize(300, 300);
		f.show();
	}
	
	public String getAppletInfo() {
        return "An interactive demonstration of\nthe need to control variables.\n(A reimplementation of Rafi Nachmias' PolyLine)";
	}
}

class PolyLineCanvas extends Canvas {
	private static final int xOffset = 0;
	private static final int yOffset = 20;
	
	private static final Color[] colors = new Color[] {
			Color.black, Color.blue, Color.red, Color.green, Color.orange,
			Color.cyan, Color.darkGray, Color.magenta, Color.pink,
			Color.yellow
	};
	
	int firstLineLength;
	Color lineColor;
	int numLines = 0;
	int lengthDelta;
	BasicStroke stroke;
	double angle;
	
	int[] xPoints;
	int[] yPoints;
	
	boolean drawn = true;
	
	public void paint(Graphics g) {
		double direction = Math.PI;
		int lineLength = firstLineLength;
		
		if (0 == numLines) return;
		
		//pause for this number of milliseconds
		//between successive draws.
		int pause = 1000 / numLines;
		
		Rectangle bounds = this.getBounds();

		Graphics2D g2;
		g2 = (Graphics2D) g;
		g2.setColor(lineColor);
		g2.setStroke(stroke);
		//too slow, given how I'm doing segment-by-segment drawing
		//g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		
		xPoints[0] = ((int) bounds.getCenterX()) + xOffset;
		yPoints[0] = ((int) bounds.getCenterY()) + yOffset;
		for(int i = 1; i <= numLines; i++) {
			xPoints[i] = xPoints[i - 1] + (int) (lineLength * Math.cos(direction));
			yPoints[i] = yPoints[i - 1] + (int) (lineLength * Math.sin(direction));
			lineLength += lengthDelta;
			if (lineLength < 0) {
				numLines = i;
				break;
			}
			direction += angle;
		}
		
		if ((! drawn) && (numLines < 100)) {
			try {
				for(int i = 1; i <= numLines; i++) {
					g2.drawPolyline(xPoints, yPoints, i + 1);
					Thread.sleep(pause);
				}
			}catch (InterruptedException e) {
				System.out.println("Interruped");
			}
		} else {
			g2.drawPolyline(xPoints, yPoints, numLines + 1);
		} 
		
		drawn = true;
	}
	
	public void redraw(int firstLineLength,
			int colorNum,
			int numLines,
			int lengthDelta,
			int lineThickness,
			int angleDeg) {
		this.xPoints = new int[numLines + 1];
		this.yPoints = new int[numLines + 1];
		
		this.firstLineLength = firstLineLength;
		this.lineColor = colors[(colorNum - 1) % colors.length];
		this.numLines = numLines;
		this.lengthDelta = lengthDelta;
		this.stroke = new BasicStroke((float)lineThickness, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
		this.angle = ((180.0 - angleDeg) / 180.0) * Math.PI;
		
		drawn = false;
		repaint();
	}
}

class PolyLineControls extends Panel
implements ActionListener {
	PolyLineCanvas canvas;
	
	//these are min, max, default
	private static final int[][] limits = new int[][] {
			{1, 300, 200},   //first line length
			{1, 10, 1},      //color
			{1, 1000, 10},   //number of lines
			{-100, 100, 0},  //length delta
			{1, 20, 3},      //line thickness
			{-180, 180, 50}  //angle
	};
	
	private TextField[] fields = new TextField[limits.length];

	protected void placeComponent(Component comp,
            GridBagLayout gridbag,
            GridBagConstraints c) {
		gridbag.setConstraints(comp, c);
		add( comp);
	}

	public PolyLineControls(PolyLineCanvas canvas, int mode) {
		Button b = null;
		
		GridBagLayout gridbag = new GridBagLayout();
		GridBagConstraints c = new GridBagConstraints();
		
		setLayout(gridbag);
		
		this.canvas = canvas;
		for(int i = 0; i < limits.length; i++) {
			fields[i] = new TextField(String.valueOf(limits[i][2]), 4);
			fields[i].addKeyListener(new KeyListener() {
				public void keyTyped(KeyEvent e) {}
				public void keyReleased(KeyEvent e) {}
				public void keyPressed(KeyEvent e) {
					if (KeyEvent.VK_ENTER == e.getKeyCode())
						draw();
				}
			});
		}
		
		int[] varsToUse;
		
		if (PolyLine.BEGINNER_MODE == mode)
			varsToUse = new int[] {2, 5};
		else
			varsToUse = new int[] {0, 1, 2, 3, 4, 5};
		
		for(int i = 0; i < varsToUse.length; i++) {
			c.gridx = i;
			c.gridy = 0;
			Label lbl = new Label("Variable " + String.valueOf(i + 1));
			placeComponent(lbl, gridbag, c);
			c.gridy = 1;
			fields[varsToUse[i]].setText("");
			placeComponent(fields[varsToUse[i]], gridbag, c);
		}
		
		b = new Button("Draw");
		b.addActionListener(this);
		c.gridheight = 2;
		c.gridx = fields.length;
		c.gridy = 0;
		placeComponent(b, gridbag, c);
	}
	
	private void draw() {
		int[] vals = new int[fields.length];
		for(int i = 0; i < fields.length; i++) {
			try {
				vals[i] = Integer.parseInt(fields[i].getText().trim());
			} catch (NumberFormatException e) {
				vals[i] = limits[i][2];
				fields[i].setText(String.valueOf(vals[i]));
			}
			if (vals[i] < limits[i][0]) {
				vals[i] = limits[i][0];
				fields[i].setText(String.valueOf(vals[i]));
			} else if (vals[i] > limits[i][1]) {
				vals[i] = limits[i][1];
				fields[i].setText(String.valueOf(vals[i]));
			}
		}
		canvas.redraw(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
	}
	
	public void actionPerformed(ActionEvent ev) {
		draw();
	}
}
