// A Paint Program

// By Brian Prentice
// February 1997
   
import java.applet.*;
import java.awt.*;
import java.util.Vector;
import java.io.*;
import java.net.*;

public class Paint extends Applet
{
	final static Color backgroundColor = new Color(0, 0x80, 0x80);

	private int width;
	private int height;

	private Panel buttonPanel;
	private Panel cardControlPanel;
	private Panel toolsPanel;
	private Panel palettePanel;
	private Panel fontsPanel;
	private Panel cardPanel;
	private Panel panel1;
	private Panel panel2;
	private Panel panel3;
	private Panel panel4;
	private Panel panel5;

	private Button clearButton;
	private Button undoButton;
	private Button helpButton;
	private Button readButton;
	private Button writeButton;
	private Button toolsButton;
	private Button paletteButton;
	private Button fontsButton;

	private CardLayout cardLayout;

	private Tools tools;
	private Palette palette;
	private Fonts fonts;
	private ScribblePad scribblePad;	

	public void init()
	{
		width = size().width;
		height = size().height;
		setBackground(backgroundColor);

		clearButton = new Button("Clear");
		clearButton.setBackground(Color.lightGray);
		undoButton = new Button("Undo");
		undoButton.setBackground(Color.lightGray);
		helpButton = new Button("Help");
		helpButton.setBackground(Color.lightGray);
		readButton = new Button("Read");
		readButton.setBackground(Color.lightGray);
		writeButton = new Button("Write");
		writeButton.setBackground(Color.lightGray);
		buttonPanel = new Panel();
		buttonPanel.setLayout(new GridLayout(2, 3, 5, 5));
		buttonPanel.add(clearButton);
		buttonPanel.add(undoButton);
		buttonPanel.add(helpButton);
		buttonPanel.add(readButton);
		buttonPanel.add(writeButton);

		toolsButton = new Button("Tool");
		toolsButton.setBackground(Color.lightGray);
		paletteButton = new Button("Palette");
		paletteButton.setBackground(Color.lightGray);
		fontsButton = new Button("Font");
		fontsButton.setBackground(Color.lightGray);
		cardControlPanel = new Panel();
		cardControlPanel.setLayout(new GridLayout(1, 3, 5, 5));
		cardControlPanel.add(toolsButton);
		cardControlPanel.add(paletteButton);
		cardControlPanel.add(fontsButton);

		scribblePad = new ScribblePad(this);

		toolsPanel = new Panel();
		tools = new Tools(scribblePad);
		toolsPanel.add(tools);

		palettePanel = new Panel();
		palette = new Palette(scribblePad);
		palettePanel.add(palette);

		fontsPanel = new Panel();
		fonts = new Fonts(scribblePad);
		fontsPanel.add(fonts);

		cardPanel = new Panel();
		cardLayout = new CardLayout();
		cardPanel.setLayout(cardLayout);
		cardPanel.add("tools", toolsPanel);
		cardPanel.add("palette", palettePanel);
		cardPanel.add("fonts", fontsPanel);

		panel1 = new Panel();
		panel1.add(buttonPanel);
		panel2 = new Panel();
		panel2.add(cardControlPanel);
		panel3 = new Panel();
		panel3.add(cardPanel);
		panel4 = new Panel();
		panel4.setLayout(new BorderLayout());
		panel4.add("North", panel2);
		panel4.add("Center", panel3);
		panel5 = new Panel();
		panel5.setLayout(new BorderLayout());
		panel5.add("North", panel1);
		panel5.add("Center", panel4);
		
		
		setLayout(new BorderLayout());
		add("West", panel5);
		add("Center", scribblePad);

	}

	public Insets insets()
	{
		return new Insets(10, 10, 10, 10);
	}

	public boolean action(Event e, Object arg)
	{
		if (e.target == clearButton)
		{
			repaint();
			scribblePad.clear();
		}
		else if (e.target == undoButton)
			scribblePad.undo();
		else if (e.target == readButton)
			getAppletContext().showStatus(scribblePad.read());
		else if (e.target == writeButton)
			getAppletContext().showStatus(scribblePad.write());
		else if (e.target == toolsButton)
			cardLayout.show(cardPanel, "tools");
		else if (e.target == paletteButton)
			cardLayout.show(cardPanel, "palette");
		else if (e.target == fontsButton)
			cardLayout.show(cardPanel, "fonts");
		else if (e.target == helpButton)
		{
			try
			{				
				getAppletContext().showDocument(new URL(getCodeBase(), "PaintHelp.html"));
			}
			catch(Exception error)
			{
				System.out.println("" + error);
			}
		}
		else
			return false;
		return true;
	}
	
	public boolean keyUp(Event event, int key)
	{
		scribblePad.move(event, key);
		return true;
	}
}

class IntField extends TextField
{
	private int value;
	private int max;
	private int min;

	public IntField(String value, int size, int min, int max)
	{
		super(value, size);
		this.max = max;
		this.min = min;
	}

	private int stoi(String s)
	{
		return (new Integer(s)).intValue();
	}

	private String itos(int i)
	{
		return (new Integer(i)).toString();
	}

	public int getValue()
	{
		validateValue();
		return value;
	}

	public int incValue(int inc)
	{
		validateValue();
		setText(itos(stoi(getText()) + inc));
		validateValue();
		return value;
	}

	public void validateValue()
	{
		String s = getText();		
		if (s.equals(""))
		{
			value = min;
			setText(itos(min));
		}
		else
		{
			value = stoi(s);
			if (value < min)
			{
				value = min;
				setText(itos(min));
			}
			else if (value > max)
			{
				value = max;
				setText(itos(max));
			}
		}
	}

	public boolean keyDown(Event event, int key)
	{
		if ((key >= 0x30) && (key <= 0x39))
		{
			return false;
		}
		switch (key)
		{
			case 0x08:
			case 0x0A:
			case 0x7F:
			case Event.RIGHT:
			case Event.LEFT:
			case Event.HOME:
			case Event.END:
				return false;
			default:
				return true;
		}
	}
}

class Tools extends Panel
{
	final static Color backgroundColor = new Color(220, 220, 220);

	final static String freehandName = "Freehand";
	final static String lineName = "Line";
	final static String rectangleName = "Rectangle";
	final static String ovalName = "Oval";
	final static String polygonName = "Polygon";
	final static String curveName = "Curve";
	final static String filledRectangleName = "Filled Rectangle";
	final static String filledOvalName = "Filled Oval";
	final static String filledPolygonName = "Filled Polygon";
	final static String filledCurveName = "Filled Curve";
	final static String lightSprayName = "Light Spray";
	final static String heavySprayName = "Heavy Spray";
	final static String textName = "Text";
	final static String modes[] =
	{
		freehandName, lineName, rectangleName, ovalName, polygonName, curveName,
		filledRectangleName, filledOvalName, filledPolygonName, filledCurveName,
		lightSprayName, heavySprayName, textName
	};

	private Panel panel1;
	private Panel panel2;
	private Panel panel3;
	private Panel panel4;

	private Choice modeSelector;
	private Button downButton;
	private IntField lineSizeInput;
	private Button upButton;

	private ScribblePad scribblePad;

