//{{{}}}

// package occam;

//{{{  public class Alternative {
public class Alternative {

  //{{{  COMMENT documentation
  //
  //This mechanism is based upon the data structures and algorithms implemented
  //by micro-code in the transputer.  It is exactly the same algorithm as used
  //by the KRoC kernel and by the SPoC translator.
  //
  //An Alternative object is part of the ALTing process and records the status
  //of the Alt.
  //
  //{{{  (PRI) ALT on a channel array (with optional pre-conditions)
  //
  //It's `select' method is called by the ALTing process and returns an index
  //for a ready Channel if-and-only-if one of the Channels becomes ready.  If
  //no Channel is ready, it waits (non-busilly) until one is.  The parameters
  //to `select' are an array of Channels over which to ALT (plus an optional
  //array of boolean pre-conditions, settable at run-time).  The `select'
  //implements a PRI ALT over the channel array, with highest priority going
  //to channel index zero.
  //
  //}}}
  //
  //{{{  (PRI) ALT on a channel array and a timeout (with optional pre-conditions)
  //
  //Versions of `select' also allow ALTing a channel array against a timeout
  //(with and without pre-conditions).  The timeout guard has lowest priority.
  //The timeout setting is relative to the time of ALTing and not an absolute
  //time (as in occam).  To get timeouts at an absolute time, read the time now
  //and compute the time remaining -- use the latter in the `select'.  Because
  //of the primitives provided by Java, there are two versions of each of these
  //timeout ALTs: one which sets the timeout in terms of milliseconds and one
  //in terms of milliseconds plus nanoseconds.
  //
  //The select method returns an value in the range 0 to the channel array
  //length inclusive.  If the value is a legal channel array index, then that
  //is the channel selected.  If the value is equal to the size of the channel
  //array (and is not, therefore, an array index), the timeout has occured.
  //
  //}}}
  //
  //{{{  (PRI) ALT on a channel array and SKIP (with optional pre-conditions)
  //
  //Finally, there are versions of `select' also to allow ALTing a channel
  //array against a SKIP guard (with a complusory pre-condition on the SKIP
  //and an optional set on the channels).  The SKIP guard has lowest priority,
  //which means these versions may be used to poll the channel array.
  //
  //The select method returns an value in the range 0 to the channel array
  //length inclusive.  If the value is a legal channel array index, then that
  //is the channel selected.  If the value is equal to the size of the channel
  //array (and is not, therefore, an array index), none of the channels were
  //ready.
  //
  //}}}
  //
  //This `select' method operates exactly like similar routines for ALT provided
  //in INMOS or 3L parallel C (e.g. `procAltList' or `alt_wait_vec' respectively).
  //As with those, it is the programmer's responsibility to ensure that the
  //Channel indicated by the selected index is actually used.
  //
  //An Alternative object is "passive", containing no threads of its own.
  //
  //For any Channel, there may only be one process ALTing on it, although there
  //may be many processes sharing the other end.
  //
  //The `schedule' method is not public and is called only from an enabled
  //Channel object (by the outputting process).
  //
  //}}}

  //{{{  state
  
  private static final int inactive = 0;    // whatever happened to
  private static final int enabling = 1;    // enumerated types?
  private static final int waiting = 2;
  private static final int ready = 3;
  
  private int state = inactive;
  
  //}}}

  //{{{  (PRI) ALT on a channel array
  
  public synchronized int select (Channel[] c)
    throws InterruptedException {
    //{{{  
    int selected;
    int i;
    
    state = enabling;                              // ALT START
    
    for (i = 0; i < c.length; i++) {
      if (c[i].enable(this)) {                     // ENABLE CHANNEL
        state = ready;
        break;
      }
    }
    
    if (state == enabling) {                       // ALT WAIT
      state = waiting;
      wait ();
    }
    
    // assert : state == ready
    
    selected = i;
    for (i--; i >= 0; i--) {
      if (c[i].disable()) {                        // DISABLE CHANNEL
        selected = i;
      }
    }
    
    state = inactive;
    return selected;                               // ALT END
    //}}}
  }
  
  //}}}
  //{{{  (PRI) ALT on a (condition & channel array)
  
  public synchronized int select (Channel[] c, boolean[] guard)
    throws InterruptedException {
    //{{{  
    int selected;
    int i;
    
    state = enabling;                              // ALT START
    
    for (i = 0; i < c.length; i++) {
      if (guard[i] && c[i].enable(this)) {         // ENABLE CHANNEL
        state = ready;
        break;
      }
    }
    
    if (state == enabling) {                       // ALT WAIT
      state = waiting;
      wait ();
    }
    
    // assert : state == ready
    
    selected = i;
    for (i--; i >= 0; i--) {
      if (guard[i] && c[i].disable()) {            // DISABLE CHANNEL
        selected = i;
      }
    }
    
    state = inactive;
    return selected;                               // ALT END
    //}}}
  }
  
