//{{{}}}

//{{{  COMMENT documentation
//|
//| This program shows a use of the ALT mechanism including timeouts.  It is
//| loosely based on the Starving Philosophers example.  The Canteen now ALTs
//| between the Philosophers, the Cook and a timeout.  Each guard has to satisfy
//| a pre-condition.  Here is the College:
//|
//|
//|      0   1   2   3   4
//|      :)  :)  :)  :)  :)      ___________             ________
//|      |   |   |   |   |       |         |             |      |
//|    ---------------------<->--| Canteen |------<------| Cook |
//|       service/deliver        |_________|    supply   |______|
//|
//|
//|
//| The timeout is, of course, internal to the Canteen and doesn't show up
//| on this view of the system.
//|
//}}}

import java.io.DataInputStream;
import java.io.IOException;

// import occam.*

//{{{  class Canteen extends Thread {
class Canteen extends Thread {
  //{{{  
  
  //{{{  COMMENT documentation
  //
  //The Canteen is an active object -- a pure SERVER process for its `supply'
  //and `service'/`deliver' Channels, giving priority to the former.
  //
  //Philosphers eat chickens.  They queue up at the Canteen on its `service'
  //Channel.  They only get served when chickens are available -- otherwise,
  //they just have to wait.  Once they have got `service', they are dispensed
  //a chicken down the `deliver' Channel.
  //
  //The Chef cooks chickens.  When a batch ready is ready, she queues up at
  //the Canteen on its `supply' Channel.  The Canteen only accepts a new batch
  //when its current stock is below a maximum threshold.
  //
  //The Canteen staff get anxious when there are five of less chickens available
  //and complain every second this state persists.  If there are no chickens left
  //at all, they get really cross.
  //
  //So, the Canteen is implemented as a server loop, PRI ALTing against the
  //supply channel (from the Chef), the service channel (from the Philosphers)
  //and a one-second timeout (relative from the last event).  The service channel
  //is guarded by the availablity of chickens.  The supply Channel is guarded by
  //there not being too many chickens already.  The timeout is only set when the
  //chicken supply gets low.
  //
  //}}}
  
  //{{{  channels
  private Channel service;    // shared from all Philosphers (many-1)
  private Channel deliver;    // shared to all Philosphers (but only used 1-1)
  private Channel supply;     // from the Chef (1-1)
  //}}}
  
  //{{{  constructor
  public Canteen (Channel service, Channel deliver, Channel supply) {
    this.service = service;
    this.deliver = deliver;
    this.supply = supply;
    start ();
  }
  //}}}
  
  //{{{  run
  public void run () {
    try {
      //{{{  
      //{{{  alt channels and guards
      Alternative alt = new Alternative ();
      Channel[] c = {supply, service};
      boolean[] guard = {true, false};
      int supply_index = 0;                   // how do I declare this as a constant?
      int service_index = 1;                  // how do I declare this as a constant?
      //}}}
      //{{{  state
      int n_chickens = 0;
      int max_chickens = 12;                  // how do I declare this as a constant?
      int low_on_chickens = 5;                // how do I declare this as a constant?
      //}}}
      
      //{{{  starting
      System.out.println ("(" + System.currentTimeMillis() + ")" +
                          " Canteen : starting ... ");
      //}}}
      while (true) {
        guard[supply_index] = (n_chickens < max_chickens);
        guard[service_index] = (n_chickens > 0);
        switch (alt.select (c, guard, 1000, n_chickens <= low_on_chickens)) {
          //{{{  supply
          case 0: {                        // case supply_index: {
            int value;
            value = supply.read ();        // new batch of chickens from the Chef
            //{{{  bring in the tray
            System.out.println ("(" + System.currentTimeMillis() + ") " +
                                "Canteen : ouch ... make room ... this dish is very hot ... ");
            //}}}
            n_chickens += value;
            //{{{  announce arrival of more chickens
            System.out.println ("(" + System.currentTimeMillis() + ") " +
                                "Canteen : more chickens ... " +
                                n_chickens + " now available ... ");
            //}}}
            break;
          }
          //}}}
          //{{{  service
          case 1: {                        // case service_index: {
            int dummy;
            dummy = service.read ();       // Philosopher wants a chicken
            //{{{  thanks
            System.out.println ("(" + System.currentTimeMillis() + ") " +
                                "Canteen : one chicken coming down ... " +
                                (n_chickens - 1) + " left ... ");
            //}}}
            deliver.write (1);             // serve one chicken
            n_chickens--;
            break;
          }
          //}}}
          //{{{  timeout
          case 2: {                        // case timeout_index: {
            if (n_chickens > 0) {
              //{{{  moan
              System.out.println ("(" + System.currentTimeMillis() + ") " +
                                  "Canteen : where are those chickens ... only " +
                                  n_chickens + " left ... ");
              //}}}
            } else {
              //{{{  really moan
              System.out.println ("(" + System.currentTimeMillis() + ") " +
                                  "Canteen : where are those ?*!%@#! chickens ... " +
                                  "none left ... ");
              //}}}
            }
            break;
          }
          //}}}
        }
      }
      //}}}
    } catch (InterruptedException e) {}
  }
  //}}}
  
  //}}}
}
//}}}