	public Tools(ScribblePad scribblepad)
	{		
		setBackground(backgroundColor);

		this.scribblePad = scribblepad;

		panel1 = new Panel();
		panel1.add(new Label("Mode Selector"));

		modeSelector = new Choice();
		for (int i = 0; i < modes.length; i++)
			modeSelector.addItem(modes[i]);
		panel2 = new Panel();
		panel2.add(modeSelector);

		panel3 = new Panel();
		panel3.add(new Label("Line Size Selector"));

		downButton = new Button("<");
		lineSizeInput = new IntField("1", 2, 1, 100);
		upButton = new Button(">");

		panel4 = new Panel();
		panel4.add(downButton);
		panel4.add(lineSizeInput);
		panel4.add(upButton);		

		setLayout(new GridLayout(4, 1));
		add(panel1);
		add(panel2);
		add(panel3);
		add(panel4);
	}

	public boolean keyUp(Event event, int key)
	{
		if (key == 10)
			scribblePad.setLineSize(lineSizeInput.getValue());
		return false;
	}

	public boolean action(Event event, Object arg)
	{
		if (event.target == modeSelector)
			scribblePad.setMode(modeSelector.getSelectedIndex());
		else if (event.target == downButton)
			scribblePad.setLineSize(lineSizeInput.incValue(-1));
		else if (event.target == upButton)
			scribblePad.setLineSize(lineSizeInput.incValue(1));
		else
			return false;
		return true;
	}
}

class Palette extends Panel
{
	final static Color backgroundColor = new Color(220, 220, 220);

	final static Color yellowGreen = Color.getColor("", 0x9ACD32);
	final static Color lightBlue = Color.getColor("", 0xADE8E6);
	final static Color teal = Color.getColor("", 0x008080);
	final static Color navyBlue = Color.getColor("", 0x23238E);
	final static Color darkGreen = Color.getColor("", 0x215E21);
	final static Color brown = Color.getColor("", 0xA62A2A);
	final static Color purple = Color.getColor("", 0x800080);
	final static Color defaultColor[] =
	{
		Color.black, Color.darkGray, Color.gray, Color.lightGray,
		Color.white, Color.red, Color.pink, Color.orange, Color.yellow,
		Color.cyan, Color.blue, Color.magenta, Color.green,
		yellowGreen, lightBlue, teal, navyBlue, darkGreen, brown, purple
	};

	private Panel panel1;
	private Panel panel2;
	private Panel panel3;
	private Panel panel4;
	private Panel panel5;
	private Panel panel6;
	private Panel panel7;
	private Panel panel8;
	private Panel panel9;
	private Panel panel10;
	private Panel panel11;
	private Panel panel12;
	private Panel panel13;

	private Label redLabel;
	private Button redFastDown;
	private Button redDown;
	private IntField redValue;
	private Button redUp;
	private Button redFastUp;

	private Label greenLabel;
	private Button greenFastDown;
	private Button greenDown;
	private IntField greenValue;
	private Button greenUp;
	private Button greenFastUp;

	private Label blueLabel;
	private Button blueFastDown;
	private Button blueDown;
	private IntField blueValue;
	private Button blueUp;
	private Button blueFastUp;

	private CustomColor customColor;

	private Button scrollButton;
	private Button addButton;
	private Button mixButton;

	private Vector colorSelector;

	private Button saveButton;
	private Button restoreButton;

	private Color color[];
	private Vector customPalette;
	private int palettePointer;

	private ScribblePad scribblePad;

	public Palette(ScribblePad scribblePad)
	{		
		setBackground(backgroundColor);

		color = new Color[defaultColor.length];		
		for (int i = 0; i < defaultColor.length; i++)
			color[i] = defaultColor[i];
		customPalette = new Vector();
		customPalette.addElement(color);
		palettePointer = 0;

		this.scribblePad = scribblePad;

		redFastDown = new Button("<<");
		redDown = new Button("<");
		greenFastDown = new Button("<<");
		greenDown = new Button("<");
		blueFastDown = new Button("<<");
		blueDown = new Button("<");

		panel1 = new Panel();		
		panel1.setLayout(new GridLayout(3, 2, 2, 2));
		panel1.add(redFastDown);
		panel1.add(redDown);
		panel1.add(greenFastDown);
		panel1.add(greenDown);
		panel1.add(blueFastDown);
		panel1.add(blueDown);

		redLabel = new Label("red");
		greenLabel = new Label("green");
		blueLabel = new Label("blue");

		panel2 = new Panel();		
		panel2.setLayout(new GridLayout(3, 1, 2, 2));
		panel2.add(redLabel);
		panel2.add(greenLabel);
		panel2.add(blueLabel);

		redValue = new IntField("255", 3, 0, 255);
		greenValue = new IntField("0", 3, 0, 255);
		blueValue = new IntField("0", 3, 0, 255);

		panel3 = new Panel();		
		panel3.setLayout(new GridLayout(3, 1, 2, 2));
		panel3.add(redValue);
		panel3.add(greenValue);
		panel3.add(blueValue);

		redUp = new Button(">");
		redFastUp = new Button(">>");
		greenUp = new Button(">");
		greenFastUp = new Button(">>");
		blueUp = new Button(">");
		blueFastUp = new Button(">>");

		panel4 = new Panel();		
		panel4.setLayout(new GridLayout(3, 2, 2, 2));
		panel4.add(redUp);
		panel4.add(redFastUp);
		panel4.add(greenUp);
		panel4.add(greenFastUp);
		panel4.add(blueUp);
		panel4.add(blueFastUp);
		
		panel5 = new Panel();
		panel5.add(panel1);		
		panel5.add(panel2);		
		panel5.add(panel3);		
		panel5.add(panel4);

		customColor = new CustomColor(Color.red);

		panel6 = new Panel();
		panel6.add(customColor);

		scrollButton = new Button("Scroll");
		addButton = new Button("Add");
		mixButton = new Button("Mix");

		panel7 = new Panel();
		panel7.setLayout(new GridLayout(1, 3, 10, 0));
		panel7.add(scrollButton);
		panel7.add(addButton);
		panel7.add(mixButton);

		panel8 = new Panel();
		panel8.add(panel7);

		saveButton = new Button("Save");
		restoreButton = new Button("Restore");

		panel9 = new Panel();
		panel9.setLayout(new GridLayout(1, 2, 10, 0));
		panel9.add(saveButton);
		panel9.add(restoreButton);

		panel10 = new Panel();
		panel10.add(panel9);

		colorSelector = new Vector();
		for (int i = 0; i < color.length; i++)
			colorSelector.addElement(new ColorSelector(color[i], scribblePad));

		panel11 = new Panel();
		panel11.setLayout(new GridLayout(2, 8));
		for (int i = 0; i < color.length; i++)
			panel11.add((ColorSelector) colorSelector.elementAt(i));

		panel12 = new Panel();
		panel12.add(panel11);

		panel13 = new Panel();
		panel13.setLayout(new GridLayout(3, 1));
		panel13.add(panel6);
		panel13.add(panel8);
		panel13.add(panel10);

		setLayout(new BorderLayout());
		add("North", panel5);
		add("South", panel12);
		add("Center", panel13);
	}

	private void setColor()
	{
		customColor.setColor(redValue.getValue(), greenValue.getValue(), blueValue.getValue());
	}

