/**
   The Monitor class contains the data shared by the Readers and Writers
   and the operations on the shared data.
  
   @author      Franck van Breugel
   @version     1.1    February 28, 2000
   @see Reader
   @see Writer
*/
public class Monitor 
{
    private int activeReaders;
    private int totalReaders;
    private int activeWriters;
    private int totalWriters;


    /**
       Initializes the instance variables of this monitor.
    */
    public Monitor()
    {
	activeReaders = 0;
	totalReaders = 0;
	activeWriters = 0;
	totalWriters = 0;
    }

    /**
       Prints out that the specified reader arrives and waits until the reader
       is allowed to start reading, prints that this reader starts reading, 
       sleeps for a random amount of time between 0 and DELAY miliseconds and 
       prints that this reader stops reading, prints out that the specified 
       reader departs and wakes up a waiting reader or writer if it is the \
       last active reader.

       @param number Number of the reader.
    */
    public synchronized void read(int number) 
    {
	synchronized(this)
	{
	    System.out.println("Reader " + number + " arrives.");
	    totalReaders++;
	    while (totalWriters != 0) 
	    {
		try 
		{
		    wait();
		} 
		catch (InterruptedException e) {}
	    }
	    activeReaders++;
	    System.out.println("Reader " + number + " starts reading.");
	}

        delay();

	synchronized(this)
	{
	    System.out.println("Reader " + number + " stops reading.");
	    activeReaders--;
	    totalReaders--;
	    System.out.println("Reader " + number + " departs.");
	    if (activeReaders == 0) 
	    {
		notifyAll();
	    }
	}
    }

    /**
       Prints out that the specified writer arrives and waits until the writer
       is allowed to start writing, prints that this writer starts writing, 
       sleeps for a random amount of time between 0 and DELAY miliseconds and 
       prints that this writer stops writing, prints out that the specified 
       writer departs and wakes up a waiting reader or writer.

       @param number Number of the writer.
    */
    public synchronized void write(int number) 
    {
       System.out.println("Writer " + number + " arrives.");
       totalWriters++;
       while (activeReaders + activeWriters != 0) 
       {
            try 
	    {
                wait();
            } 
	    catch (InterruptedException e) {}
        }
        activeWriters++;

        System.out.println("Writer " + number + " starts writing.");
        delay();
        System.out.println("Writer " + number + " stops writing.");

        activeWriters--;
        totalWriters--;
        System.out.println("Writer " + number + " departs.");
        notifyAll();
    }

    /** The maximum delay: 5000 miliseconds. */
    public static final int DELAY = 5000;

    /** Sleeps for a random amount of time between 0 and DELAY miliseconds. */
    private void delay() 
    {
        try 
	{
            Thread.currentThread().sleep((int) (Math.random() * DELAY));
        } 
	catch (InterruptedException e) {}
    }
}
