import java.applet.*;
import java.awt.*;
import java.awt.image.*;

public class GenArtApplet extends Applet
{
	private Image mask;
	private Image face;
	private Image masked_face;
	private Image buffer;

	private Rectangle[] rects = new Rectangle[9];
	private Image[] images = new Image[9];
	private QuaternionOperatorTree[] formulas = new QuaternionOperatorTree[9];

	private Dimension image_size = new Dimension(80, 80);

	public void init()
	{
		// System.err.println("Loading images...");
		showStatus("Loading images...");
		
		mask = getImage(getDocumentBase(), getParameter("mask"));
		face = getImage(getDocumentBase(), getParameter("face"));

		MediaTracker tracker = new MediaTracker(this);
		tracker.addImage(mask, 1);
		tracker.addImage(face, 2);
		try { tracker.waitForAll(); }
		catch (InterruptedException e) { System.err.println("Could not load images!"); }

		// System.err.println("Images loaded.");
		showStatus("Images loaded.");

		/*
		Frame mask_frame = new Frame();
		mask_frame.add(new ImageCanvas(mask));
		mask_frame.pack();
		mask_frame.show();

		Frame face_frame = new Frame();
		face_frame.add(new ImageCanvas(face));
		face_frame.pack();
		face_frame.show();
		*/

		// System.err.println("Filtering images...");
		showStatus("Filtering images...");

		AlphaMaskImageFilter alpha_filter = new AlphaMaskImageFilter(mask);
		ImageProducer producer = new FilteredImageSource(face.getSource(), alpha_filter);
		masked_face = createImage(producer);
		tracker.addImage(masked_face, 3);
		try { tracker.waitForAll(); } catch (InterruptedException e) {}

		// System.err.println("Images filtered.");
		showStatus("Images filtered.");

	/*
		Frame masked_face_frame = new Frame();
		masked_face_frame.add(new ImageCanvas(masked_face));
		masked_face_frame.pack();
		masked_face_frame.show();
	*/
	}

	public void start()
	{
		Dimension size = this.size();
		buffer = this.createImage(size.width, size.height);

		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				int index = i*3 + j;
				rects[index] = new Rectangle(20 + i*90, 20 + j*90, 80, 80);
				formulas[index] = new QuaternionOperatorTree(7);
			}
		}

		for (int i = 0; i < 9; i++)
			images[i] = createImage(new QuaternionImageProducer(formulas[i], image_size));
	}

	public void update(Graphics g)
	{
		// System.err.println("Updating...");
		// System.err.println("buffer = " + buffer);

		paint(buffer.getGraphics());
		g.drawImage(buffer, 0, 0, this);

		// System.err.println("Done updating.");
	}

	public void paint(Graphics g)
	{
		// System.err.println("Painting...");
		
		Dimension size = size();
		g.setColor(Color.black);
		g.fillRect(0, 0, size.width, size.height);

		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				int index = i*3 + j;
				int x = 20 + (90 * i);
				int y = 20 + (90 * j);
				g.drawImage(images[index], x, y, this);
			}
		}

		g.drawImage(masked_face, 0, 0, this);

		// System.err.println("Done painting");
	}

	public boolean mouseMove(Event e, int x, int y)
	{
		for (int i = 0; i < 9; i++)
		{
			if (rects[i].contains(x, y))
			{
				setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
				return true;
			}
		}

		setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		return true;
	}

	public boolean mouseUp(Event e, int x, int y)
	{
		for (int i = 0; i < 9; i++)
		{
			if (rects[i].contains(x, y))
			{
				formulas[4] = formulas[i];
				images[4] = images[i];
				repaint();

				for (int j = 0; j < 9; j++)
				{
					if (j != 4)
					{
						formulas[j] = (QuaternionOperatorTree) formulas[i].clone();
						formulas[j].mutate();
					}
				}
				
				for (int j = 0; j < 9; j++)
					if (j != 4)
						images[j] = createImage(new QuaternionImageProducer(formulas[j], image_size));
			}
		}
		return true;
	}
}

class ImageCanvas extends Canvas
{
	public ImageCanvas(Image init_image)
	{
		image = init_image;
		width = image.getWidth(this);
		height = image.getHeight(this);
	}

	public void update(Graphics g)
	{ paint(g); }

	public void paint(Graphics g)
	{
		g.setColor(Color.lightGray);
		g.fillRect(0, 0, width, height);

		g.setColor(Color.gray);
		for (int x = 0; x < width; x += CHECKER_SIZE)
			for (int y = 0; y < height; y += CHECKER_SIZE)
				if ((x+y) % (2*CHECKER_SIZE) == 0)
					g.fillRect(x, y, CHECKER_SIZE, CHECKER_SIZE);

		g.drawImage(image, 0, 0, this);
	}

	public Dimension minimumSize()
	{ return new Dimension(width, height); }

	public Dimension preferredSize()
	{ return new Dimension(width, height); }

	public Dimension maximumSize()
	{ return new Dimension(width, height); }

	private Image image;
	private int width;
	private int height;

	private int CHECKER_SIZE = 20;
}