import java.util.List;
import java.util.LinkedList;

public class ProducerConsumer2 {
    public static void main(String[] args) {
	SynchronizedQueue queue = new SynchronizedQueue(5);
// 	new Producer(queue, "p1", 0, 100, 500).start();
// 	new Producer(queue, "p2", 10, 100, 500).start();
// 	new Consumer(queue, "c1", 30, 100, 500).start();
// 	new Consumer(queue, "c2", 40, 100, 500).start();
// 	new Producer(queue, "p1", 0, 100, 500).start();
// 	new Consumer(queue, "c1", 30, 100, 500).start();
// 	new Consumer(queue, "c2", 40, 100, 500).start();
// 	new Consumer(queue, "c3", 50, 100, 500).start();
// 	new Consumer(queue, "c4", 60, 100, 500).start();
 	Producer p = new Producer("p", queue, 10, 10, 500); 
	System.err.println("Active count of " + p.getThreadGroup().getName() 
			   + "  is " + p.getThreadGroup().activeCount());
	Consumer c = new Consumer("c", queue, 30, 10, 500);
	
	System.err.println("Active count of " + c.getThreadGroup().getName() 
			   + "  is " + c.getThreadGroup().activeCount());
	p.start();
	c.start();
}

class SynchronizedQueue {
    protected List content;
    protected int maxSize;
    protected int imbalance;
    
    SynchronizedQueue(int maxSize) {
	this.maxSize = maxSize;
	content = new LinkedList();
	imbalance = 0;
    }

    synchronized public boolean isFull() {
	return content.size() == maxSize;
    }    

    synchronized public boolean isEmpty() {
	return content.isEmpty();
    }    

    synchronized public boolean put(Object o) {
	try {
	    while (isFull()) {
		System.err.println(Thread.currentThread() + " waiting to put");
		wait();
	    } 
	} catch (InterruptedException e) {
	    System.err.println("Interrupted?");
	}
	content.add(o);
	notify();
	return true;
    }

//     synchronized public boolean put(Object o) {
// 	if (isFull()) {
// 	    return false;
// 	} else {
// 	    content.add(o);
// 	    return true;
// 	}
//     }

    synchronized public Object get() {
	try {
	    while (isEmpty()) {
		System.err.println(Thread.currentThread() + " waiting to get");
		wait();
	    } 
	} catch (InterruptedException e) {
	    System.err.println("Interrupted?");
	}
	Object result = content.remove(0);
	notify();
	return result;
    }

//     synchronized public Object get() {
// 	if (isEmpty()) {
// 	    return null;
// 	} else {
// 	    return content.remove(0);
// 	}
//     }

}

class Agent extends Thread {
    protected SynchronizedQueue queue;
    protected int count;
    protected int indent;
    protected int wait;

    public Agent(ThreadGroup group, String name,
		 SynchronizedQueue queue, int indent, int count, int wait) {
	super(group, name);
	this.queue = queue;
	this.indent = indent;
	this.count = count;
	this.wait = wait;
    }

    public String toString() {
	String result = "";
	for (int i = 0; i < indent; i++) {
	    result += " ";
	}
	return result + getName();
    }
}

class Producer extends Agent {
    public static ThreadGroup producers = new ThreadGroup("Producers");

    public Producer(String name, SynchronizedQueue queue, int indent, int count,
		    int wait) {
	super(producers, name, queue, indent, count, wait);
    }

    public void run() {
	System.err.println("Active count of " + getThreadGroup().getName() 
			   + "  is " + activeCount());
	for (int i = 0; i < count; i++) {
	    String message = getName() + "(" + i + ")";
	    if (queue.put(message)) {
		System.err.println(this + ": put " + message + " succeeded");
	    } else {
		System.err.println(this + ": put " + message + " failed");
	    }
	    try {
		sleep((int)(Math.random()*wait));
	    } catch (InterruptedException e) {
	    }
	}
    }
}

class Consumer extends Agent {
    public static ThreadGroup consumers = new ThreadGroup("Consumers");

    public Consumer(String name, SynchronizedQueue queue, int indent, int count,
		    int wait) {
	super(consumers, name, queue, indent, count, wait);
    }

    public void run() {
	System.err.println("Active count of " + getThreadGroup().getName() 
			   + "  is " + activeCount());
	for (int i = 0; i < count; i++) {
	    Object o = queue.get();
	    if (o == null) {
		System.err.println(this + ": get failed");;
	    } else {
		System.err.println(this + ": get succeeded, got " + o);
	    }
	    try {
		sleep((int)(Math.random()*wait));
	    } catch (InterruptedException e) {
	    }
	}
    }
}