	private void repaintColorSelectors()
	{
		for (int i = 0; i < color.length; i++)
			((ColorSelector) colorSelector.elementAt(i)).repaint();
	}

	public boolean action(Event event, Object arg)
	{
		if (event.target == redFastDown)
		{
			redValue.incValue(-25);
			setColor();
		}
		else if (event.target == redDown)
		{
			redValue.incValue(-1);
			setColor();
		}
		else if (event.target == redFastUp)
		{
			redValue.incValue(25);
			setColor();
		}
		else if (event.target == redUp)
		{
			redValue.incValue(1);
			setColor();
		}
		else if (event.target == greenFastDown)
		{
			greenValue.incValue(-25);
			setColor();
		}
		else if (event.target == greenDown)
		{
			greenValue.incValue(-1);
			setColor();
		}
		else if (event.target == greenFastUp)
		{
			greenValue.incValue(25);
			setColor();
		}
		else if (event.target == greenUp)
		{
			greenValue.incValue(1);
			setColor();
		}
		else if (event.target == blueFastDown)
		{
			blueValue.incValue(-25);
			setColor();
		}
		else if (event.target == blueDown)
		{
			blueValue.incValue(-1);
			setColor();
		}
		else if (event.target == blueFastUp)
		{
			blueValue.incValue(25);
			setColor();
		}
		else if (event.target == blueUp)
		{
			blueValue.incValue(1);
			setColor();
		}
		else if (event.target == addButton)
		{
			((ColorSelector) colorSelector.elementAt(0)).setColor(customColor.getColor());
			ColorSelector.setSelectedColor(customColor.getColor());
			repaintColorSelectors();
		}
		else if (event.target == scrollButton)
		{
			int e = color.length - 1;
			ColorSelector.setSelectedColor(((ColorSelector) colorSelector.elementAt(1)).
																				getColor());
			Color c = ((ColorSelector) colorSelector.elementAt(e)).getColor();
			for (int i = e - 1; i >= 0; i--)
			{
				Color t = ((ColorSelector) colorSelector.elementAt(i)).getColor();
				((ColorSelector) colorSelector.elementAt(i)).setColor(c);
				c = t;
			}
			((ColorSelector) colorSelector.elementAt(e)).setColor(c);
		}
		else if (event.target == mixButton)
		{
			int nc = color.length;
			Color startColor = ((ColorSelector) colorSelector.elementAt(0)).getColor();
			Color endColor = ((ColorSelector) colorSelector.elementAt(nc - 1)).getColor();
			ColorSelector.setSelectedColor(startColor);
			int red = startColor.getRed();
			int green = startColor.getGreen();
			int blue = startColor.getBlue();
			int redInc = (endColor.getRed() - red) / nc;
			int greenInc = (endColor.getGreen() - green) / nc;
			int blueInc = (endColor.getBlue() - blue) / nc;
			for (int i = 1; i < nc - 1; i++)
			{
				red += redInc;
				green += greenInc;
				blue += blueInc;
				Color c = new Color(red, green, blue);
				((ColorSelector) colorSelector.elementAt(i)).setColor(c);
			}
			repaintColorSelectors();
		}
		else if (event.target == saveButton)
		{
			Color colors[] = new Color[color.length];
			for (int i = 0; i < color.length; i++)
				colors[i] = ((ColorSelector) colorSelector.elementAt(i)).getColor();
			customPalette.addElement(colors);
		}
		else if (event.target == restoreButton)
		{
			ColorSelector.setSelectedColor(((Color[]) customPalette.
															elementAt(palettePointer))[0]);
			for (int i = 0; i < color.length; i++)
				((ColorSelector) colorSelector.elementAt(i)).
							setColor(((Color[]) customPalette.elementAt(palettePointer))[i]);
			palettePointer = (palettePointer + 1) % customPalette.size();
		}
		else
			return false;
		return true;
	}

	public boolean keyUp(Event event, int key)
	{
		if (key == 10)
			setColor();
		return false;
	}

	public boolean mouseUp(Event event, int x, int y)
	{
		if (panel12.bounds().inside(x, y))
		{
			repaintColorSelectors();
			return true;
		}		
		return false;
	}
}

class CustomColor extends Canvas
{
	final static int width = 100;
	final static int height = 25;

	public CustomColor(Color color)
	{
		setBackground(color);
		resize(width, height);
	}

	public void setColor(int red, int green, int blue)
	{
		setBackground(new Color(red, green, blue));
		repaint();
	}

	public Color getColor()
	{
		return getBackground();
	}

	public void paint(Graphics g)
	{
		Rectangle r = bounds();
		g.setColor(getBackground());
		g.fillRect(0, 0, r.width, r.height);
	}
}

class ColorSelector extends Canvas
{
	final static int width = 20;
	final static int height = 20;

	private static Color selectedColor = Color.black;
	private Color color;
	private ScribblePad scribblePad;

	public ColorSelector(Color buttonColor, ScribblePad scribblePad)
	{
		color = buttonColor;
		this.scribblePad = scribblePad;
		resize(width, height);
	}

	public static void setSelectedColor(Color newColor)
	{
		selectedColor = newColor;		
	}

	public void setColor(Color newColor)
	{
		color = newColor;
		repaint();
	}

	public Color getColor()
	{
		return color;
	}

	public void paint(Graphics g)
	{
		g.setColor(color);
		g.fillRect(2, 2, width - 4, height - 4);
		if (color.equals(selectedColor))
		{
			scribblePad.setColor(selectedColor);
			g.setColor(Color.black);
		}
		else
			g.setColor(Palette.backgroundColor);
		g.drawRect(0, 0, width - 1, height - 1);
	}

	public boolean mouseUp(Event event, int x, int y)
	{
		selectedColor = color;
		scribblePad.setColor(color);
		return false;
	}
}

class Fonts extends Panel
{
	final static Color backgroundColor = new Color(220, 220, 220);
   
	private Panel panel1;
	private Panel panel2;
	private Panel panel3;
	private Panel panel4;
	private Panel panel5;
	private Panel panel6;
	private Panel panel7;
	private Panel panel8;

	private List fontName;
	private Checkbox bold;
	private Checkbox italic;
	private int style;
	private Label label;
	private IntField fontSize;
	private Button select;

	private ScribblePad scribblePad;

	public Fonts(ScribblePad scribblePad)
	{
		setBackground(backgroundColor);

		this.scribblePad = scribblePad;
    
		fontName = new List(3, false);
		fontName.addItem("SansSerif");
		fontName.addItem("Serif");
		fontName.addItem("Monospaced");
		fontName.select(0);                  
		panel1 = new Panel();
		panel1.add(fontName);

		bold = new Checkbox("Bold");
		panel2 = new Panel();
		panel2.add(bold);

		italic = new Checkbox("Italic");
		panel3 = new Panel();
		panel3.add(italic);

		panel4 = new Panel();
		panel4.setLayout(new GridLayout(1, 2));
		panel4.add(panel2);
		panel4.add(panel3);

		fontSize = new IntField("15", 2, 9, 100);
		label = new Label("Size");
		panel5 = new Panel();
		panel5.add(fontSize);
		panel5.add(label);

		panel6 = new Panel();
		panel6.setLayout(new GridLayout(2, 1));
		panel6.add(panel4);
		panel6.add(panel5);

		panel7 = new Panel();
		panel7.setLayout(new BorderLayout());
		panel7.add("North", panel1);
		panel7.add("South", panel6);

		select = new Button("Select");
		panel8 = new Panel();
		panel8.add(select);

		setLayout(new BorderLayout());
		add("North", panel7);
		add("South", panel8);

		style = 0;
	}

