class ProducerConsumerDemo {

	public static void main(String args[]) {
	
	  BoundedBuffer buffer = new BoundedBuffer ( 3 );
	  Producer p1 = new Producer ( 20 , buffer) ;
	  Producer p2 = new Producer ( 20 , buffer) ;
	  Consumer c1 = new Consumer ( 20 , buffer) ;
	  Consumer c2 = new Consumer ( 20 , buffer) ;
	  
	 p1.start();
	 p2.start();
	 c1.start();
	 c2.start();
	 
	}
}

class Item {
  private int unique_id = 0;
  private static int num = 0;
  
  Item() {
  unique_id = ++num;
  }

  public String toString() {
  String ret = null;
  ret = "Item #" + unique_id;
  return ret;
  }
  
}

class BoundedBuffer {

  private final Object[] buffer;
  private final int size;
  private int in = 0;
  private int out = 0;
  private int count = 0;
  
  private final Object Clock = new Object();
  private final Object Plock = new Object();
  
   public BoundedBuffer (int load) {
    size = load + 1;
	buffer = (Object[]) new Object[ size ];
   }

   public int insert ( Object item, Producer p ) {
   
    synchronized ( Plock ) {
      if( count >= (size - 1 ) ) {
	   System.out.println( p.toString() + " waiting to insert " + item.toString());
	   return 0;
/*	   try{
	    while(count >= (size - 1) ) {
	     Plock.notify();
	     Plock.wait();
	    }
	   }
	   catch(InterruptedException e){
	   }*/
	  }
      buffer[ in ] = item;
	  in = (in + 1) % buffer.length;
	  
	  synchronized ( this ) {
	   ++count;
	   System.out.printf( "%s inserted %s\t(%d of %d slots full)%n", p, item, count, (size - 1));
      }
	  if ( p.quotahasbeenmet) {
//     System.out.println("check1");
//	  Plock.notify();
	  }
	  
	}
	return 1;
   }
   

   
   public Object remove ( Consumer c) {
    Object pulled = null;
    synchronized ( Clock ) {
      if( count <= 0 ) {
	   System.out.println( c.toString() + " waiting to remove an item");
	   return pulled;
	   /*try {
	    while(count <=0 ) {
	    Clock.notify();
	    Clock.wait();
	    }
	   }
	   catch(InterruptedException f){
	   }*/
	  }
	  
	  pulled = buffer [ out ] ;
	  buffer [ out ] = null;
	  out = (out + 1) % buffer.length;
	  
	   synchronized ( this ) {
	    --count;
		System.out.printf( "%s removed %s\t(%d of %d slots full)%n", c, pulled, count, (size - 1));
	   }
	   if ( c.quotahasbeenmet ) {
//	   System.out.println("check2");
//	   Clock.notify();
	   }
    }
	return pulled;
	
  }
   
}

class Producer extends Thread {

 private int unique_id = 0;
 private static int num = 0;
 private int quota = 0;
 private BoundedBuffer inserthere;
 public boolean quotahasbeenmet = false;
 
// public Producer() {
//	unique_id = ++num;
//	quota = 1;
//	
//  }
 
  public Producer(int make, BoundedBuffer buffer) {
   unique_id = ++num;
   quota = make;
   inserthere = buffer;
   }

  public void run() {
   int i = 1;
   int returned = 0;
   while(!quotahasbeenmet) {
     returned = 0;
     Item newguy = new Item();
	 if ( i == quota) {
	 quotahasbeenmet = true;
	 }
	 while (returned == 0) {
	 returned = inserthere.insert( newguy , this );
	 i += returned;
	 }
   }
   System.out.println( this.toString() + " FINISHED producing " + quota + " items");
  }
   
  public String toString() {
    String ret = null;
	ret = "Producer" + unique_id;
	return ret;
  }
   
}

class Consumer extends Thread {

 private int unique_id = 0;
 private static int num = 0;
 private int quota = 0;
 private BoundedBuffer pullhere;
 public boolean quotahasbeenmet = false;
 
//  public Consumer () {
//	unique_id = ++num;
//	quota = 1;
//  }
 
  public Consumer (int consume, BoundedBuffer buffer) {
   unique_id = ++num;
   quota = consume;
   pullhere = buffer;
   }

  public void run() {
    int j = 0;
	while (!quotahasbeenmet) {
	  Object eatthis = null;
	  j++;
	  if (j == quota) {
	  quotahasbeenmet = true;
	  }
	  while ( eatthis == null) {
	  eatthis = pullhere.remove(this);
	  }
	  }
	System.out.println( this.toString() + " has finished consuming " + quota + " items");
  }  
  
  public String toString() {
    String ret = null;
	ret = "Consumer" + unique_id;
	return ret;
  }
   
}