package asc;

import aglet.*;
import aglet.event.*;
import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;


class agTimer extends Thread {
  AscRemote ag;
  int Timeout;

  public agTimer (AscRemote ag, int timeout) {
    this.ag = ag;
    Timeout = timeout;
  }

  public void run () {
    System.out.println(ag.agID + ": setting timer for " + Timeout + " sec");
    try { sleep(Timeout*1000); }
    catch (Exception ex) { ex.printStackTrace(); }

    ag.Forward();
  }
}


class AscRemoteFrame extends Frame {
  Frame f;
  TextArea input;
  TextArea output;
  TextArea status;

  Button send;
  Button bcast;
  Button clear;
  Button stop;
  Button quit;

  public AscRemoteFrame(String title) {
    f = new Frame();
    Font normFont = new Font("Helvetica", Font.PLAIN, 10);
    Font boldFont = new Font("Helvetica", Font.BOLD, 10);
    f.setSize(400, 420);
    f.setFont(boldFont);
    f.setTitle(title);
    f.setLayout(new BorderLayout());    

    Panel buttonP = new Panel();

    send = new Button("send reply");
    buttonP.add(send);

    bcast = new Button("bcast reply");
    buttonP.add(bcast);

    clear = new Button("clear");
    clear.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        input.setText(""); }});
    buttonP.add(clear);

    stop = new Button("stop timer");
    buttonP.add(stop);

    quit = new Button("quit");
    buttonP.add(quit);

    f.add(buttonP, "North");

    output = new TextArea("", 13, 40,  TextArea.SCROLLBARS_VERTICAL_ONLY);
    output.setEditable(false);
    output.setText("INSTRUCTIONS:\n" +
		   "Enter your reply message  in the text area below, then press \n" + 
		   "the 'send reply' button to send the reply, or the 'bcast reply' \n" +
		   "button to broadcast the reply. Press the 'quit' button to exit.\n");
    f.add(output, "Center");

    Panel textP = new Panel();
    textP.setLayout(new BorderLayout());

    input = new TextArea("", 7, 40,  TextArea.SCROLLBARS_VERTICAL_ONLY);
    textP.add(input, "Center");

    status = new TextArea("", 5, 40,  TextArea.SCROLLBARS_VERTICAL_ONLY);
    status.setFont(normFont);
    status.setEditable(false);
    textP.add(status, "South");

    f.add(textP, "South");

    f.validate();
    f.show();    
  }

  public void addStatus (String txt) {
    status.append("\n" + txt);
    status.select(status.getText().length(), status.getText().length());
  }

  public void setStatus (String txt) {
    status.setText(txt);
    status.select(status.getText().length(), status.getText().length());
  }

  public void addOutput (String txt) {
    output.append("\n" + txt);
    output.select(output.getText().length(), output.getText().length());
  }

  public void setOutput (String txt) {
    output.setText(txt);
    output.select(output.getText().length(), output.getText().length());
  }
}


public class AscRemote extends Aglet implements MobilityListener /*, CloneListener*/  {

  public AscRemoteFrame ascFr = null;

  private String ID = "none";
  private AgletProxy master;

  private int NumAgs;
  private int NextDest = -1;
  private String[] Dests = null;
  private String MsgText = null;
  private int Timeout = 15;
  public AgletID agID;

  transient private agTimer Timer;


  public void onCreation (Object o) { 
    super.onCreation(o);
    addMobilityListener(this);
    // addCloneListener(this);

    agID = getAgletID();
    System.out.println("\n" + agID + ": onCreation");

    if (o instanceof AgletID) { master = getAgletContext().getAgletProxy((AgletID)o); } 
    else 
      if (o instanceof AgletProxy) { master = (AgletProxy)o; } 
      else { master = null; }
  }