	public boolean action(Event event, Object arg)
	{
		if (event.target == select)
		{
			String name = fontName.getSelectedItem();
			if (name == null)
				name = "SansSerif";
			int size = fontSize.getValue();
			scribblePad.setFont(name, style, size);			
		}
		else if ((event.target == bold) || (event.target == italic))
			style = (bold.getState() ? Font.BOLD : 0) | (italic.getState() ? Font.ITALIC : 0);
		else
			return false;
		return true;
	}   
}

class GraphicsExtension
{
    final static int DDA_SCALE = 8192;

    static void drawThickLine(Graphics g, int x1, int y1, int x2, int y2, int linewidth)
	{
		g.fillOval(x1 - linewidth / 2, y1 - linewidth / 2, linewidth, linewidth);

		if (x1 == x2 && y1 == y2)
		    return;

		if (Math.abs(x2 - x1) > Math.abs(y2 - y1))
    	{
		    int dy, srow;
		    int dx, col, row, prevrow;

		    if (x2 > x1)
				dx = 1;
		    else
				dx = -1;
		    dy = (y2 - y1) * DDA_SCALE / Math.abs(x2 - x1);
		    prevrow = row = y1;
		    srow = row * DDA_SCALE + DDA_SCALE / 2;
		    col = x1;
		    for (;;)
			{
				if (row != prevrow)
			    {
				    g.drawOval(col - linewidth / 2, prevrow - linewidth / 2,
							   linewidth, linewidth);
				    prevrow = row;
			    }
				g.drawOval(col - linewidth / 2, row - linewidth / 2,
						   linewidth, linewidth);
				if (col == x2)
				    break;
				srow += dy;
				row = srow / DDA_SCALE;
				col += dx;
			}
	    }
		else
	    {
		    int dx, scol;
		    int dy, col, row, prevcol;

		    if (y2 > y1)
				dy = 1;
		    else
				dy = -1;
		    dx = (x2 - x1) * DDA_SCALE / Math.abs(y2 - y1);
		    row = y1;
		    prevcol = col = x1;
		    scol = col * DDA_SCALE + DDA_SCALE / 2;
		    for (;;)
			{
				if (col != prevcol)
			    {
				    g.drawOval(prevcol - linewidth / 2, row - linewidth / 2,
							   linewidth, linewidth);
			    	prevcol = col;
			    }
				g.drawOval(col - linewidth / 2, row - linewidth / 2,
						   linewidth, linewidth);
				if (row == y2)
				    break;
				row += dy;
				scol += dx;
				col = scol / DDA_SCALE;
			}
	    }
	}

	static void drawThickRectangle(Graphics g, int x, int y, int width, int height,
								   int linewidth)
	{
		drawThickLine(g, x, y, x + width, y, linewidth);
		drawThickLine(g, x + width, y, x + width, y + height, linewidth);
		drawThickLine(g, x + width, y + height, x, y + height, linewidth);
		drawThickLine(g, x, y + height, x, y, linewidth);
	}

	static void drawThickOval(Graphics g, int x1, int y1, int width, int height, int linewidth)
	{
		int xd = x1 + (width - linewidth) / 2;
		int yd = y1 + (height - linewidth) / 2;
		double a = width / 2;
		double as = a * a;
		double b = height / 2;
		double bs = b * b;
		int x = 0;
		int y = (int) b;
		double d = bs - as * b + as / 4.0;
		drawThickOvalPoints(g, x, y, xd, yd, linewidth);
		while (as * (y - 0.5) > bs * (x + 1.0))
		{
			if (d < 0)
			{
				d = d + bs * (2.0 * x + 3.0);
				x++;
			}
			else
			{
				d = d + bs * (2.0 * x + 3.0) + as * (2.0 - 2.0 * y);
				x++;
				y--;
			}
			drawThickOvalPoints(g, x, y, xd, yd, linewidth);
		}
		d = bs * (x + 0.5) * (x + 0.5) + as * (y - 1) * (y - 1) - as * bs;
		while (y > 0)
		{
			if (d < 0)
			{
				d = d + bs * (2.0 * x + 2.0) + as * (3.0 - 2.0 * y);
				x++;
				y--;
			}
			else
			{
				d = d + as * (3.0 - 2.0 * y);
				y--;
			}
			drawThickOvalPoints(g, x, y, xd, yd, linewidth);
		}
	}

	static private void drawThickOvalPoints(Graphics g, int x, int y, int xd, int yd, int w)
	{
		g.fillOval(xd + x, yd + y, w, w);
		g.fillOval(xd + x, yd - y, w, w);
		g.fillOval(xd - x, yd - y, w, w);
		g.fillOval(xd - x, yd + y, w, w);
	}

	static void drawThickPolygon(Graphics g, Polygon p, int linewidth)
	{
		for (int i = 0; i < p.npoints; i++)
			drawThickLine(g, p.xpoints[i], p.ypoints[i],
				p.xpoints[(i + 1) % p.npoints], p.ypoints[(i + 1) % p.npoints], linewidth);
	}

	static void drawCurve(Graphics g, int cx0, int cy0, int cx1, int cy1,
									  int cx2, int cy2, int cx3, int cy3, int lineSize)
	{
		double cx = 3 * (cx1 - cx0);
		double bx = 3 * (cx2 - cx1) - cx;
		double ax = cx3 - cx0 - cx - bx;		
		double cy = 3 * (cy1 - cy0);
		double by = 3 * (cy2 - cy1) - cy;
		double ay = cy3 - cy0 - cy - by;
		int x1 = cx0;
		int y1 = cy0;
		for (double t = .01; t < (1.005); t += .01)
		{
			double t2 = t * t;
			double t3 = t2 * t;
			int x2 = (int) (ax * t3 + bx * t2 + cx * t + cx0);
			int y2 = (int) (ay * t3 + by * t2 + cy * t + cy0);
			if (lineSize == 1)
				g.drawLine(x1, y1, x2, y2);
			else
				drawThickLine(g, x1, y1, x2, y2, lineSize);
			x1 = x2;
			y1 = y2;
		}
	}
	static void drawFilledCurve(Graphics g, int cx0, int cy0, int cx1, int cy1,
											int cx2, int cy2, int cx3, int cy3, int lineSize)
	{
		Polygon p = new Polygon();
		double cx = 3 * (cx1 - cx0);
		double bx = 3 * (cx2 - cx1) - cx;
		double ax = cx3 - cx0 - cx - bx;		
		double cy = 3 * (cy1 - cy0);
		double by = 3 * (cy2 - cy1) - cy;
		double ay = cy3 - cy0 - cy - by;
		p.addPoint(cx0, cy0);
		for (double t = .01; t < (1.005); t += .01)
		{
			double t2 = t * t;
			double t3 = t2 * t;
			int x = (int) (ax * t3 + bx * t2 + cx * t + cx0);
			int y = (int) (ay * t3 + by * t2 + cy * t + cy0);
			p.addPoint(x, y);
		}
		p.addPoint(cx0, cy0);
		g.fillPolygon(p);
	}
}

