// Fig. 13.6: SharedCell.java
// Show multiple threads modifying shared object.
// Use synchronization to ensure that both threads 
// access the shared cell properly.
import java.applet.Applet;
import java.awt.*;
import java.text.DecimalFormat;

public class SharedCell extends Applet {
   private TextArea output;

   public void init()
   {
      setLayout( new BorderLayout() );
      output = new TextArea();
      add( output, BorderLayout.CENTER );
   }

   public void start()
   {
      HoldInteger h = new HoldInteger( output );
      ProduceInteger p = new ProduceInteger( h );
      ConsumeInteger c = new ConsumeInteger( h );

      p.start();
      c.start();
   }
}

class ProduceInteger extends Thread {
   private HoldInteger pHold;

   public ProduceInteger( HoldInteger h )
   {
      pHold = h;
   }

   public void run()
   {
      for ( int count = 0; count < 10; count++ ) {
         // sleep for a random interval
         try {
            Thread.sleep( (int) ( Math.random() * 500 ) );
         }
         catch( InterruptedException e ) {
            System.err.println( e.toString() );
         }

         pHold.setSharedInt( count );
         System.out.println( "Produced set sharedInt to " +
                             count );
      }

      pHold.setMoreData( false );
   }
}

class ConsumeInteger extends Thread {
   private HoldInteger cHold;

   public ConsumeInteger( HoldInteger h )
   {
      cHold = h;
   }

   public void run()
   {
      int val;

      while ( cHold.hasMoreData() ) {
         // sleep for a random interval
         try {
            Thread.sleep( (int) ( Math.random() * 3000 ) );
         }
         catch( InterruptedException e ) {
            System.err.println( e.toString() );
         }

         val = cHold.getSharedInt();
         System.out.println( "Consumer retrieved " + val );
      } 
   }
}

class HoldInteger {
   private int sharedInt[] = { -1, -1, -1, -1, -1 };
   private boolean moreData = true;
   private boolean writeable = true;
   private boolean readable = false;
   private int readLoc = 0, writeLoc = 0;
   private TextArea output;

   public HoldInteger( TextArea out )
   {
      output = out;
   }

   public synchronized void setSharedInt( int val )
   {
      while ( !writeable ) {
         try {
            output.append( " WAITING TO PRODUCE " + val );
            wait();
         }
         catch ( InterruptedException e ) {
            System.err.println( e.toString() );
         }
      }

      sharedInt[ writeLoc ] = val;
      readable = true;

      output.append( "\nProduced " + val +
                         " into cell " + writeLoc );

      writeLoc = ( writeLoc + 1 ) % 5;

      output.append( "\twrite " + writeLoc +
                     "\tread " + readLoc );
      printBuffer( output, sharedInt );

      if ( writeLoc == readLoc ) {
         writeable = false;
         output.append( "\nBUFFER FULL" );
      }

      notify();
   }

   public synchronized int getSharedInt()
   {
      int val;

      while ( !readable ) {
         try {
            output.append( " WAITING TO CONSUME" );
            wait();
         }
         catch ( InterruptedException e ) {
            System.err.println( e.toString() );
         }
      }

      writeable = true;
      val = sharedInt[ readLoc ];

      output.append( "\nConsumed " + val +
                         " from cell " + readLoc );

      readLoc = ( readLoc + 1 ) % 5;

      output.append( "\twrite " + writeLoc +
                     "\tread " + readLoc );
      printBuffer( output, sharedInt );

      if ( readLoc == writeLoc ) {
         readable = false;
         output.append( "\nBUFFER EMPTY" );
      }

      notify();
      return val;
   }

   public void printBuffer( TextArea out, int buf[] )
   {
      DecimalFormat threeChars = new DecimalFormat( " #;-#" );
      output.append( "\tbuffer: " );

      for ( int i = 0; i < buf.length; i++ )
         out.append( " " + threeChars.format( buf[ i ] ) );
   }

   public void setMoreData( boolean b ) { moreData = b; }

   public boolean hasMoreData()
   {
      if ( moreData == false && readLoc == writeLoc )
         return false;
      else
         return true;
   }
}


