import java.util.*;

public class Philosopher extends Observable implements Runnable {
    public static final int STATUS_DONE = 0;
    public static final int STATUS_THINKING = 1;
    public static final int STATUS_GRABBING = 2;
    public static final int STATUS_EATING = 3;

    String name;
    int status;
    Utensil sticks[];
    Object observerRef;
    int appetite;
    int eaten;
    Thread myThread;
    boolean abort = false;
    boolean eatfast = false;

    public Philosopher(String n, Utensil r, Utensil l, Object ref) {
	name = n;
	sticks = new Utensil[2];
	sticks[0] = l;
	sticks[1] = r;
	eaten = 0;
	appetite = rand(Dining.BASIC_APPETITE - 2, Dining.BASIC_APPETITE + 2);
	observerRef = ref;
	myThread = new Thread(this, n);
	status = STATUS_THINKING;
    }

    int rand(int min, int max) {
	return (min + (int)(Math.random() * (max - min)));
    }
    void sleepFor(int ms) {
	try { Thread.sleep(ms * (eatfast?1:6)); } 
	catch (InterruptedException e) { abort = true; }
    }
    public void setEatFast(boolean e) { eatfast = e; }

    public synchronized void start() {
	myThread.start();
    }
    public synchronized void stop() {
	abort = true;
	myThread.interrupt();
    }

    public void run() {
	message("arrived at table with appetite of " + appetite);
	int s1, s2;

	while(eaten < appetite && !abort) {
	    // think for a while..
	    status = STATUS_THINKING;
	    setChanged(); notifyObservers(observerRef);
	    message("thinking...");
	    sleepFor(rand(0,4) * Dining.BASIC_DELAY);
	    message("trying to eat...");
		
		
	    // okay, philosopher has decided to have a bite
	    status = STATUS_GRABBING;
	    boolean gotboth = false;
	    setChanged(); notifyObservers(observerRef);
	    while(!gotboth && !abort) {
		// decide on a stick to acquire first, at random
		s1 = rand(0,2);
		s2 = ((s1 == 0)?(1):(0));

		// Attempt to acquire first stick.
		synchronized (sticks[s1]) {
		    while(!(sticks[s1].isAvailable())) {
			try {
			    sticks[s1].wait();
			} catch (Exception e) { 
			    if (abort) return;
			}
			// okay, if we get here then we own
			// stick 1
		    }
		    // okay, first stick acquired
		    sticks[s1].pick_up();
		}

		// Attempt to acquire second stick, but give up
		// immediately if we can't get it.
		synchronized (sticks[s2]) {
		    if (sticks[s2].isAvailable()) {
			sticks[s2].pick_up();
			gotboth = true;
		    }
		}
		// If we didn't manage to get second stick,
		// then put first one down.
		if (!gotboth) {
		    sticks[s1].put_down();
		}
	    }
	    if (abort) return;

	    // If we get here, we've got both sticks, so we
	    // can eat for a while.
	    status = STATUS_EATING;
	    message("Got both chopsticks, EATING!");
	    setChanged(); notifyObservers(observerRef);
	    sleepFor(rand(2,5) * Dining.BASIC_DELAY);
	    eaten += 1;
	    if (abort) return;

	    // Okay, we're done eating for now, put both
	    // sticks down.
	    message("done eating for the moment...");
	    sticks[0].put_down();
	    sticks[1].put_down();
	    if (abort) return;
	}
	status = STATUS_DONE;
	setChanged(); notifyObservers(observerRef);
	message("all full, leaving the table.");
    }

    public void message(String s) {
	setChanged();
	notifyObservers(myThread.getName() + ": " + s);
    }

}