interface ToolConstants
{
	final static int freehandMode = 0;
	final static int lineMode = 1;
	final static int rectangleMode = 2;
	final static int ovalMode = 3;
	final static int polygonMode = 4;
	final static int curveMode = 5;
	final static int filledRectangleMode = 6;
	final static int filledOvalMode = 7;
	final static int filledPolygonMode = 8;
	final static int filledCurveMode = 9;
	final static int lightSprayMode = 10;
	final static int heavySprayMode = 11;
	final static int textMode = 12;
}

class ScribblePad extends Canvas implements ToolConstants
{
	final static Color XORColor = new Color(0xB0, 0x60, 0x90);

	private Picture picture;
	private Color backgroundColor = Color.white;
	private Color color = Color.black;
	private int lineSize = 1;
	private int mode = freehandMode;
	private Font font;
	private Polygon polygon;
	private int old_x, old_y, new_x, new_y;
	private int curveControl = 0;
	private int cx0, cy0, cx1, cy1, cx2, cy2, cx3, cy3;
	private int text_x0, text_x, text_y;
	String text = "";
	private boolean haveCursor = false;

	public ScribblePad(Applet paint)
	{
		picture = new Picture(paint, backgroundColor);
		font = new Font("SansSerif", 0, 15);
	}

	private void drawCursor()
	{
       	Graphics g = getGraphics();
		g.setFont(font);
       	g.setColor(color);
		FontMetrics fm = g.getFontMetrics(font);
		int size = fm.getAscent();
		g.setXORMode(XORColor);
		g.drawRect(text_x, text_y - size, size / 2, size);
		g.setPaintMode();
		g.dispose();
		haveCursor = true;
	}

	private void removeCursor()
	{
		if (haveCursor)
		{
			drawCursor();
			haveCursor = false;
		}
	}

	private void cleanup()
	{
		removeCursor();
		if ((mode == polygonMode) || (mode == filledPolygonMode))
		{
			if (polygon != null)
			{
				polygon = null;
				undo();
			}
		}
		if (curveControl != 0)
		{
			curveControl = 0;
			repaint();
		}
	}

	public void setColor(Color newColor)
	{
		cleanup();
		color = newColor;
	}

	public void setLineSize(int newLineSize)
	{
		cleanup();
		lineSize = newLineSize;
	}

	public void setMode(int newMode)
	{
		cleanup();
		mode = newMode;
	}

	public void setFont(String name, int style, int size)
	{
		cleanup();
		font = new Font(name, style, size);
	}

	private int fontToInt(Font font)
	{
		int f = font.getSize();
		if (font.isItalic())
			f |= 0x100;
		if (font.isBold())
			f |= 0x200;
		String name = font.getName();
		if (name.equals("SansSerif"))
			f |= 0x400;
		else if (name.equals("Serif"))
			f |= 0x800;
		else if (name.equals("Monospaced"))
			f |= 0x1000;
		return f;
	}

	public void clear()
	{
		backgroundColor = color;
		picture.clear(backgroundColor);
		cleanup();
		repaint();
	}

	public void paint(Graphics g)
	{
		Rectangle r = bounds();
		setBackground(backgroundColor);
		g.setColor(backgroundColor);
		g.fillRect(0, 0, r.width, r.height);
		picture.drawPicture(g);
		if ((mode == textMode) && (haveCursor))
			drawCursor();
		getParent().repaint();
	}

	public void undo()
	{
		picture.undo();
		cleanup();
		repaint();
	}

	public String read()
	{
		String status = picture.readPicture();
		if (status.startsWith("Error"))
			return status;
		backgroundColor = picture.getBackgroundColor();
		cleanup();
		repaint();
		return status;
	}

	public String write()
	{
		return picture.writePicture();
	}

    public boolean mouseDown(Event e, int x, int y)
    {
		if (picture.full())
			return true;
		switch (mode)
		{
			case freehandMode:
				picture.initSegments(mode, 0, lineSize, color, x, y);
				break;
			case polygonMode:
			case filledPolygonMode:
				if (polygon != null)
				{
        			Graphics g = getGraphics();
        			g.setColor(color);
					g.setXORMode(XORColor);
					g.drawLine(old_x, old_y, new_x, new_y);
					g.drawLine(old_x, old_y, x, y);
					g.setPaintMode();
					g.dispose();
					new_x = x;
					new_y = y;
					return true;
				}
				else
				{
					polygon = new Polygon();
					polygon.addPoint(x, y);
					picture.initSegments(mode, 1, lineSize, color, x, y);
				}
			case lineMode:
        		Graphics g = getGraphics();
        		g.setColor(color);
				g.setXORMode(XORColor);
				g.drawLine(x, y, x, y);
				g.setPaintMode();
				g.dispose();
			case rectangleMode:
			case ovalMode:
			case filledRectangleMode:
			case filledOvalMode:
				new_x = x;
				new_y = y;
				break;
		}
        old_x = x;
		old_y = y;
        return true;
    }
    
    public boolean mouseDrag(Event e, int x, int y)
    {
		if (picture.full())
			return true;
        Graphics g = getGraphics();
		switch (mode)
		{
			case freehandMode:
				picture.addSegment(x, y);
        		g.setColor(color);
				if (lineSize == 1)
					g.drawLine(old_x, old_y, x, y);
				else
			        GraphicsExtension.drawThickLine(g, old_x, old_y, x, y, lineSize);
	        	old_x = x;
		        old_y = y;
		        break;			
			case lineMode:
			case polygonMode:
			case filledPolygonMode:
				g.setColor(color);
				g.setXORMode(XORColor);
				g.drawLine(old_x, old_y, new_x, new_y);
				g.drawLine(old_x, old_y, x, y);
				g.setPaintMode();
				new_x = x;
				new_y = y;
				break;
			case curveMode:
			case filledCurveMode:
				if (curveControl < 2)
					break;
				g.setColor(color);
				g.setXORMode(XORColor);
				GraphicsExtension.drawCurve(g, cx0, cy0, cx1, cy1, cx2, cy2, cx3, cy3, 1);
				if ((e.modifiers & Event.SHIFT_MASK) != 0)			
				{
					cx1 = x;
					cy1 = y;
				}
				else
				{
					cx2 = x;
					cy2 = y;
				}
				GraphicsExtension.drawCurve(g, cx0, cy0, cx1, cy1, cx2, cy2, cx3, cy3, 1);
				g.setPaintMode();
				break;
			case rectangleMode:
			case filledRectangleMode:
				g.setColor(color);
				g.setXORMode(XORColor);
				g.drawRect(Math.min(new_x, old_x), Math.min(new_y, old_y),
						   Math.abs(new_x - old_x), Math.abs(new_y - old_y));
				g.drawRect(Math.min(x, old_x), Math.min(y, old_y),
						   Math.abs(x - old_x), Math.abs(y - old_y));
				g.setPaintMode();
				new_x = x;
				new_y = y;
				break;
			case ovalMode:
			case filledOvalMode:
				g.setColor(color);
				g.setXORMode(XORColor);
				g.drawOval(Math.min(new_x, old_x), Math.min(new_y, old_y),
						   Math.abs(new_x - old_x), Math.abs(new_y - old_y));
				g.drawOval(Math.min(x, old_x), Math.min(y, old_y),
						   Math.abs(x - old_x), Math.abs(y - old_y));
				g.setPaintMode();
				new_x = x;
				new_y = y;
				break;
		}
		g.dispose();
		return true;
	}