  //}}}

  //{{{  (PRI) ALT on a channel array and a timeout
  
  public synchronized int select (Channel[] c, long msecs)
    throws InterruptedException {
    //{{{  
    int selected;
    int i;
    
    state = enabling;                              // ALT START
    
    for (i = 0; i < c.length; i++) {
      if (c[i].enable(this)) {                     // ENABLE CHANNEL
        state = ready;
        break;
      }
    }
    
    if (state == enabling) {                       // ALT WAIT
      state = waiting;
      if (msecs > 0) wait (msecs);
      state = ready;                               // in case we timed out ...
    }
    
    // assert : state == ready
    
    selected = i;
    for (i--; i >= 0; i--) {
      if (c[i].disable()) {                        // DISABLE CHANNEL
        selected = i;
      }
    }
    
    state = inactive;
    return selected;                               // ALT END
    //}}}
  }
  
  public synchronized int select (Channel[] c, long msecs, int nsecs)
    throws InterruptedException {
    //{{{  
    int selected;
    int i;
    
    state = enabling;                              // ALT START
    
    for (i = 0; i < c.length; i++) {
      if (c[i].enable(this)) {                     // ENABLE CHANNEL
        state = ready;
        break;
      }
    }
    
    if (state == enabling) {                       // ALT WAIT
      state = waiting;
      if (msecs > 0) wait (msecs, nsecs);
      state = ready;                               // in case we timed out ...
    }
    
    // assert : state == ready
    
    selected = i;
    for (i--; i >= 0; i--) {
      if (c[i].disable()) {                        // DISABLE CHANNEL
        selected = i;
      }
    }
    
    state = inactive;
    return selected;                               // ALT END
    //}}}
  }
  
  //}}}
  //{{{  (PRI) ALT on a (condition & channel array) and (condition & timeout)
  
  public synchronized int select (Channel[] c, boolean[] guard,
                                  long msecs, boolean t_guard)
    throws InterruptedException {
    //{{{  
    int selected;
    int i;
    
    state = enabling;                              // ALT START
    
    for (i = 0; i < c.length; i++) {
      if (guard[i] && c[i].enable(this)) {         // ENABLE CHANNEL
        state = ready;
        break;
      }
    }
    
    if (state == enabling) {                       // ALT WAIT
      state = waiting;
      if (t_guard) {
        if (msecs > 0) wait (msecs);
        state = ready;                             // in case we timed out ...
      } else {
        wait ();
      }
    }
    
    // assert : state == ready
    
    selected = i;
    for (i--; i >= 0; i--) {
      if (guard[i] && c[i].disable()) {            // DISABLE CHANNEL
        selected = i;
      }
    }
    
    state = inactive;
    return selected;                               // ALT END
    //}}}
  }
  
  public synchronized int select (Channel[] c, boolean[] guard,
                                  long msecs, int nsecs, boolean t_guard)
    throws InterruptedException {
    //{{{  
    int selected;
    int i;
    
    state = enabling;                              // ALT START
    
    for (i = 0; i < c.length; i++) {
      if (guard[i] && c[i].enable(this)) {         // ENABLE CHANNEL
        state = ready;
        break;
      }
    }
    
    if (state == enabling) {                       // ALT WAIT
      state = waiting;
      if (t_guard) {
        if (msecs > 0) wait (msecs, nsecs);
        state = ready;                             // in case we timed out ...
      } else {
        wait ();
      }
    }
    
    // assert : state == ready
    
    selected = i;
    for (i--; i >= 0; i--) {
      if (guard[i] && c[i].disable()) {            // DISABLE CHANNEL
        selected = i;
      }
    }
    
    state = inactive;
    return selected;                               // ALT END
    //}}}
  }
  
  //}}}

  //{{{  (PRI) ALT on a channel array and (condition & SKIP)
  
  public int select (Channel[] c, boolean skip)
    throws InterruptedException {
    //{{{  
    if (skip) {
      //{{{  
      int i;
      
      for (i = 0; i < c.length; i++) {
        if (! c[i].channel_empty) {
          break;
        }
      }
      
      return i;
      //}}}
    } else {
      return select (c);
    }
    //}}}
  }
  
  //}}}
  //{{{  (PRI) ALT on a (condition & channel array) and (condition & SKIP)
  
  public int select (Channel[] c, boolean[] guard, boolean skip)
    throws InterruptedException {
    //{{{  
    if (skip) {
      //{{{  
      int i;
      
      for (i = 0; i < c.length; i++) {
        if (guard[i] && (! c[i].channel_empty)) {
          break;
        }
      }
      
      return i;
      //}}}
    } else {
      return select (c, guard);
    }
    //}}}
  }
  
  //}}}

  //{{{  synchronized void schedule () {
  
  synchronized void schedule () {
    if (state == waiting) {
      state = ready;
      notify ();
    }
  }
  
  //}}}

}
//}}}

