/* Task Farm 
 * VRML version.
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.Vector;

import eduni.simanim.*;
import eduni.simjava.*;

import vrml.external.field.*;
import vrml.external.Node;
import vrml.external.Browser;
import vrml.external.exception.*;


// The worker
class Worker extends Sim_entity {
  private Sim_port in, out;
  private int state;
  private Sim_normal_obj delay;

  private static final int IDLE = 0;
  private static final int BUSY = 1;

  private Applet2 app;

  // --- Connect to VRML node.
  Node mynode = null;
  EventInSFInt32  numbeads = null;
  void connect3d() {
    mynode = app.getNode(get_name());
    numbeads = (EventInSFInt32) mynode.getEventIn("set_numbeads");
  }
  void update3d(int i) {
    if (numbeads != null) { numbeads.setValue(i); }    
  }

  public Worker(String name, double hold, int x, int y, Applet2 app) {
    super(name, "worker", x, y);
    this.app = app;
    in = new Sim_port("in", "port", Anim_port.TOP, 10); add_port(in);
    out = new Sim_port("out", "port", Anim_port.BOTTOM, 10); add_port(out);
    state = IDLE;
    add_param(new Anim_param("State", Anim_param.STATE, "idle"));
    delay = new Sim_normal_obj("", hold,hold/10.0, (int)System.currentTimeMillis());
  }

  public void body() {
    Sim_event ev = new Sim_event();
    double h;
    connect3d();
    dump_state();
    while(true) {
      sim_wait(ev);
      sim_hold(0.01);
      state = BUSY; dump_state();
      h = delay.sample();
      sim_hold(h>0?h:0.05); // Chuga Chuga Chuga
      state = IDLE; dump_state();
      sim_hold(0.05);
      sim_schedule(out, 0.0, 0);
      if(Applet2.show_msg) sim_trace(1, "S out R"); // Send on result
      sim_schedule(in, 0.0, 0);
      if(Applet2.show_msg) sim_trace(1, "S in F");  // Tell router we want more
    }
  }

  public void dump_state() {
    if(state == IDLE) {sim_trace(1, "P idle"); update3d(0);}
    if(state == BUSY) {sim_trace(1, "P busy"); update3d(1);}
  }
}

// The Work router
class Wrouter extends Sim_entity {
  private Sim_port worker, next, in;
  private int state;

  private static final int FORWARD = 0;
  private static final int COLLECT = 1;

  private Applet2 app;


  public Wrouter(String name, int x, int y, Applet2 app) {
    super(name, "wrouter", x, y);
    this.app = app;
    worker = new Sim_port("worker", "port", Anim_port.BOTTOM, 10); add_port(worker);
    next = new Sim_port("next", "port", Anim_port.RIGHT, 10); add_port(next);
    in = new Sim_port("in", "port", Anim_port.LEFT, 10); add_port(in);
    state = COLLECT;
    add_param(new Anim_param("State", Anim_param.STATE, "collect"));
  }

  // --- Connect to VRML node.
  Node mynode = null;
  EventInSFInt32 wrouting = null;
  void connect3d() {
    mynode = app.getNode(get_name());
    wrouting = (EventInSFInt32) mynode.getEventIn("set_routing");
  }
  void update3d(int i) {
    if (wrouting != null) { wrouting.setValue(i); }    
  }


  public void body() {
    Sim_event ev = new Sim_event();
    connect3d();
    while(true) {
      sim_get_next(ev);
      sim_hold(0.01);
      if(ev.get_tag() == 1) {
        if(state == COLLECT) {
          sim_schedule(worker, 0.0, 1);
          if(Applet2.show_msg) sim_trace(1, "S worker W");
          state = FORWARD; dump_state();
        } else {
          sim_schedule(next, 0.0, 1);
          if(Applet2.show_msg) sim_trace(1, "S next W");
        }
      } else {
        state = COLLECT; dump_state();
      }
    }
  }

  public void dump_state() {
    if(state == FORWARD) {sim_trace(1, "P forward"); update3d(0); }
    if(state == COLLECT) {sim_trace(1, "P collect"); update3d(1); }
  }
}

// The results router
class Rrouter extends Sim_entity {
  private Sim_port out, in1, in2;
  private Applet2 app;
  public Rrouter(String name, int x, int y, Applet2 app) {
    super(name, "rrouter", x, y);
    this.app = app;
    out = new Sim_port("out", "port", Anim_port.LEFT, 10);  add_port(out);
    in1 = new Sim_port("in1", "port", Anim_port.TOP, 10);  add_port(in1);
    in2 = new Sim_port("in2", "port", Anim_port.RIGHT, 10);  add_port(in2);
  }

  // --- Connect to VRML node.
  Node mynode = null;
  EventInSFInt32 routing = null;
  void connect3d() {
    mynode = app.getNode(get_name());
    routing = (EventInSFInt32) mynode.getEventIn("set_routing");
  }
  void update3d(int i) {
    if (routing != null) { routing.setValue(i); }    
  }

  public void body() {
    Sim_event ev = new Sim_event();
    connect3d();
    while(true) {
	update3d(0);
      sim_get_next(ev);
	update3d(1);
      sim_hold(0.3);
      sim_schedule(out, 0.0, 1);
      if(Applet2.show_msg) sim_trace(1, "S out R");
    }
  }
}


//the farmer
class Farmer extends Sim_entity {
  private Sim_port first;
  private Sim_negexp_obj delay;
  private int max;
  private Applet2 app;

  public Farmer(String name, int max, double hold, int x, int y, Applet2 app) {
    super(name, "farmer", x, y);
    this.app = app;
    first = new Sim_port("first", "port", Anim_port.RIGHT,10); add_port(first);
    delay = new Sim_negexp_obj("", hold, (int)System.currentTimeMillis());
    this.max = max;
  }

  // --- Connect to VRML node.
  Node mynode = null;
  EventInSFInt32 routing = null;
  void connect3d() {
    mynode = app.getNode(get_name());
    routing = (EventInSFInt32) mynode.getEventIn("set_isactive");
  }
  void update3d(int i) {
    if (routing != null) { routing.setValue(i); }    
  }


  public void body() {
    int i;
    double h;
    connect3d();
    for(i=0; i<max; i++) {
      sim_schedule(first, 0.0, 1);
      if(Applet2.show_msg) sim_trace(1, "S first W");   // Send work out
	update3d(1);
      h = delay.sample();
      sim_hold(h>0?h:0.01);    // wait a bit
	update3d(0);
	sim_hold(0.1);
    }
    sim_trace(1,"C Farmer done");
  }
}

// A bucket, simply keeps a count of how many messages it receives
class Bucket extends Sim_entity {
  int count = 0;
  Sim_port in;
  private Applet2 app;

  public Bucket(String name, int x, int y, int side, Applet2 app) {
    super(name, "bucket", x, y);
    this.app = app;
    count = 0;
    in = new Sim_port("in", "port", side, 10); add_port(in);
    add_param(new Anim_param("Count", Anim_param.NAME_VALUE, "0", -10, -5));
  }

  // --- Connect to VRML node.
  Node mynode = null;
  EventInSFVec3f fullness = null;
  void connect3d() {
    mynode = app.getNode(get_name());
    fullness = (EventInSFVec3f) mynode.getEventIn("set_gaugescale");
  }
  void update3d(double f) {
    float[] bscale = new float[3]; bscale[0] = 1; 
    if (f<0.01) f = 0.01;
    bscale[1] = (float)f; bscale[2] = 1;
    if (fullness!=null) fullness.setValue(bscale); 
  }


  public void body() {
    Sim_event ev=null;
    connect3d();
    while(true) {
      sim_wait(ev);
      sim_hold(0.05);
      count++;
      sim_trace(1, "P "+count);
	update3d((double)count / 20.0);
    }
  }
}


// The main applet
public class Applet2 extends Anim_applet implements EventOutObserver  {

  // --- VRML Nodes     ---
  Browser browser=null;
  boolean error=true;

  Node clock = null;
  EventOutSFTime vrmlTime = null;

  // Advise callback from vrml sensors
  public void callback(EventOut who, double when, Object which) {
    Integer whichNum = (Integer) which;
    if (whichNum.intValue() == 1) {
      System.out.println("Observed an eventOut");
    }
  }

  void pause(int t) {
    try {Thread.currentThread().sleep(t);} catch (InterruptedException e) {
	System.out.println("Rudely interrupted");
    }
  }

  public void initBrowser() { 
    System.out.println("About to connect to browser.");
    pause(1000);
    browser = Browser.getBrowser(this);
    if (browser == null) {
	System.out.println("Didn't get the browser: ");
	error = true;
    } else  {
	System.out.println("Got the browser: ");
	error = false;
      try {
        clock  = browser.getNode("Clock");
	  vrmlTime = (EventOutSFTime) clock.getEventOut("time");
      }
      catch (InvalidNodeException ne) { System.out.println("Failed to get node:" + ne);   error = true; }
      catch (InvalidEventInException ee) { System.out.println("Failed to get EventIn:" + ee);  error = true; }
    }
  }
  public Node getNode(String name) {
    Node n = null;
    if (!error) {
      try {n = browser.getNode(name); }
      catch (InvalidNodeException ne) { 
	  System.out.println("Failed to get node:" + ne);   
	  error = true; }
    }
    return n;
  }
  public EventIn getEventIn(Node n, String ename) {
    EventIn e = null;
    if (!error) {
    if (n!=null) {
	try {
 	  e = n.getEventIn(ename);
 	} catch (Exception ex) { 
	  System.out.println("Couldn't get event "+ename+":"+ex);
	}
    }}
    return e;
  }
  // --- End VRML Nodes ---

  // --- Normal bits ---
  Panel inputs;
  Label msgLabel, sinkLabel, srcLabel, numWorkLabel, showMsgLabel;
  TextField msgField, sinkField, srcField;
  Choice numWorkChoice;
  Checkbox showMsgBox;
  static public boolean show_msg = true;

  // initialise the gui inputs
  public void anim_init() {
    int i;
    inputs = new Panel();
    inputs.setLayout(new GridLayout(0,2));

    msgLabel = new Label("Number of jobs:", Label.RIGHT);
    inputs.add(msgLabel);
    msgField = new TextField("5",3);
    inputs.add(msgField);

/*
    numWorkLabel = new Label("Number of workers:", Label.RIGHT);
    inputs.add(numWorkLabel);
    numWorkChoice = new Choice();
    for(i=1; i<=5; i++) numWorkChoice.addItem(" "+i+" ");
    numWorkChoice.select(2);
    numWorkChoice.addItemListener(this);;
    inputs.add(numWorkChoice);
*/
    showMsgLabel = new Label("Show messages:", Label.RIGHT);
    inputs.add(showMsgLabel);
    showMsgBox = new Checkbox("");
    showMsgBox.setState(false);
    inputs.add(showMsgBox);

    srcLabel = new Label("Farmer delay between jobs (mean):", Label.RIGHT);
    inputs.add(srcLabel);
    srcField = new TextField("0.4",5);
    inputs.add(srcField);

    sinkLabel = new Label("Worker processing delay (mean):", Label.RIGHT);
    inputs.add(sinkLabel);
    sinkField = new TextField("1.0",5);
    inputs.add(sinkField);

    this.add("North", inputs);
  }

  // Setup the animation
  public void anim_layout() {
    int num_msg, i, num_workers;
    double src_hold, sink_hold;

    System.out.println("Laying out simulation");

    initBrowser();

    num_msg = Integer.parseInt(msgField.getText());
    num_workers = 3;
    src_hold = Double.valueOf(srcField.getText()).doubleValue();
    sink_hold = Double.valueOf(sinkField.getText()).doubleValue();
    show_msg = showMsgBox.getState();

    // Add entities
    Sim_system.add(new Farmer("Farmer", num_msg, src_hold, 20, 20,this));
    Sim_system.add(new Bucket("Bucket_1", 90+70*num_workers, 20, Anim_port.LEFT,this));
    Sim_system.add(new Bucket("Bucket_2", 20, 160, Anim_port.RIGHT,this));
    for(i=1; i<=num_workers; i++) {
      Sim_system.add(new Wrouter("Wrouter_"+i, 20+70*i, 20,this));
      Sim_system.add(new Worker("Worker_"+i, sink_hold, 20+70*i, 90,this));
      Sim_system.add(new Rrouter("Rrouter_"+i, 20+70*i, 160,this));
    }

    // Link entities
    Sim_system.link_ports("Farmer", "first", "Wrouter_1", "in");
    Sim_system.link_ports("Wrouter_"+num_workers, "next", "Bucket_1", "in");
    Sim_system.link_ports("Rrouter_1", "out", "Bucket_2", "in");
    for(i=1; i<=num_workers; i++) {
      if(i!=num_workers)
        Sim_system.link_ports("Wrouter_"+i, "next", "Wrouter_"+(i+1), "in");
      Sim_system.link_ports("Wrouter_"+i, "worker", "Worker_"+i, "in");
      Sim_system.link_ports("Worker_"+i, "out", "Rrouter_"+i, "in1");
      if(i!=1)
        Sim_system.link_ports("Rrouter_"+i, "out", "Rrouter_"+(i-1), "in2");
    }
  }
}

 