	public boolean mouseUp(Event e, int x, int y)
	{
		if (picture.full())
			return true;
		if (mode == freehandMode)
			return true;
		int x1, y1, w, h;
		if (mode == lineMode)
		{
        	Graphics g = getGraphics();
        	g.setColor(color);
			if (lineSize == 1)
				g.drawLine(old_x, old_y, new_x, new_y);
			else
		        GraphicsExtension.drawThickLine(g, old_x, old_y, new_x, new_y, lineSize);
			g.dispose();
			picture.addItem(mode, lineSize, color, old_x, old_y, new_x, new_y);
			return true;
		}
		if ((mode == polygonMode) || (mode == filledPolygonMode))
		{
        	Graphics g = getGraphics();
			if ((Math.abs(x - polygon.xpoints[0]) < 5) &&
				(Math.abs(y - polygon.ypoints[0]) < 5) && (polygon.npoints > 1))
			{
				g.setColor(color);
				g.setXORMode(XORColor);
				g.drawLine(old_x, old_y, new_x, new_y);
				g.setPaintMode();
				polygon.addPoint(polygon.xpoints[0], polygon.ypoints[0]);
				picture.addSegment(polygon.xpoints[0], polygon.ypoints[0]);
				if (mode == polygonMode)
					if (lineSize == 1)
						g.drawPolygon(polygon);
					else
						GraphicsExtension.drawThickPolygon(g, polygon, lineSize);
				else
					g.fillPolygon(polygon);
				polygon = null;
			}
			else
			{
				g.setColor(color);
				g.drawLine(old_x, old_y, new_x, new_y);
				g.setXORMode(XORColor);
				g.drawLine(x, y, x, y);				
				g.setPaintMode();
				polygon.addPoint(x, y);
				picture.addSegment(x, y);
				old_x = x;
				old_y = y;
				new_x = x;
				new_y = y;
			}
			g.dispose();
			return true;
		}
		if ((mode == curveMode) || (mode == filledCurveMode))
		{
			switch (curveControl)
			{
				case 0:
					cx0 = x;
					cy0 = y;
					cx1 = x;
					cy1 = y;
					curveControl = 1;
					break;
				case 1:
					cx2 = x;
					cy2 = y;
					cx3 = x;
					cy3 = y;
		        	Graphics g = getGraphics();
		        	g.setColor(color);
					g.setXORMode(XORColor);
					GraphicsExtension.drawCurve(g, cx0, cy0, cx1, cy1, cx2, cy2, cx3, cy3, 1);
					g.setPaintMode();
					g.dispose();
					curveControl = 2;
					break;
				default:				
		        	g = getGraphics();
		        	g.setColor(color);
					GraphicsExtension.drawCurve(g, cx0, cy0, cx1, cy1,
															 cx2, cy2, cx3, cy3, lineSize);
					if (mode == filledCurveMode)
						GraphicsExtension.drawFilledCurve(g, cx0, cy0, cx1, cy1,
															 cx2, cy2, cx3, cy3, lineSize);
					g.dispose();
					picture.addCurve(mode, lineSize, color, cx0, cy0, cx1, cy1,
															cx2, cy2, cx3, cy3);
					curveControl = 0;
			}
			return true;
		}
		if ((mode == lightSprayMode) || (mode == heavySprayMode))
		{
        	Graphics g = getGraphics();
        	g.setColor(color);
			for (int i = 0; i < 25; i++)
			{
				int radius = (int) (Math.random() * 15);
				int theta = (int) (Math.random() * 360);
				int sx = x + (int) (radius * Math.sin(theta));
				int sy = y + (int) (radius * Math.cos(theta));
				if (mode == lightSprayMode)
					g.drawLine(sx, sy, sx, sy);
				else
					g.drawOval(sx, sy, 1, 1);
			}
			g.dispose();
			picture.addSpray(mode, color, x, y);
			return true;
		}
		if (mode == textMode)
		{
			removeCursor();
			picture.initSegments(mode, 0, fontToInt(font), color, x, y);
			text_x0 = x;
			text = "";
			text_x = x;
			text_y = y;
			drawCursor();
			return true;
		}
		x1 = Math.min(x, old_x);
		y1 = Math.min(y, old_y);
		w = Math.abs(x - old_x);
		h = Math.abs(y - old_y);
        Graphics g = getGraphics();
        g.setColor(color);
		switch (mode)
		{
			case rectangleMode:
				if (lineSize == 1)
					g.drawRect(x1, y1, w, h);
				else
					GraphicsExtension.drawThickRectangle(g, x1, y1, w, h, lineSize);
				break;
			case ovalMode:
				if (lineSize == 1)
					g.drawOval(x1, y1, w, h);
				else
					GraphicsExtension.drawThickOval(g, x1, y1, w, h, lineSize);
				break;
			case filledRectangleMode:
				g.setXORMode(XORColor);
				g.drawRect(x1, y1, w, h);
				g.setPaintMode();
				g.fillRect(x1, y1, w, h);
				break;
			case filledOvalMode:
				g.setXORMode(XORColor);
				g.drawOval(x1, y1, w, h);
				g.setPaintMode();
				g.fillOval(x1, y1, w, h);
				break;
		}
		g.dispose();
		picture.addItem(mode, lineSize, color, x1, y1, w, h);
		return true;
	}
	
	public boolean keyUp(Event event, int key)
	{
		if ((mode != textMode) || (! haveCursor))
			return false;
		if (key == 10)
		{
			removeCursor();
			return true;
		}
		if (key == 8)
		{
			if (! text.equals(""))
			{
				text = text.substring(0, text.length() - 1);
		        Graphics g = getGraphics();
				g.setFont(font);
				FontMetrics fm = g.getFontMetrics(font);
				text_x = text_x0 + fm.stringWidth(text);
				g.dispose();
				picture.removeChar();
				repaint();
			}
			return true;
		}
		if (picture.full())
			return true;
		if ((key < 0x20) || (key > 0x7E))
			return false;
		char[] c = new char[1];
		c[0] = (char) key;
		picture.addChar(c[0]);
		drawCursor();
        Graphics g = getGraphics();
		g.setFont(font);
		g.setColor(color);
		FontMetrics fm = g.getFontMetrics(font);
		g.drawChars(c, 0, 1, text_x, text_y);
		text_x += fm.charWidth(c[0]);
		g.dispose();
		drawCursor();
		text = text + String.valueOf(c[0]);
		return true;
	}

