import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.Arrays;
import java.text.DecimalFormat;

/** Manages the overall view shown to the user, including the
 * first-person view (as computed using the View class), the
 * overview map, and the raw position information. */
public class Canvas extends JPanel
		implements ComponentListener, UserListener, Runnable {
	private User user;

	private Dimension size;
	private boolean pixels_filled;
	private int[] pixels;
	private MemoryImageSource producer;
	private Image img;
	private DecimalFormat form;

	public Canvas(User user) {
		this.user = user;
		this.size = new Dimension(600, 300);
		this.img = null;
		this.pixels_filled = false;
		this.form = new DecimalFormat("0.0");

		this.setBackground(Color.darkGray);
		this.setPreferredSize(size);
		this.addComponentListener(this);
		user.addUserListener(this);
		new Thread(this).start();
	}

	private synchronized void ensureImageExists() {
		if(img == null) {
			pixels = new int[size.height * size.width];
			producer = new MemoryImageSource(size.width, size.height,
				pixels, 0, size.width);
			producer.setAnimated(true);
			img = createImage(producer);
		}
	}

	public synchronized void userMoved(UserEvent e) {
		pixels_filled = false;
		notifyAll();
	}

	public void run() {
		while(true) {
			synchronized(this) {
				while(pixels_filled) {
					try { wait(); } catch(InterruptedException e) { }
				}
			}

			Dimension sz;
			User me;
			int[] pix;
			MemoryImageSource prod;
			synchronized(this) {
				ensureImageExists();
				sz = size;
				me = new User(user);
				pix = pixels;
				prod = producer;
			}
			View.fillPixels(me, pix, sz.width, sz.height);
			prod.newPixels();
			synchronized(this) {
				pixels_filled = true;
				notifyAll();
			}
		}
	}

	private synchronized void awaitPixelsFilled() {
		notifyAll();
		while(!pixels_filled) {
			try { wait(); } catch(InterruptedException e) { }
		}
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		ensureImageExists();
		g.drawImage(img, 0, 0, this);
		paintPosition(g, 2, size.height - 2);
		paintMap(g, size.width - 75, 2, 73, 50);
	}

	public void componentHidden(ComponentEvent e) { }
	public void componentMoved(ComponentEvent e) { }
	public void componentShown(ComponentEvent e) { }
	public synchronized void componentResized(ComponentEvent e) {
		Dimension newSize = getSize();
		if(!newSize.equals(size)) {
			size = newSize;
			img = null;
			pixels_filled = false;
			notifyAll();
		}
	}

	private void paintMap(Graphics g, int x, int y,
			int wid, int ht) {
		g.setColor(Color.black);
		g.fillRect(x, y, wid, ht);
		g.setColor(Color.darkGray);
		g.drawRect(x, y, wid, ht);

		World walls = user.getWorld();
		Bounds bds = walls.getBounds();
		double scale = Math.min(wid / bds.getWidth(),
			ht / bds.getHeight());
		double dx = x + (wid - scale * bds.getWidth()) / 2
			- scale * bds.getX();
		double dy = (y + ht) - (ht - scale * bds.getHeight()) / 2
			+ scale * bds.getY();

		for(Surface s : walls.getSurfaces()) {
			s.draw(g, dx, dy, scale, -scale);
		}

		g.setColor(Color.red);
		int ux = (int) (dx + scale * user.getX());
		int uy = (int) (dy - scale * user.getY());
		int udx = (int) (7 * Math.cos(user.getTheta()));
		int udy = (int) (7 * Math.sin(user.getTheta()));
		g.fillOval(ux - 2, uy - 2, 5, 5);
		g.drawLine(ux, uy, ux + udx, uy - udy);

	}

	private void paintPosition(Graphics g, int x, int y) {
		double degs = Math.toDegrees(user.getTheta());
		degs -= 360.0 * (int) (degs / 360.0);
		String pos = form.format(user.getX()) + ","
			+ form.format(user.getY()) + "/"
			+ form.format(degs);
		g.setColor(Color.white);
		g.drawString(pos, x, y);
	}

}
