/* rsriddle.java
**
** Author: Eric Laroche
**
** 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.
**
*/

import java.applet.Applet;
import java.awt.*;
import java.io.PrintStream;

/** rsriddle.
**
** @author Eric Laroche
** @version @(#)$Id: rsriddle.java,v 1.2 1999/02/28 18:21:03 laroche Exp $
*/
public class rsriddle
	extends Applet
{
	/** version. */
	private final static String version =
		"Version: @(#)$Id: rsriddle.java,v 1.2 1999/02/28 18:21:03 laroche Exp $";

	/** applet usage. */
	private final static String[][] usage = {
	};

	/** disclaimer. */
	private final static String disclaimer =
		"This program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.";

	/** author. */
	private final static String author =
		"Author: Eric Laroche";

	/** ratio. */
	private int dratio = 1, qratio = 2;

	/** grid dimensions. */
	private Dimension dgrid = null;

	/** entry point for standalone application.
	*/
	public static void main(String[] args)
	{
		// usage
		System.err.println("Sorry, you must run this as an applet.");
		System.err.println("");

		// version info, disclaimer, etc.
		printInfo(System.err);
	}

	/** print version info, disclaimer, etc.
	*/
	protected static void printInfo(PrintStream out)
	{
		out.println(version);
		out.println(author);
		out.println("");
		out.println(disclaimer);
	}

	/** called by the browser.
	** returns info on this applet.
	*/
	public String getAppletInfo()
	{
		return version + "\n" + author;
	}

	/** called by the browser.
	** returns info on the parameters of this applet.
	*/
	public String[][] getParameterInfo()
	{
		return usage;
	}

	/** called after the constructor and before start().
	** sets the background color and reads in the applet parameters.
	*/
	public void init()
	{
		// white background, black foreground
		setBackground(Color.white);
		setForeground(Color.black);
	}

	/** called by the browser.
	*/
	public void start()
	{
	}

	/** called by the browser.
	*/
	public void stop()
	{
	}

	/** paint the component.
	*/
	public void paint(Graphics g)
	{
		// get the drawing area
		// split it horizontally into two
		// cut some some border

		Rectangle upper = bounds();
		upper.height /= 2;

		Rectangle lower = bounds();
		lower.y = lower.height / 2;

		try
		{
			addBorder(upper);
			addBorder(lower);
		}
		catch (IllegalArgumentException ex)
		{
			// area too small; don't paint anything
			return;
		}

		paintVariantA(g,
			new Dimension(upper.width - upper.x, upper.height - upper.y),
			new Point(upper.x, upper.y));
		paintVariantB(g,
			new Dimension(lower.width - lower.x, lower.height - lower.y),
			new Point(lower.x, lower.y));
	}

	/** add some border.
	*/
	protected void addBorder(Rectangle r)
		throws IllegalArgumentException
	{
		int border = 5;

		r.x += border, r.y += border;
		r.width -= border, r.height -= border;

		if (r.x > r.width || r.y > r.height)
				throw new IllegalArgumentException();
	}

	/** paint the first variant.
	*/
	private void paintVariantA(Graphics g, Dimension d, Point o)
	{
		// parameters
		int a1 = getA1(d), a2 = getA2(d), b1 = getB1(d);

		// polygons
		Polygon p;

		p = new Polygon();
		p.addPoint(0*a1+0*a2, 0*b1);
		p.addPoint(3*a1+2*a2, 0*b1);
		p.addPoint(3*a1+2*a2, 3*b1);
		fillAndDrawPolygon(g, d, o, p, Color.red.darker());

		p = new Polygon();
		p.addPoint(3*a1+2*a2, 0*b1);
		p.addPoint(5*a1+3*a2, 0*b1);
		p.addPoint(5*a1+3*a2, 2*b1);
		p.addPoint(4*a1+2*a2, 2*b1);
		p.addPoint(4*a1+2*a2, 1*b1);
		p.addPoint(3*a1+2*a2, 1*b1);
		fillAndDrawPolygon(g, d, o, p, Color.green.darker());

		p = new Polygon();
		p.addPoint(3*a1+2*a2, 1*b1);
		p.addPoint(4*a1+2*a2, 1*b1);
		p.addPoint(4*a1+2*a2, 2*b1);
		p.addPoint(5*a1+3*a2, 2*b1);
		p.addPoint(5*a1+3*a2, 3*b1);
		p.addPoint(3*a1+2*a2, 3*b1);
		fillAndDrawPolygon(g, d, o, p, Color.orange);

		p = new Polygon();
		p.addPoint(3*a1+2*a2, 3*b1);
		p.addPoint(5*a1+3*a2, 3*b1);
		p.addPoint(5*a1+3*a2, 5*b1);
		fillAndDrawPolygon(g, d, o, p, Color.green.darker().darker());

		// grid
		paintGrid(g, d, o);
	}

	/** paint the second variant.
	*/
	private void paintVariantB(Graphics g, Dimension d, Point o)
	{
		// parameters
		int a1 = getA1(d), a2 = getA2(d), b1 = getB1(d);

		// polygons
		Polygon p;

		p = new Polygon();
		p.addPoint(2*a1+1*a2, 2*b1);
		p.addPoint(5*a1+3*a2, 2*b1);
		p.addPoint(5*a1+3*a2, 5*b1);
		fillAndDrawPolygon(g, d, o, p, Color.red.darker());

		p = new Polygon();
		p.addPoint(3*a1+2*a2, 0*b1);
		p.addPoint(5*a1+3*a2, 0*b1);
		p.addPoint(5*a1+3*a2, 2*b1);
		p.addPoint(4*a1+2*a2, 2*b1);
		p.addPoint(4*a1+2*a2, 1*b1);
		p.addPoint(3*a1+2*a2, 1*b1);
		fillAndDrawPolygon(g, d, o, p, Color.green.darker());

		p = new Polygon();
		p.addPoint(2*a1+1*a2, 0*b1);
		p.addPoint(3*a1+1*a2, 0*b1);
		p.addPoint(3*a1+1*a2, 1*b1);
		p.addPoint(4*a1+2*a2, 1*b1);
		p.addPoint(4*a1+2*a2, 2*b1);
		p.addPoint(2*a1+1*a2, 2*b1);
		fillAndDrawPolygon(g, d, o, p, Color.orange);

		p = new Polygon();
		p.addPoint(0*a1+0*a2, 0*b1);
		p.addPoint(2*a1+1*a2, 0*b1);
		p.addPoint(2*a1+1*a2, 2*b1);
		fillAndDrawPolygon(g, d, o, p, Color.green.darker().darker());

		// grid
		paintGrid(g, d, o);
	}

	/** paint a grid.
	*/
	private void paintGrid(Graphics g, Dimension d, Point o)
	{
		// parameters
		int a1 = getA1(d), a2 = getA2(d), b1 = getB1(d);

		int lx = 5*a1+3*a2, ly = 5*b1;

		// calculate grid only once -- keeps the user less confused
		if (dgrid == null)
			dgrid = new Dimension(Math.min(a1, a2), b1);

		// note: draw inclusive upper bounds
		// note: dgrid.width may be 0, based on ratio
		if (dgrid.width > 0)
			for (int x = 0; x <= lx; x += dgrid.width)
				drawLine(g, d, o, x, 0, x, ly);
		if (dgrid.height > 0)
			for (int y = 0; y <= ly; y += dgrid.height)
				drawLine(g, d, o, 0, y, lx, y);
	}

	/** ratio setter. */
	protected void setRatio(int d, int q)
		throws IllegalArgumentException
	{
		// slightly normalize ratio and check for range
		// note: probably no need for using gcd()
		if (q == 0)
			throw new IllegalArgumentException();
		if (q < 0)
			d *= -1, q *= -1;
		if (d < 0)
			throw new IllegalArgumentException();

		dratio = d, qratio = q;
		repaint();
	}

	protected int getA1(Dimension d)
	{
		// width is 5*a1+3*a2, a2 is ratio*a1, so
		return (d.width * qratio) / (5 * qratio + 3 * dratio);
	}

	protected int getA2(Dimension d)
	{
		// a2 is ratio*a1, so
		return (getA1(d) * dratio) / qratio;
	}

	protected int getB1(Dimension d)
	{
		// height is 5*b1, so
		return d.height / 5;
	}

	/** draw a line relative to dimension and point.
	*/
	private static void drawLine(Graphics g, Dimension d, Point o,
		int x1, int y1, int x2, int y2)
	{
		// transform the coordinates
		int[] xp = new int[2], yp = new int[2];
		xp[0] = transformX(d, o, x1), yp[0] = transformY(d, o, y1);
		xp[1] = transformX(d, o, x2), yp[1] = transformY(d, o, y2);

		g.drawLine(xp[0], yp[0], xp[1], yp[1]);
	}

	/** draw a filled polygon relative to dimension and point.
	*/
	private static void fillPolygon(Graphics g, Dimension d, Point o,
		int[] xPoints, int[] yPoints, int nPoints)
	{
		// transform the coordinates
		int[] xp = new int[nPoints], yp = new int[nPoints];
		for (int i = 0; i < nPoints; i++)
			xp[i] = transformX(d, o, xPoints[i]), yp[i] = transformY(d, o, yPoints[i]);

		g.fillPolygon(xp, yp, nPoints);
	}

	/** draw a filled polygon relative to dimension and point.
	*/
	private static void fillPolygon(Graphics g, Dimension d, Point o,
		Polygon p)
	{
		fillPolygon(g, d, o, p.xpoints, p.ypoints, p.npoints);
	}

	/** draw a filled polygon with a color.
	*/
	private static void fillPolygon(Graphics g, Dimension d, Point o,
		int[] xPoints, int[] yPoints, int nPoints, Color c)
	{
		// set color
		Color cold = g.getColor();
		g.setColor(c);

		fillPolygon(g, d, o, xPoints, yPoints, nPoints);

		// reset color
		g.setColor(cold);
	}

	/** draw a filled polygon with a color.
	*/
	private static void fillPolygon(Graphics g, Dimension d, Point o,
		Polygon p, Color c)
	{
		fillPolygon(g, d, o, p.xpoints, p.ypoints, p.npoints, c);
	}

	/** draw a polygon relative to dimension and point.
	*/
	private static void drawPolygon(Graphics g, Dimension d, Point o,
		int[] xPoints, int[] yPoints, int nPoints)
	{
		// transform the coordinates
		int[] xp = new int[nPoints], yp = new int[nPoints];
		for (int i = 0; i < nPoints; i++)
			xp[i] = transformX(d, o, xPoints[i]), yp[i] = transformY(d, o, yPoints[i]);

		g.drawPolygon(xp, yp, nPoints);
	}

	/** draw polygon relative to dimension and point.
	*/
	private static void drawPolygon(Graphics g, Dimension d, Point o,
		Polygon p)
	{
		drawPolygon(g, d, o, p.xpoints, p.ypoints, p.npoints);
	}

	/** draw a filled polygon with border and a color.
	*/
	private static void fillAndDrawPolygon(Graphics g, Dimension d, Point o,
		Polygon p, Color c)
	{
		fillPolygon(g, d, o, p, c);
		drawPolygon(g, d, o, p);
	}

	/** transform the x coordinate relative to dimension and point.
	*/
	protected static int transformX(Dimension d, Point o, int x)
	{
		// transform the coordinate
		return x + o.x;
	}

	/** transform the y coordinate relative to dimension and point.
	*/
	protected static int transformY(Dimension d, Point o, int y)
	{
		// transform the coordinate
		return d.height - y + o.y;
	}
}