	public void move(Event event, int key)
	{
		int inc = 1;
		if ((event.modifiers & Event.SHIFT_MASK) != 0)
			inc = 10;
		if (key == Event.LEFT)
		{
			cleanup();
			picture.move(-inc, 0);
			repaint();
		}
		else if (key == Event.RIGHT)
		{
			cleanup();
			picture.move(inc, 0);
			repaint();
		}
		else if (key == Event.UP)
		{
			cleanup();
			picture.move(0, -inc);
			repaint();
		}
		else if (key == Event.DOWN)
		{
			cleanup();
			picture.move(0, inc);
			repaint();
		}
	}
}

class Picture implements ToolConstants
{
	final static int pictureSize = 10000;
	final static int safetySize = 100;

	private Applet paint;
	private byte picture[];
	private int noSegments;
	private int next, link;
	private static String filePosition = "0";

	public Picture(Applet paint, Color color)
	{
		this.paint = paint;
		clear(color);
	}

	private Font intToFont(int f)
	{
		String name;
		if ((f & 0x400) != 0)
			name = "SansSerif";
		else if ((f & 0x800) != 0)
			name = "Serif";
		else if ((f & 0x1000) != 0)
			name = "Monospaced";
		else
			name = "SansSerif";
		int style = 0;
		if ((f & 0x200) != 0)
			style |= Font.BOLD;
		if ((f & 0x100) != 0)
			style |= Font.ITALIC;
		int size = f & 0xFF;
		return new Font(name, style, size);
	}

	public void clear(Color color)
	{
		picture = new byte[pictureSize + safetySize];
		next = 0;
		addColor(color);
	}

	private void addByte(byte b)
	{
		picture[next++] = b;
	}

	private byte getByte()
	{
		return picture[next++];
	}

	private void addInt(int i)
	{
		picture[next++] = (byte) ((i >> 8) & 0xFF);
		picture[next++] = (byte) (i & 0xFF);
	}

	private int getInt()
	{
		int h = picture[next++];
		int l = picture[next++];
		return (h << 8) | (l & 0xFF);
	}

	private void addColor(Color color)
	{
		picture[next++] = (byte) (color.getRed() & 0xFF);
		picture[next++] = (byte) (color.getGreen() & 0xFF);
		picture[next++] = (byte) (color.getBlue() & 0xFF);
	}

	private Color getColor()
	{
		byte r = picture[next++];
		byte g = picture[next++];
		byte b = picture[next++];
		return new Color((r < 0) ? r + 256 : r, (g < 0) ? g + 256 : g, (b < 0) ? b + 256 : b);
	}

	private void addPosition(int x, int y)
	{
		addInt(x);
		addInt(y);
	}

	private void incNoSegments()
	{
		noSegments++;
		picture[link + 1] = (byte) ((noSegments >> 8) & 0xFF);
		picture[link + 2] = (byte) (noSegments & 0xFF);
	}

	private boolean decNoSegments()
	{
		if (noSegments == 0)
			return false;
		noSegments--;
		picture[link + 1] = (byte) ((noSegments >> 8) & 0xFF);
		picture[link + 2] = (byte) (noSegments & 0xFF);
		return true;
	}

	private int getInt(int p)
	{
		int h = picture[p];
		int l = picture[p + 1];
		return (h << 8) | (l & 0xFF);		
	}

	private void inc(int p, int v)
	{
		int h = picture[p];
		int l = picture[p + 1];
		v = ((h << 8) | (l & 0xFF)) + v;
		picture[p] = (byte) ((v >> 8) & 0xFF);
		picture[p + 1] = (byte) (v & 0xFF);
		return;		
	}

	private void cleanup()
	{
		if (next == 3)
			return;
		int save = next;
		next -= 2;
		next = getInt();
		int mode = getByte();
		if ((mode == freehandMode) || (mode == polygonMode) ||
			(mode == filledPolygonMode) || (mode == textMode))
		{
			if (getInt() == 0)
			{
				next -= 3;
				return;
			}
		}
		next = save;
	}

	public Color getBackgroundColor()
	{
		int p = 0;
		byte r = picture[p++];
		byte g = picture[p++];
		byte b = picture[p];
		return new Color((r < 0) ? r + 256 : r, (g < 0) ? g + 256 : g, (b < 0) ? b + 256 : b);
	}

	public boolean full()
	{
		if (next > pictureSize)
			return true;
		else
			return false;
	}

	public void initSegments(int mode, int noSegs, int arg, Color color, int x, int y)
	{
		cleanup();
		link = next;
		addByte((byte) mode);
		noSegments = noSegs;
		addInt(noSegments);
		addInt(arg);
		addColor(color);
		addPosition(x, y);
		addInt(link);
	}

	public void addSegment(int x, int y)
	{
		incNoSegments();
		next -= 2;
		addPosition(x, y);
		addInt(link);
	}

	public void addChar(char c)
	{
		incNoSegments();
		next -= 2;
		addByte((byte) c);
		addInt(link);
	}

	public void removeChar()
	{
		if (decNoSegments())
		{
			next -= 3;
			addInt(link);
		}
	}

	public void addItem(int mode, int lineSize, Color color, int x, int y, int w, int h)
	{
		cleanup();
		link = next;
		addByte((byte) mode);
		addInt(lineSize);
		addColor(color);
		addPosition(x, y);
		addPosition(w, h);
		addInt(link);
	}

	public void addSpray(int mode, Color color, int x, int y)
	{
		cleanup();
		link = next;
		addByte((byte) mode);
		addColor(color);
		addPosition(x, y);
		addInt(link);
	}

	public void addCurve(int mode, int lineSize, Color color, int x0, int y0, int x1, int y1,
															  int x2, int y2, int x3, int y3)
	{
		cleanup();
		link = next;
		addByte((byte) mode);
		addInt(lineSize);		
		addColor(color);
		addPosition(x0, y0);
		addPosition(x1, y1);
		addPosition(x2, y2);
		addPosition(x3, y3);
		addInt(link);
	}

	public void undo()
	{
		if (next > 3)
		{
			next -= 2;
			next = getInt();
		}		
	}

	public void move(int xInc, int yInc)
	{
		if (next <= 3)
			return;
		int p = getInt(next - 2);
		int mode = picture[p];
		switch (mode)
		{
			case lineMode:
				inc(p + 6, xInc);
				inc(p + 8, yInc);
				inc(p + 10, xInc);
				inc(p + 12, yInc);
				break;
			case rectangleMode:
			case ovalMode:
			case filledRectangleMode:
			case filledOvalMode:
				inc(p + 6, xInc);
				inc(p + 8, yInc);
				break;
			case freehandMode:
			case polygonMode:
			case filledPolygonMode:
				int n = getInt(p + 1);
				if (mode == freehandMode)
					n++;
				p += 8;
				while (n > 0)
				{
					inc(p, xInc);
					p += 2;
					inc(p, yInc);
					p += 2;
					n--;
				}
				break;
			case curveMode:
			case filledCurveMode:
				for (int i = 0; i < 4; i++)
				{
					inc(4 * i + p + 6, xInc);
					inc(4 * i + p + 8, yInc);
				}
				break;
			case lightSprayMode:
			case heavySprayMode:
				inc(p + 4, xInc);
				inc(p + 6, yInc);
				break;
			case textMode:
				inc(p + 8, xInc);
				inc(p + 10, yInc);
				break;				
		}
	}

