import eduni.simjava.*;
import eduni.simanim.*;
import simdistalg.*;
class Bottom extends Sim_node implements Sim_faulty_node {
  private int ns = 1; // next item to send
  int i;              // auxillary variable
  static final int seed = 1;
  public static Sim_uniform_obj chooser =
    new Sim_uniform_obj("chooser", 1, 2, seed);

  // unanimated constructor
  public Bottom(String name, String[] out_nb, String[] in_nb) {
    super(name, out_nb, in_nb);
  }
  // animated constructor
  public Bottom(String name, String[] out_nb, String[] in_nb, int x, int y) {
    super(name, out_nb, in_nb, x, y);
    add_param(new Anim_param("privileged", Anim_param.STATE, "up", 0,0));
  }
  // behaviour
  public void guarded_commands() {
    sim_trace(1, "P up");
    set_timeout(Applet_tester.n*15);
    switch ((int)(Math.round(chooser.sample()))) {
      case 1:
        if (timeout()) {
          System.out.println("Bottom machine timed out");
          out("Machine1", new Integer(ns), "" + ns);
          reset_timeout();
        }
      break;
      case 2:
        if (in_guard("Machine" + (Applet_tester.n - 1))) {
          i = ((Integer) in("Machine" + (Applet_tester.n - 1))).intValue();
          if (i == ns) {
            System.out.println("Bottom machine in CRITICAL SECTION!!!");
            sim_trace(1, "P token");
            ns++;
            out("Machine1", new Integer(ns), "" + ns);
            reset_timeout();
          } else {
            // former case 3
            reset_timeout();
          }
        }
      break;
    }
  }

  public void perturb() {
    ns = (int) (Math.random()*256);
  }
  public void reset() {
    ns = 1;
  }
  public void down() {;}
  public void up() {;}
  public void byzantine() {;}
}
class Other extends Sim_node implements Sim_faulty_node {
  int id;     // own id
  int na = 0; // number of last acknowledged item
  int j;      // auxillary variable

  // refined unanimated constructor to pass id to node
  public Other(String name, int i, String[] out_nb, String[] in_nb) {
    super(name, out_nb, in_nb);
    id = i;
  }
  // refined animated constructor
  public Other(String name, int i, String[] out_nb,
      String[] in_nb, int x, int y) {
    super(name, out_nb, in_nb, x, y);
    id = i;
    add_param(new Anim_param("privileged", Anim_param.STATE, "up", 0,0));
  }

  public void guarded_commands() {
    sim_trace(1, "P up");
    if (in_guard("Machine" +
        Applet_tester.mod(id - 1, Applet_tester.n))) {
      j = ((Integer) in("Machine" +
          Applet_tester.mod(id - 1, Applet_tester.n))).intValue();
      if (j != na) {
        // first guarded command
        System.out.println("Machine" + id + " is in CRITICAL SECTION!!!");
        sim_trace(1, "P token");
        na = j;
      }
      out("Machine" + Applet_tester.mod(id + 1, Applet_tester.n),
          new Integer(na),  "" + na);
    }
  }

  public void perturb() {
      na = (int) (Math.random()*256);
  }
  public void reset() {
    na = 0;
  }
  public void down() {;}
  public void up() {;}
  public void byzantine() {;}
}
public class Applet_tester extends Simdistalg_applet {
  static final int n = 4;
  static final int pixel_offset = 30;
  static final int radius = 100;

  public void anim_layout() {
    super.anim_layout();
    double degree = 2*Math.PI / n; // angle between nodes

    for (int i=0; i<n; i++) {
      // place machine i in simulation (NB: machine0 is bottom machine)
      if (i==0) {
        Sim_system.add(new Bottom("Machine0",
            new String[] { "Machine1" },
            new String[] { "Machine" + (Applet_tester.n - 1) },
            pixel_offset + radius + (int) (radius * Math.sin(i*degree)),
            pixel_offset + radius - (int) (radius * Math.cos(i*degree))));
      } else {
        int id = i; // to be able to use chunk
        System.out.println( "Machine" + i + ", left=Machine" +
            Applet_tester.mod(id - 1, Applet_tester.n) +
            ", right=Machine" +
            Applet_tester.mod(id + 1, Applet_tester.n)  +
            ", placing at (" +
            (pixel_offset + radius + radius * Math.sin(i*degree)) +
            "," + (pixel_offset + radius - radius * Math.cos(i*degree)) +
            ")");
        Sim_system.add(new Other("Machine" + i, i,
            new String[] {
              "Machine" + Applet_tester.mod(id + 1, Applet_tester.n) },
            new String[] {
              "Machine" + Applet_tester.mod(id - 1, Applet_tester.n) },
            pixel_offset + radius + (int) (radius * Math.sin(i*degree)),
            pixel_offset + radius - (int) (radius * Math.cos(i*degree))));
      }
    }
    Sim_node.connect();
  }
  static int mod(int a, int b) {
    while (a<0) a+=b;
    return a%b;
  }
}

