import java.awt.*;
import java.applet.Applet;
import java.util.Random;
import java.io.DataInputStream;

public class JPApplet extends Applet
{
	Pool			cars;
	Semaphore		gate;
	Pool			riders;
	Passenger[]		p;
	Jeep[]			j;
	GenericTRex		t;
	final Random 	r = new Random();
	Image 			offscreen;
	Image			background;

	// Universal image loader
	// Get an image, try finding through a JAR file first
	Image getImage(String f)
	{
		Image img = null;

		try
		{	// Attempt to access JAR file first
			DataInputStream in = new DataInputStream(getClass().getResourceAsStream(f));

			byte[] data=new byte[in.available()];
			in.readFully(data);
			in.close();
			img=Toolkit.getDefaultToolkit().createImage(data);
		}
		catch(Exception e)
		{	// Get the image normally
			img=getImage(getCodeBase(),f);
		}

		// Wait for image to finish loading or making
		MediaTracker mt = new MediaTracker(this);
		mt.addImage(img,0);
		try
		{
			mt.waitForID(0);
		}
		catch(InterruptedException e) {}

		return img;
	}	
	
	public void init()
	{
		cars = new Pool();
		gate = new Semaphore(1);
		riders = new Pool();
		p = new Passenger[10];	
		j = new Jeep[5];
		
		try
		{
			if ((Boolean.valueOf(getParameter("TRex")).booleanValue()))
				t = new TRex(this, r);
			else
				t = new CagedTRex(this, r);
		}
		catch (Exception e)
		{
			t = new CagedTRex(this, r); // default
		}

		System.err.println(getCodeBase());
		
		offscreen = createImage(200, 200);
		background = getImage("background.gif");
		
		for (int k = 0; k < 5; k++)
			cars.release(j[k] = new Jeep(this, 10 + k * 15, 190));
			
		for (int k = 0; k < 10; k++)
			(p[k] = new Passenger(this, r)).start();
			
		t.start();
	
		repaint();
	}
	
	public void update(Graphics g) // Override clearing behavior
	{
		paint(g);
	}
	
	public void paint(Graphics G)
	{
		Graphics g = offscreen.getGraphics();
		g.setClip(0, 0, 200, 200);
		
		
		g.drawImage(background, 0, 0, null);
		
		g.setColor(Color.darkGray);
		if (gate.permits() > 0)
			g.fillRect(98, 0, 4, 200);
		else
		{
			g.fillRect(98,   0, 4, 140);
			g.fillRect(98, 160, 4,  40);
		}
		
		for (int k = 0; k < 5; k++)
			j[k].paint(g);
		
		for (int k = 0; k < 10; k++)
			p[k].paint(g);
			
		t.paint(g);
			
		G.drawImage(offscreen, 0, 0, null);
	}
}

public class Jeep
{
	int x0, y0;
	int x, y;
	boolean parked;
	final Image myself;

	public Jeep(JPApplet jp, int X, int Y)
	{
		x0 = X;
		y0 = Y;
		parked = true;
		myself = jp.getImage("jeep.gif");
	}
	
	public boolean parked()
	{
		return parked;
	}
	
	public int homeX()
	{
		return x0;
	}
	
	public int homeY()
	{
		return y0;
	}
	
	public void park(boolean p)
	{
		parked = p;
	}
	
	public void drive(int X, int Y)
	{
		x = X;
		y = Y;
	}
	
	public void paint(Graphics g)
	{
		g.setColor(Color.red);
		if (parked)
			g.drawImage(myself, x0, y0, null);
		else
			g.drawImage(myself, x, y, null);
	}
}

public class Passenger extends Thread
{
	final JPApplet	park;
	boolean			eaten;
	int				x, y;
	Jeep			myJeep;
	final Random	r;
	final Image 	myself;

	public Passenger(JPApplet myPark, Random randomizer)
	{
		park = myPark;
		r = randomizer;
		eaten = false;
		x = Math.abs(r.nextInt() % 100);
		y = Math.abs(r.nextInt() % 100);
		switch (Math.abs(r.nextInt() % 5))
		{
			case 0:
				myself = park.getImage("yellow.gif");
				break;
			case 1:
				myself = park.getImage("pink.gif");
				break;
			case 2:
				myself = park.getImage("orange.gif");
				break;
			case 3:
				myself = park.getImage("magenta.gif");
				break;
			default:
				myself = park.getImage("white.gif");
				break;
		}
	}

	public void run()
	{
		try
		{		
			while (true)
			{
				WanderAroundMuseum();
				myJeep = (Jeep) park.cars.acquire();
				GetInJeep();
				park.gate.acquire();
				ExitLot();
				park.gate.release();
				park.riders.release(this);
				DriveAroundPark();
				if (park.riders.acquire(this) != this) // We've been eaten!
					return;
				park.gate.acquire();
				EnterLot();
				park.gate.release();
				park.cars.release(myJeep);
				myJeep = null;
			}
		}
		catch(InterruptedException e)
		{
			return;
		}
	}
	