//{{{  class Chef extends Thread {
class Chef extends Thread {
  //{{{  
  
  //{{{  COMMENT documentation
  //
  //The Chef is an active object.  She cooks chickens in batches, taking around 9
  //seconds per batch, and then sends them to the Canteen.  Although the production
  //of each batch is regular, the number of chickens in each batch varies.  Of
  //course, if the Canteen is full of chickens, she has to wait.
  //
  //}}}
  
  //{{{  channels
  private Channel supply;
  //}}}
  
  //{{{  constructor
  public Chef (Channel supply) {
    this.supply = supply;
    start ();
  }
  //}}}
  
  //{{{  run
  public void run () {
    try {
      //{{{  run
      //{{{  state
      int lo_chickens = 2;
      int hi_chickens = 17;
      int n_chickens = lo_chickens;
      int step = 3;
      boolean cook_more = true;
      //}}}
      //{{{  starting
      System.out.println ("(" + System.currentTimeMillis() + ")" +
                          " Chef    : starting ... ");
      //}}}
      while (true) {
        //{{{  cook n_chickens
        System.out.println ("(" + System.currentTimeMillis() + ")" +
                            " Chef    : cooking ... ");
        //{{{  cook for around 9 seconds
        try {sleep (9000);} catch (InterruptedException e) {}
        //}}}
        System.out.println ("(" + System.currentTimeMillis() + ")" +
                            " Chef    : " + n_chickens + " chickens, ready-to-go ... ");
        //}}}
        supply.write (n_chickens);            // supply the chickens
        //{{{  adjust n_chickens
        if (cook_more) {
          if (n_chickens < hi_chickens) {
            n_chickens += step;
          } else {
            cook_more = false;
            n_chickens -= step;
          }
        } else {
          if (n_chickens > lo_chickens) {
            n_chickens -= step;
          } else {
            cook_more = true;
            n_chickens += step;
          }
        }
        //}}}
      }
      //}}}
    } catch (InterruptedException e) {}
  }
  //}}}
  
  //}}}
}
//}}}

//{{{  class Phil extends Thread {
class Phil extends Thread {
  //{{{  
  
  //{{{  COMMENT documentation
  //
  //A Philosopher thinks for a while -- around 5 seconds -- and then goes to the
  //Canteen for food.  When she gets served, which may take a while if the Canteen
  //has run out of chickens, she consumes it instantly.  This cycle continues
  //indefinitely.
  //
  //}}}
  
  //{{{  parameters
  private int id;
  //}}}
  
  //{{{  channels
  private Channel service;
  private Channel deliver;
  //}}}
  
  //{{{  constructor
  public Phil (int id, Channel service, Channel deliver) {
    this.id = id;
    this.service = service;
    this.deliver = deliver;
    start ();
  }
  //}}}
  
  //{{{  run
  public void run () {
    try {
      //{{{  
      
      //{{{  starting
      System.out.println ("(" + System.currentTimeMillis() + ")" +
                          " Phil " + id + "  : starting ... ");
      //}}}
      while (true) {
        int chicken;
        //{{{  think for around 5 seconds
        try {sleep (5000);} catch (InterruptedException e) {}
        //}}}
        //{{{  want chicken
        System.out.println ("(" + System.currentTimeMillis() + ")" +
                            " Phil " + id + "  : gotta eat ... ");
        //}}}
        service.write (0);
        chicken = deliver.read ();
        //{{{  consume chicken
        System.out.println ("(" + System.currentTimeMillis() + ")" +
                            " Phil " + id + "  : mmm ... that's good ... ");
        //}}}
      }
      //}}}
    } catch (InterruptedException e) {}
  }
  //}}}
  
  //}}}
}
//}}}

//{{{  class College {
class College {
  //{{{  
  
  //{{{  COMMENT documentation
  //
  //The College consists of 5 Philosophers, a Chef and the Canteen.  All are
  //"active" objects.  The Canteen ALTs between a service Channel, shared by
  //all the Philosophers, a supply Channel from the Chef and a timeout.  Upon
  //acceptance of a service request, chickens are dispensed through a delivery
  //Channel.
  //
  //The Canteen guards the supply Channel, refusing acceptance of more chickens
  //when the number already in stock exceeds a certain level.
  //
  //The Canteen guards the service Channel so that Philosophers cannot blunder
  //in when there are no chickens, but are held waiting in the service queue.
  //
  //The Canteen guards the timeout so that it only gets set when there are a few
  //chickens left.  The effect is that the Canteen regularly complains when that
  //happens.
  //
  //The system timings are sufficiently chaotic that all these refusals can be
  //observed.
  //
  //}}}
  
  //{{{  main
  public static void main (String argv[]) {
    //{{{  
    
    int n_philosophers = 5;
    
    Channel service = new Channel ();
    Channel deliver = new Channel ();
    Channel supply = new Channel ();
    
    Canteen canteen = new Canteen (service, deliver, supply);
    
    Chef chef = new Chef (supply);
    
    Phil[] phil = new Phil[n_philosophers];
    
    for (int i = 0; i < n_philosophers; i++) {
      phil[i] = new Phil (i, service, deliver);
    }
    
    //}}}
  }
  //}}}
  
  //}}}
}
//}}}

