import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.util.Random;

public class FlyBy extends java.applet.Applet implements Runnable {

	Thread mainThread;
	Image test_image;
	int x = 0;
	int y = 0;
	Image buffer_image;
	Graphics offscreen;
	// image_node test_node;
	// image_node test_node2;
	int screen_x, screen_y;
	int number_nodes = 30;
	image_node image_nodes[];
	Random num_gen = new Random(); // random number generator;
	boolean threadSuspended = false; // to allow suspension of the program

	public void init() {
		screen_x = 400;
		screen_y = 300;
		int i;
		resize(screen_x + 10, screen_y + 10);
		test_image = getImage(getCodeBase(), "8ball.gif");
		// test_node = new image_node(150,150,5.0,test_image);
		// test_node2 = new image_node(200,360,3.0,test_image);
		// test_node.set_display_size(screen_x,screen_y);
		// test_node2.set_display_size(screen_x,screen_y);
		image_nodes = new image_node[number_nodes];
		for (i = 0; i < number_nodes; i++) { // initialize all of the nodes
			image_nodes[i] = get_image_node();
			image_nodes[i].initialize((int) (num_gen.nextDouble() * screen_x),
					(int) (num_gen.nextDouble() * screen_y), num_gen
							.nextDouble() * 7, test_image);
			image_nodes[i].set_display_size(screen_x, screen_y);
		}

		buffer_image = createImage(screen_x, screen_y);
		offscreen = buffer_image.getGraphics();
	}

	public void sort_nodes() { // does a simple bubble sort, is not speed
								// critical
		int i, j;
		image_node temp;
		for (i = number_nodes - 1; i >= 0; i--) {
			for (j = 0; j < i; j++) {
				if (image_nodes[j].z_pos < image_nodes[j + 1].z_pos) {
					temp = image_nodes[j + 1];
					image_nodes[j + 1] = image_nodes[j];
					image_nodes[j] = temp;
				}
			}
		}
	}

	public image_node get_image_node() {
		image_node temp = new image_node();
		return temp;
	}

	public void start() {
		System.out.println("starting");
		if (mainThread == null) {
			mainThread = new Thread(this, "main");
			mainThread.start();
		}
	}

	public boolean mouseDown(java.awt.Event evt, int x, int y) {
		if (threadSuspended) { // allows stopping of the program
			mainThread.resume();
		} else {
			mainThread.suspend();
		}
		threadSuspended = !threadSuspended;
		return true;
	}

	public void run() {
		System.out.println("running");
		while (mainThread != null) {
			repaint();
			try {
				mainThread.sleep(10); // don't want to eat too many cycles
			} catch (InterruptedException e) {
			}
		}
	}

	public void paint(Graphics g) {

		update(g);
	}

	public void update(Graphics g) {
		int i;
		offscreen.setColor(Color.lightGray); // clear the background
		offscreen.fillRect(0, 0, screen_x, screen_y);
		for (i = 0; i < number_nodes; i++) { // move
			image_nodes[i].decrease_z(.25);
		}
		sort_nodes(); // z sort the nodes
		for (i = 0; i < number_nodes; i++) { // display the graphics and move
			image_nodes[i].display_frame(offscreen);
		}
		g.drawImage(buffer_image, 0, 0, null);
	}

	public void stop() {
		if (mainThread != null) {
			mainThread.stop();
			mainThread = null;
		}
		offscreen.finalize();
	}
}

class image_node { // a node class for the linked list of objects
	int x_pos; // x position
	int y_pos; // y position
	double z_pos; // z position
	int width; // actual width
	int height; // actual height
	Image frame;// image for the node
	Random num_gen = new Random(); // random number generator;
	int screen_x, screen_y;

	public image_node() { // do nothing constructor
	}

	public void initialize(int x, int y, double z, Image image) {
		x_pos = x;
		y_pos = y;
		z_pos = z;
		width = 51;
		height = 55;
		frame = image;
	}

	public void set_display_size(int x, int y) { // sets the background size
		screen_x = x;
		screen_y = y;
	}

	public void display_frame(Graphics g) { // to display the frame
		int temp_x, temp_y;
		int new_width, new_height;

		new_height = (int) (height / z_pos); // calculate new dimentions by z
												// loc
		new_width = (int) (width / z_pos);
		temp_x = x_pos - (int) (new_width / 2); // off set the frames
		temp_y = y_pos - (int) (new_height / 2);
		temp_x = (int) ((temp_x - (screen_x / 2)) / (1 - (1 / z_pos)))
				+ (int) (screen_x / 2);
		temp_y = (int) ((temp_y - (screen_y / 2)) / (1 - (1 / z_pos)))
				+ (int) (screen_y / 2);
		g.drawImage(frame, temp_x, temp_y, new_width, new_height, null);// draw
	}

	public void decrease_z(double amount) { // decreases the z value of the
											// image
		z_pos -= amount;
		if (z_pos < 1.5) {
			z_pos = 7.0;
			x_pos = (int) (num_gen.nextDouble() * (screen_x - width) + (width / 2));
			y_pos = (int) (num_gen.nextDouble() * (screen_y - height) + (height / 2));
			if (x_pos < 0)
				x_pos *= -1;
			if (y_pos < 0)
				y_pos *= -1;
			if (y_pos > screen_y)
				y_pos = 0;
			if (x_pos > screen_x)
				x_pos = 0;
		}
	}
}


