/* PolygonApplet.java -- Polygon applet using the com.lrdev.turtle package.
**
** Copyright (C) 1997-2003 Eric Laroche.  All rights reserved.
**
** @author Eric Laroche <laroche@lrdev.com>
** @version 1.3 @(#)$Id: PolygonApplet.java,v 1.3 2003/02/16 12:41:22 laroche Exp $
**
** This program is free software;
** you can redistribute it and/or modify it.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**
*/

// note: no wildcard imports

// Use the AWT based graphics Turtle package.
import com.lrdev.turtle.PersistentTurtle;
import com.lrdev.turtle.Turtle;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Rectangle;

/** PolygonApplet: Polygon applet using the com.lrdev.turtle package.
*
* @author Eric Laroche <laroche@lrdev.com>
* @version 1.3 @(#)$Id: PolygonApplet.java,v 1.3 2003/02/16 12:41:22 laroche Exp $
*/
public class PolygonApplet
	extends Applet
{
	/** Version.
	*/
	private final static String m_version =
		"Version: 1.3 @(#)$Id: PolygonApplet.java,v 1.3 2003/02/16 12:41:22 laroche Exp $";

	/** Author.
	*/
	private final static String m_author =
		"Author: Eric Laroche <laroche@lrdev.com>";

	/** Applet usage.
	*/
	private final static String[][] m_usage = {
		{"order", "int", "initial order"},
		{"suborder", "int", "initial suborder"},
		{"center", "boolean", "center and scale the drawing"},
	};

	/** Polygon order.
	* [Default was smallest possible (3).]
	* 7.1 is cool enough.
	*/
	private int m_order = 7;

	/** Polygon suborder.
	* [Default was 0.]
	*/
	private int m_suborder = 1;

	/** Center/scale mode.
	*/
	private boolean m_center = true;

	/** Called by the browser.
	* Returns info on this applet.
	*
	* Overwrites Applet.getAppletInfo.
	*/
	public String getAppletInfo()
	{
		return
			m_version + "\n" +
			m_author + "\n\n" +
			"interactive usage:\n" + interactiveUsage();
	}

	/** Called by the browser.
	* Returns info on the parameters of this applet.
	*
	* Overwrites Applet.getParameterInfo.
	*/
	public String[][] getParameterInfo()
	{
		return m_usage;
	}

	/** Constructor.
	*/
	public PolygonApplet()
	{
		// NOTE: must postpone getParameter() calls to init()
	}

	/** Called after the constructor and before start().
	* Sets the background color and reads in the applet parameters.
	*
	* Overwrites Applet.init.
	*/
	public void init()
	{
		String parameter;

		parameter = getParameter("order");
		if (parameter != null) {
			m_order = Integer.valueOf(parameter).intValue();
		}

		parameter = getParameter("suborder");
		if (parameter != null) {
			m_suborder = Integer.valueOf(parameter).intValue();
		}

		parameter = getParameter("center");
		if (parameter != null) {
			m_center = Boolean.valueOf(parameter).booleanValue();
		}

		if (m_order < 3) {
			throw new IllegalArgumentException("order must be 3 or greater");
		}

		int m = maximalSuborder(m_order);
		if (m_suborder >= m) {
			throw new IllegalArgumentException(
				"suborder for order " + m_order + " must be smaller than " + m);
		}

		// white background, black foreground
		setBackground(Color.white);
		setForeground(Color.black);
	}

	/** Called by the browser.
	*
	* Overwrites Applet.start.
	*/
	public void start()
	{
	}

	/* Called by the browser.
	*
	* Overwrites Applet.stop.
	*/
	public void stop()
	{
	}

	/** Called by the browser.
	*
	* Overwrites Applet.destroy.
	*/
	public void destroy()
	{
	}

	/** Paint the component.
	*
	* Overwrites Component.paint.
	*/
	public void paint(Graphics graphics)
	{
		// get drawing box
		Rectangle b = bounds();

		// 2 pixel border
		int border = 2;

		b.width -= 2 * border;
		b.height -= 2 * border;
		b.x += border;
		b.y += border;

		// check if the box is large enough
		if (b.width < 0 || b.height < 0) {
			// we're too small
			return;
		}

		paintPolygon(graphics, b);
   	}

	/** Paint polygon.
	*/
	protected void paintPolygon(
		Graphics graphics,
		Rectangle bounds)
	{
		if (bounds.width <= 0 || bounds.height <= 0) {
			return;
		}

		// 20 is reasonable, though irrelevent while scaling
		double length = 20.0;

		Turtle turtle = null;

		if (m_center) {
			// use the persistent Turtle to have the center() functionality
			turtle = new PersistentTurtle();
			((PersistentTurtle)turtle).center();
		} else {
			turtle = new Turtle(graphics, bounds);
		}

		// do the polygon
		doPolygon(turtle, length);

		if (turtle instanceof PersistentTurtle) {
			// paint it
			((PersistentTurtle)turtle).paint(graphics, bounds);
		}
   	}

	/** Do (paint) the polygon.
	*/
	protected void doPolygon(Turtle turtle, double length)
	{
		double phi = (Math.PI * 2) / m_order;

		for (int i = 0; i < m_order; i++) {
			if (m_suborder == 0) {
				turtle.forward(length).left(phi);
			} else {
				turtle.forward(length).right(phi * m_suborder);
				turtle.forward(length).left(phi * (m_suborder + 1));
			}
		}
   	}

	/** Calculate the maximal suborder from a given order.
	*/
	protected static int maximalSuborder(int order)
	{
		return (order - 1) / 2;
	}

	/** Handle keyboard input.
	*
	* Overwrites Component.keyDown.
	*/
	public boolean keyDown(Event event, int key)
	{
		boolean handled = doCommand(key);

		if (handled) {
			// update GUI
			// This is safe as long as called from the
			// user interface handling/painting thread,
			// which is the case here.
			repaint();
		}

		return handled;
	}

	/** Command interpreter.
	*
	* @return command handled
	*/
	protected boolean doCommand(int command)
	{
		switch (command) {

		case 'o' : // increase order
			m_order++;
			break;

		case 'O' : // decrease order
			m_order--;
			break;

		case 's' : // increase suborder
			m_suborder++;
			break;

		case 'S' : // decrease suborder
			m_suborder--;
			break;

		case 'r' : // reset
			// let the checks below adjust this
			m_order = 0;
			m_suborder = 0;
			break;

		case 'R' : // reset suborder only
			m_suborder = 0;
			break;

		case 'n' : // next: increase suborder if possible, else increase order and reset suborder
		case 'P' :
			m_suborder++;
			if (m_suborder >= maximalSuborder(m_order)) {
				m_order++;
				m_suborder = 0;
			}
			break;

		case 'N' : // previous: decrease suborder if possible, else decrease order and set suborder to max
		case 'p' :
			m_suborder--;
			if (m_suborder < 0) {
				m_order--;
				m_suborder = maximalSuborder(m_order) - 1;
			}
			break;

		case 'd' : // double order
		case 'H' :
			m_order *= 2;
			break;

		case 'D' : // half order
		case 'h' :
			m_order /= 2;
			break;

		default :
			// event not handled
			return false;
		}

		while (m_order < 3) {
			m_order++;
		}

		while (m_suborder >= maximalSuborder(m_order)) {
			m_suborder--;
		}

		while (m_suborder < 0) {
			m_suborder++;
		}

		// event handled
		return true;
	}

	/** Return interactive usage.
	*/
	protected static String interactiveUsage()
	{
		return
			"o: increase order, O: decrease" + "\n" +
			"s: increase suborder, S: decrease" + "\n" +
			"r: reset, R: reset suborder only" + "\n" +
			"n: next, p: previous" + "\n" +
			"d: double order, h: half" + "\n";
	}
}