	protected void WanderAroundMuseum() throws InterruptedException
	{
		x = 50 + (r.nextInt() % 45);
		y = 50 + (r.nextInt() % 45);
		park.repaint();
		while(r.nextFloat() < .95)
		{
			x += r.nextInt() % 3;
			if (x < 5)
				x = 5;
			if (x > 95)
				x = 95;
			y += r.nextInt() % 3;
			if (y < 5)
				y = 5;
			if (y > 95)
				y = 95;
		park.repaint();
			sleep(200);
		}
		x = 5 + Math.abs(r.nextInt() % 90);
		y = 150 + (r.nextInt() % 5);
		park.repaint();
	}
	
	protected void GetInJeep()
	{
		x = myJeep.homeX();
		y = myJeep.homeY();
		park.repaint();
	}
	
	protected void ExitLot() throws InterruptedException
	{
		myJeep.park(false);
		x = 97;
		y = 150;
		myJeep.drive(x, y);
		park.repaint();
		sleep(500);
	}
	
	protected void DriveAroundPark() throws InterruptedException
	{
		x = 110 + (r.nextInt() % 5);
		y = 150 + (r.nextInt() % 20);
		myJeep.drive(x, y);
		park.repaint();
		while(r.nextFloat() < .95)
		{
			if (eaten)
				return;
			x += r.nextInt() % 5;
			if (x < 105)
				x = 105;
			if (x > 195)
				x = 195;
			y += r.nextInt() % 5;
			if (y < 5)
				y = 5;
			if (y > 195)
				y = 195;
			myJeep.drive(x, y);
			park.repaint();
			sleep(200);
		}
		x = 110 + (r.nextInt() % 5);
		y = 150 + (r.nextInt() % 20);
		myJeep.drive(x, y);
		park.repaint();
	}
	
	protected void EnterLot() throws InterruptedException
	{
		x = 97;
		y = 150;
		myJeep.drive(x, y);
		park.repaint();
		sleep(500);
		myJeep.park(true);
	}
	
	public void paint(Graphics g)
	{
		if (!eaten)
			g.drawImage(myself, x, y, null);
	}	
	
	public void Eat()
	{
		eaten = true;
	}	
}

abstract public class GenericTRex extends Thread
{
	abstract public void paint(Graphics g);
}

public class TRex extends GenericTRex
{
	final JPApplet	park;
	int				x, y;
	final Random	r;
	final Image		myself;
	
	public TRex(JPApplet myPark, Random randomizer)
	{
		park = myPark;
		r = randomizer;
		x = 150;
		y = 100;		
		myself = park.getImage("tRex.gif");
	}

	public void run()
	{
		try
		{
			while (true)
			{
				WanderAroundPark();
				Passenger lunch = (Passenger) park.riders.acquire();
				lunch.Eat();
			}
		}
		catch(InterruptedException e)
		{
			return;
		}
	}
	
	protected void WanderAroundPark() throws InterruptedException
	{
		while(r.nextFloat() < .95)
		{
			x += r.nextInt() % 5;
			if (x < 105)
				x = 105;
			if (x > 180)
				x = 180;
			y += r.nextInt() % 5;
			if (y < 5)
				y = 5;
			if (y > 180)
				y = 180;
			park.repaint();
			sleep(300);
		}
	}
	
	public void paint(Graphics g)
	{
		g.drawImage(myself, x, y, null);
	}
}

public class CagedTRex extends GenericTRex
{
	final JPApplet	park;
	int				x, y;
	final Random	r;
	final Image		myself;

	public CagedTRex(JPApplet myPark, Random randomizer)
	{
		park = myPark;
		r = randomizer;
		x = 150;
		y = 75;
		myself = park.getImage("tRex.gif");
	}

	public void run()
	{
		try
		{
			while (true)
			{
				WanderAroundPark();
			}
		}
		catch(InterruptedException e)
		{
			return;
		}
	}
	
	protected void WanderAroundPark() throws InterruptedException
	{
		while(r.nextFloat() < .95)
		{
			x += r.nextInt() % 5;
			if (x < 125)
				x = 125;
			if (x > 160)
				x = 160;
			y += r.nextInt() % 5;
			if (y < 50)
				y = 50;
			if (y > 75)
				y = 75;
			park.repaint();
			sleep(300);
		}
	}
	
	public void paint(Graphics g)
	{
		g.setColor(Color.black);
		g.drawRect(125, 50, 50, 50);
		
		g.drawImage(myself, x, y, null);
	}
}