	public void drawPicture(Graphics g)
	{
		int end = next;
		next = 3;
		while (next < end)
		{
			int mode = getByte();
			if (mode == freehandMode)
			{
				int noSegments = getInt();
				int lineSize = getInt();
				Color color = getColor();
				g.setColor(color);
				int x1 = getInt();
				int y1 = getInt();
				while (noSegments > 0)
				{
					int x2 = getInt();
					int y2 = getInt();
					if (lineSize == 1)
						g.drawLine(x1, y1, x2, y2);
					else
				        GraphicsExtension.drawThickLine(g, x1, y1, x2, y2, lineSize);
					x1 = x2;
					y1 = y2;
					noSegments--;
				}
				int link = getInt();
				continue;
			}
			if ((mode == polygonMode) || (mode == filledPolygonMode))
			{
				int noSegments = getInt();
				int lineSize = getInt();
				Color color = getColor();
				g.setColor(color);
				Polygon polygon = new Polygon();
				do
				{
					int x = getInt();
					int y = getInt();
					polygon.addPoint(x, y);
					noSegments--;
				}
				while (noSegments > 0);
				if (mode == polygonMode)
					if (lineSize == 1)
						g.drawPolygon(polygon);
					else
						GraphicsExtension.drawThickPolygon(g, polygon, lineSize);
				else
					g.fillPolygon(polygon);
				int link = getInt();
				continue;
			}
			if ((mode == curveMode) || (mode == filledCurveMode))
			{
				int lineSize = getInt();
				Color color = getColor();
				g.setColor(color);
				int x0 = getInt();
				int y0 = getInt();
				int x1 = getInt();
				int y1 = getInt();
				int x2 = getInt();
				int y2 = getInt();
				int x3 = getInt();
				int y3 = getInt();
				if (mode == curveMode)
					GraphicsExtension.drawCurve(g, x0, y0, x1, y1, x2, y2, x3, y3, lineSize);
				else
					GraphicsExtension.drawFilledCurve(g, x0, y0, x1, y1, x2, y2, x3, y3,
																				   lineSize);
				int link = getInt();
				continue;
			}
			if ((mode == lightSprayMode) || (mode == heavySprayMode))
			{
				Color color = getColor();
				g.setColor(color);
				int x = getInt();
				int y = getInt();
				for (int i = 0; i < 25; i++)
				{
					int radius = (int) (Math.random() * 15);
					int theta = (int) (Math.random() * 360);
					int sx = x + (int) (radius * Math.sin(theta));
					int sy = y + (int) (radius * Math.cos(theta));
					if (mode == lightSprayMode)
						g.drawLine(sx, sy, sx, sy);
					else
						g.drawOval(sx, sy, 1, 1);
				}
				int link = getInt();
				continue;
			}
			if (mode == textMode)
			{
				int noSegments = getInt();
				Font font = intToFont(getInt());
				g.setFont(font);
				FontMetrics fm = g.getFontMetrics(font);
				Color color = getColor();
				g.setColor(color);
				int x = getInt();
				int y = getInt();
				char[] c = new char[1];
				while (noSegments > 0)
				{
					c[0] = (char) getByte();
					g.drawChars(c, 0, 1, x, y);
					x += fm.charWidth(c[0]);
					noSegments--;
				}
				int link = getInt();
				continue;
			}
			int lineSize = getInt();
			Color color = getColor();
			g.setColor(color);
			int x = getInt();
			int y = getInt();
			int w = getInt();
			int h = getInt();
			int link = getInt();
			g.setColor(color);
			switch (mode)
			{
				case lineMode:
					if (lineSize == 1)
						g.drawLine(x, y, w, h);
					else
			    	    GraphicsExtension.drawThickLine(g, x, y, w, h, lineSize);
					break;
				case rectangleMode:
					if (lineSize == 1)
						g.drawRect(x, y, w, h);
					else
						GraphicsExtension.drawThickRectangle(g, x, y, w, h, lineSize);
					break;
				case ovalMode:
					if (lineSize == 1)
						g.drawOval(x, y, w, h);
					else
						GraphicsExtension.drawThickOval(g, x, y, w, h, lineSize);
					break;
				case filledRectangleMode:
					g.fillRect(x, y, w, h);
					break;
				case filledOvalMode:
					g.fillOval(x, y, w, h);
			}
		}
		next = end;
	}

	public String writePicture()
	{
		cleanup();
		String params = paint.getParameter("UserName") + ";" +
						paint.getParameter("Directory") + ";" +
						paint.getParameter("WriteFile") + ";" +
						paint.getParameter("Mail") + ";" +
						paint.getParameter("Message") + "\n";
		Socket s = null;
		String status;
		try
		{
			s = new Socket(paint.getParameter("Server"), 80);
			DataInputStream in = new DataInputStream(s.getInputStream());
			DataOutputStream out = new DataOutputStream(s.getOutputStream());
			out.writeBytes("POST /cgi-bin/bprentice/WritePicture HTTP/1.0\n");
			out.writeBytes("Content-type: text/plain\n");
			out.writeBytes("Content-length: " + (params.length() + next) + "\n\n");
			out.writeBytes(params);
			for (int i = 0; i < next; i++)
				out.writeByte(picture[i]);
			String line;
			do
				line = in.readLine();
			while (! line.equals(""));
			line = in.readLine();
			if (line.startsWith("Error:"))
				status = line;
			else
				status = "picture written (" + next + " bytes)";
		}
		catch (Exception e1)
		{
			status = "Error writing picture: " + e1;
		}
		finally
		{
			try
			{
				if (s != null)
					s.close();
			}
			catch (Exception e2)
			{
			}
		}
		return status;
	}

	public String readPicture()
	{
		Socket s = null;
		String params = paint.getParameter("UserName") + ";" +
						paint.getParameter("Directory") + ";" +
						paint.getParameter("ReadFile") + "\n";
		String status;
		try
		{
			s = new Socket(paint.getParameter("Server"), 80);
			DataInputStream in = new DataInputStream(s.getInputStream());
			DataOutputStream out = new DataOutputStream(s.getOutputStream());
			out.writeBytes("POST /cgi-bin/bprentice/ReadPicture HTTP/1.0\n");
			out.writeBytes("Content-type: text/plain\n");
			out.writeBytes("Content-length: " +
										(params.length() + filePosition.length()) + "\n\n");
			out.writeBytes(params);
			out.writeBytes(filePosition);
			String line;
			do
			{
				line = in.readLine();
				if (line.startsWith("Content-length:"))
					next = (new Integer(line.substring(16, line.length())).intValue()) - 8;
			}
			while (! line.equals(""));
			StringBuffer sb = new StringBuffer(8);
			for (int i = 0; i < 8; i++)
					sb.append((char) in.readByte());
			String m = new String(sb);
			if (m.startsWith("Error:"))
				status = m + in.readLine();				
			else
			{
				filePosition = m;
				for (int i = 0; i < next; i++)
					picture[i] = (byte) in.readByte();
				status = "picture read (" + next + " bytes)";
			}
		}
		catch (Exception e1)
		{
			status = "Error reading picture: " + e1;
		}
		finally
		{
			try
			{
				if (s != null)
					s.close();
			}
			catch (Exception e2)
			{
			}
		}
		return status;
	}
}