  public boolean handleMessage (Message msg) {
    System.out.println(agID + ": handleMessage");
    String kind = msg.getKind();
    String txt;

    if ( kind.equals("bcast") ) {
      ascFr.addStatus(msg.getArg("agID") + ": bcast msg");
      txt = "\nbcast from " + (String)msg.getArg("name") + ":\n" + (String)msg.getArg("txt");
      MsgText = MsgText.concat(txt);
      ascFr.addOutput(txt);
      return(true);
    }

    if ( kind.equals("synopsis") ) {
      //ascFr.addStatus(msg.getArg("agID") + ": synopsis msg");
      ID = (String)msg.getArg("id");
      Timeout = ((Integer)msg.getArg("time")).intValue();
      MsgText = "initial message from " + (String)msg.getArg("name") + ":\n" + 
	        (String)msg.getArg("txt");
      //ascFr.setOutput(MsgText);
      return(true);
    }

    if ( kind.equals("info") ) {
      ascFr.addStatus(msg.getArg("agID") + ": info msg");
      ascFr.addStatus((String)msg.getArg("txt"));
      return(true);
    }

    if ( kind.equals("error") ) {
      ascFr.addStatus("\n" + msg.getArg("agID") + " ERROR:");
      ascFr.addStatus((String)msg.getArg("txt"));
      return(true);
    }

    System.out.println(agID + ": unknown message type '" + kind + "'");
    return(true);
  }


  public void getDests () {
    BufferedReader in = null;
    Vector temp = new Vector();
    String buf;
    int i;

    System.out.println(agID + ": reading file 'forward.asc'");
    try { 
      in = new BufferedReader(new FileReader("forward.asc")); 
      buf = in.readLine();
      while ( buf != null ) {
	if ( !buf.equals("") ) { temp.addElement(buf); }
	buf = in.readLine();
      }
	NumAgs = temp.size();
	Dests = new String[NumAgs];
	temp.copyInto(Dests);
      }
    catch (FileNotFoundException fnfex) {
      // ascFr.addStatus("file 'forward.asc' not found");
      System.out.println(agID + ": file 'forward.asc' not found");
    }
    catch (Exception ex) { ex.printStackTrace(); }
  }

//   public void onCloning (CloneEvent event) {
//     System.out.println(agID + ": onCloning");
//   }
//   public void onClone (CloneEvent event) {
//     System.out.println(agID + ": onClone");
//     createUI(agID + ": cloned");
//   }
//   public void onCloned (CloneEvent event) {
//     System.out.println(agID + ": onCloned");
//   }


  public void run () {
    System.out.println(agID + ": run");
  }

  public void SendReply (boolean bcast) {
    Message Replmsg = null;

    StopTimer();

    if (!bcast) {
      Replmsg = new Message("reply");
      Replmsg.setArg("agID", agID);
      Replmsg.setArg("ID", ID);
      Replmsg.setArg("txt", ascFr.input.getText());
    }
    else {
      String msg = ascFr.input.getText();
      ascFr.addOutput("\nlocal bcast:\n" + msg);

      Replmsg = new Message("bcast");
      Replmsg.setArg("agID", agID);
      Replmsg.setArg("ID", ID);
      Replmsg.setArg("txt", ascFr.input.getText());
    }

    try { master.sendAsyncMessage(Replmsg); }
    catch (Exception ex) { ex.printStackTrace(); }
  }


  public void StopTimer () {
    if (Timer != null) {
      Timer.stop();
      Timer = null;
      System.out.println(agID + ": stopping timer");
      ascFr.addStatus("timer stopped");
    }
  }


  public void onDispatching(MobilityEvent event) {
    System.out.println(agID + ": leaving for " + event.getLocation());

    if (ascFr != null)
      destroyUI();
  }

