/* ****************************************************************************
 * IBM Confidential
 * OCO Source Materials
 * %ProductName%
 * © Copyright IBM Corp. 2003-2004
 *
 * The source code for this program is not published or 
 * otherwise divested of its trade secrets, irrespective of what 
 * has been deposited with the U.S. Copyright Office.
 * ****************************************************************************
 * Created on 27 April 2004
 */
package philosopher;


/**
 * Invite philosophers to a dinner, using command line with 
 * the number of philosopher to instantiate.
 * By default invite four philosophers.
 * 
 * MaitreDHote implements a simple deadlock detection using a 
 * system known as a "watchdog", called here "HeadWaiter".
 * If HeadWaiter detects that all philosophers wait for a fork up to 6 times,
 * it terminates the application abruptly (exit), only to provide to you traces.
 * 
 */
public class MaitreDHote 
{
  /** all philosophers who participate in the dinner are around the table*/
  private Philosopher philosophers[];
  /** there is a fork shared among all the philosophers */
  private Fork forks[];
  /** thread running the simple watchdog */
  private HeadWaiter head_waiter;
  
  public MaitreDHote( String []v )
  {
    head_waiter = new HeadWaiter();
  }
  
  /** 
   * HeadWaiter is a light watchdog system which keep an eye on the philosophers,
   * detecting when they are all locked on a resource (fork), 
   * or when they have finished dining.
   * If all participant are locked, HeadWaiter exits the application.
   * It's a cooperative system because it's based on state reported by 
   * the philosophers themselves.
   */
  private class HeadWaiter extends Thread 
  {
    public HeadWaiter()
    {
      setName( "HeadWaiter" );
    }
    
    public void run()
    {
      boolean all_locked=true;
      boolean all_finished=false;
      int all_locked_count=0;
      final int locked_limit=6;
      // loop until all philosphers have finished
      // or all are locked more than 6 times
      do
      {
         try{
           Thread.sleep(500);
         }
         catch( InterruptedException e )
         {
           e.printStackTrace();
         }
         all_locked=true;
         all_finished=true;
         for( int i=0; i<philosophers.length; i++ )
         {
           String state = philosophers[i].getState();
           if( state!=Philosopher.S_FINISHED )
           {
             all_finished = false;
           }
           if( state!=Philosopher.S_WAITING_LEFT_FORK 
            && state!=Philosopher.S_WAITING_RIGHT_FORK )
           {
             all_locked = false;
           }
         }         
         if( all_locked )
         {
           all_locked_count++;
         }         
      }
      while( !all_finished && (all_locked_count<locked_limit)  );
      if( all_finished )
      {
        System.out.println(getName()+" reports all philosophers have finished dining normally");        
      }
      if( all_locked )
      {
        System.out.println(getName()+": all philosophers are locked for "+locked_limit+" times.");
        System.out.println("exiting application.");
        System.exit(7);
      }
    }
  }
  
  /**
   * Parse the command line to invite philosophers to a dinner.
   * @return true if all philosopher have an invitation card, false otherwise.
   */
  public boolean invite( String v[] )
  {    
    int num_philosophers=4;
    if( v.length >= 1 )
    {
      try
      {
        num_philosophers = Integer.parseInt( v[0] );
        if( num_philosophers < 2 )
        {
          System.out.println("Warning: too few philosophers invited to your dinner, use 4 by default");
          num_philosophers = 4;
        }
      }
      catch( NumberFormatException e )
      {
        e.printStackTrace();
      }
    }
    
    //initialize...
    philosophers = new Philosopher[num_philosophers];
    for( int i=0; i<num_philosophers; i++ )
    {
      philosophers[i] = new Philosopher();
    }
    //create forks
    forks = new Fork[num_philosophers];
    for( int i=0; i<num_philosophers; i++ )
    {
      forks[i] = new Fork();
    }
    //associate fork to philosophers
    int j=philosophers.length-1;
    for( int i=0; i<philosophers.length; i++ )
    {
      philosophers[i].setLeftFork( forks[j] );
      philosophers[i].setRightFork( forks[i] );
      j=i;
    }
    return true;
  }
  
  /**
   * start the watchdog and philosopher dinner, this call return before the end
   * of all threads.
   */
  public void dine()
  {
    head_waiter.start();
    //start philosophers ...
    for( int i=0; i<philosophers.length; i++ )
    {
      philosophers[i].start();
    }
  }

  public static void main( String[]v )
  {
    if( v.length==0 )
    {
      System.out.println("Usage: <n>");
      System.out.println("here <n> is the number (>2) of philosopher invited to the diner");
      System.out.println("By default use four standard philosopher");
    }    
    MaitreDHote hote = new MaitreDHote( v );
    if ( hote.invite( v ) )
    {
      hote.dine();
    }
  }
}