  public void onArrival(MobilityEvent event) {
    System.out.println(agID + ": arrived at " + event.getLocation());
    //System.out.println(agID + ": onArrival threadID: " + Thread.currentThread());
    int i;
    Message Infomsg;

    createUI(agID + ": running at " + event.getLocation());
    ascFr.addStatus("ID = " + ID);

    if (Dests == null) { 
      getDests(); 
      NextDest = 0;
    }
    else NextDest++;

    if ( (Dests != null) && (NextDest < Dests.length) ) {
      ascFr.addStatus("next location: " + Dests[NextDest] + ",");
      ascFr.addStatus("dispatch in " + Timeout + " sec");
    }
    else {
      ascFr.addStatus("no further forwarding info, ");
      ascFr.addStatus("self-terminate in " + Timeout + " sec");
    }

    if (MsgText != null)
      ascFr.addOutput(MsgText);

    try {
      ascFr.addStatus("masterID = " + master.getAgletID());
    } catch (Exception ex) { ex.printStackTrace(); }

    Timer = new agTimer(this, Timeout);
    Timer.start();

    if (NextDest != 0) {
      try {
	Infomsg = new Message("forward");
	Infomsg.setArg("agID", agID);
	Infomsg.setArg("name", ID);
	AgletProxy newAP = getAgletContext().getAgletProxy(agID);
	Infomsg.setArg("newproxy", newAP);
	Infomsg.setArg("txt", Dests[NextDest-1]);
	
	master.sendAsyncMessage(Infomsg); 
      }
      catch (Exception ex) { ex.printStackTrace(); }
      System.out.println(agID + ": notified master");
    }
    else {
      try {
	Infomsg = new Message("info");
	Infomsg.setArg("agID", agID);
	Infomsg.setArg("name", ID);
	Infomsg.setArg("txt", "arrived at " + event.getLocation());
	
	master.sendAsyncMessage(Infomsg); 
      }
      catch (Exception ex) { ex.printStackTrace(); }
      System.out.println(agID + ": notified master");
    }

  }

  public void onReverting(MobilityEvent event) {}


  public void createUI (String info) {
    System.out.println(agID + ": creating UI");
    ascFr = new AscRemoteFrame("ascRemote");
    ascFr.setStatus(info);

    ascFr.send.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        SendReply(false); }});

    ascFr.bcast.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        SendReply(true); }});

    ascFr.stop.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	StopTimer(); }});

    ascFr.quit.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        dispose(); }});
  }

  public void Forward () {
    int i;
    System.out.println(agID + ": Forward");

    Timer = null;

    if ( (Dests != null) && (NextDest < Dests.length) ) {
      try {
	Message Infomsg = new Message("info");
	Infomsg.setArg("agID", agID);
	Infomsg.setArg("name", ID);
	Infomsg.setArg("txt", "forwarding to " + Dests[NextDest]);
	master.sendMessage(Infomsg);
	  
	dispatch(new URL("atp://" + Dests[NextDest])); 
      }
      catch (Exception ex) { ex.printStackTrace(); };
    }
    else {
      for (i = 5; i > 0; i--) {
 	ascFr.setStatus("\nself-terminating in " + i + " sec");
	try { Thread.currentThread().sleep(1000); }
	catch (Exception ex) { ex.printStackTrace(); }
      }
      dispose();
    }
  }

//   public void ForwardClone () {
//     int i;
//     AgletProxy newAP;
//     System.out.println(agID + ": Forward");

//     Timer = null;

//     if ( (Dests != null) && (NextDest < Dests.length) ) {
//       try {
// 	Message Infomsg = new Message("info");
// 	Infomsg.setArg("agID", agID);
// 	Infomsg.setArg("name", ID);
// 	Infomsg.setArg("txt", "forwarding to " + Dests[NextDest]);
// 	master.sendAsyncMessage(Infomsg);

//         newAP = (AgletProxy)clone();
//         newAP = newAP.dispatch(new URL("atp://" + Dests[NextDest])); 
        
//         Message Infomsg = new Message("forward");
//         Infomsg.setArg("agID", agID);
//         Infomsg.setArg("name", ID);
//         Infomsg.setArg("newproxy", newAP);
//         Infomsg.setArg("newagID", newAP.agID);
//         Infomsg.setArg("txt", "forwarding to " + Dests[NextDest]);
//         master.sendAsyncMessage(Infomsg);
//       }
//       catch (Exception ex) { ex.printStackTrace(); };
//     }
//     else {
//       for (i = 5; i > 0; i--) {
//  	ascFr.setStatus("no further forwarding info\nself-terminating in " + i + " sec");
// 	try { Thread.currentThread().sleep(1000); }
// 	catch (Exception ex) { ex.printStackTrace(); }
//       }
//       dispose();
//     }
//   }


  public void onDisposing () {
    System.out.println(agID + ": normal exit");

    Message Infomsg = new Message("dispose");
    Infomsg.setArg("agID", agID);
    Infomsg.setArg("name", ID);

    try  { master.sendAsyncMessage(Infomsg); }
    catch (Exception ex)  { ex.printStackTrace(); };

    destroyUI();
  }

  public void destroyUI () {
    ascFr.dispose();
    ascFr = null;
  }
}


