package kraft.monitor;

import kraft.monitor.display.MonitorGUI;
import kraft.monitor.adapter.LinjaAdapter;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;


/** Provides a front end to the Monitor, using the linda blackboard.
  * On startup will start a LinjaAdapter to convert between Linda format
  * and Monitor syntax.
  * Also provides a menu with options for setting up the linda adapter,
  * and also for loading in 'batch files' of stored Kraft messages.
  *
  *@author Ted Francis 
  *@date   17th July 1998
  *@see kraft.monitor.adapter.LinjaAdapter
  *@see kraft.monitor.display.MonitorGUI
  */
//================================================================
public class KraftMonitor extends Frame implements ActionListener{
//================================================================


  LinjaAdapter  linja_adapter = null;
  MonitorGUI    gui = null;
  SetupDialog   setup_dialog;
  FileDialog    file_dialog;

  private String linda_host="";
  private int    linda_port= 0;

  /** Start up a monitor to connect to the given linda host/port
    * using the given name for the monitor.
    * Also start monitoring on the given remote_sites.
    */
  KraftMonitor(String   linda_host, 
	       int      linda_port, 
	       String   name, 
	       String[] remote_sites) {

    this(linda_host, linda_port, name);
  
    if(linja_adapter!=null)
      linja_adapter.monitorRemoteSites(remote_sites);
  }


  /** Start up a monitor to connect to the given linda host/port
    * using the given name for the monitor
    */
  KraftMonitor(String linda_host, 
	       int    linda_port, 
	       String name) {
    this();
    this.linda_host = linda_host;
    this.linda_port = linda_port;

    if(linja_adapter!=null)
       linja_adapter.connectToLinda(linda_host,linda_port,name);
  }


  /** Start up a monitor.
    * User must then specify host/port/site info via the 
    * Monitor->Setup menu.
    */
  KraftMonitor() {
    super("Monitor for the KRAFT project");
    
    setSize(600,600);
    setLocation(100,100);

    addWindowListener(new WindowAdapter(){
      public void windowClosing(WindowEvent e){
	quit();
      }
    });
    
    gui = new MonitorGUI();
    add(gui);

    MenuBar  mb        = new MenuBar();
    Menu     filemenu  = new Menu("Batch File");
    Menu     setupmenu = new Menu("Monitor");
    MenuItem load      = new MenuItem("Load");
    MenuItem save      = new MenuItem("Save");
    MenuItem quit      = new MenuItem("Quit");
    MenuItem setup     = new MenuItem("Setup");
    

    filemenu.add(load);
    filemenu.add(save);
    //filemenu.addSeparator();
    //filemenu.add(quit);
    setupmenu.add(setup);
    setupmenu.addSeparator();
    setupmenu.add(quit);

    filemenu.addActionListener(this);
    setupmenu.addActionListener(this);

    mb.add(setupmenu);
    mb.add(filemenu);

    mb.setFont(new Font("SansSerif", Font.PLAIN, 10));

    this.setMenuBar(mb);

    setup_dialog = new SetupDialog(this,"Monitor Setup");
    file_dialog  = new FileDialog(this, "Batch file");
    file_dialog.setFont(setup_dialog.getFont());
    linja_adapter = new LinjaAdapter(gui);

    show();
  }
  

  /** Called to connect the Monitor to a particular linda server
    * at l_host:l_port.  Uses 'name' and 'site' as the monitor's 
    * name and site.
    * Connects also to the given remote_sites
    */
  public void setup_linda(String   l_host, int l_port, 
			  String   name,   String site,
			  String[] remote_sites) {

    linda_host = l_host;
    linda_port = l_port;

    if(linja_adapter!=null) {

      linja_adapter.connectToLinda(l_host,l_port,name,site);
      linja_adapter.monitorRemoteSites(remote_sites);
    }
  }


  /** Load the given batch file for monitoring
    *
    */
  public void loadFile(String dir, String name) {
    if(dir!=null && name!=null) {
      File file = new File(dir,name);
      FileBasedClient client = new FileBasedClient(file,linda_host,linda_port);
      client.start(); //start running on own thread
    }
  } 

  /** Save the current state of the Monitor to the given batch file.
    *
    */
  public void saveFile(String dir, String name) {

    if(dir!=null && name!=null) {
      File file = new File(dir,name);
      try {
	PrintWriter writer 
	  = new PrintWriter(new BufferedWriter(new FileWriter(file)));

	writer.println("%---------------------------------------------------------");
	writer.println("% KraftMonitor: saved messages");
	writer.println("%");
	writer.println("% date: "+ Calendar.getInstance().getTime());
	writer.println("%");
	writer.println("%---------------------------------------------------------");


	String[] messages = gui.getMessages();
	for(int i=0; i< messages.length; i++) {
	  writer.println("%\n% step "+(i+1) + "\n%");
	  writer.println(messages[i]+ ".");
	}
	writer.println();
	writer.flush();
	writer.close();

	System.out.println("\nHave saved messages to file:"+file.getPath());
      }
      catch(IOException e) {
	System.err.println("\nError writing to file '"+file.getPath()+"'");
      }
    }
  }//end SaveFile


  /** Disconnect from Linda, close down the Monitor
    *
    */
  public void quit(){
    try{
      linja_adapter.stop();
      gui.stop();
    }catch(Exception ex) {}
    dispose();
    System.exit(0);
  }


  /** Deals with events relating to the Menu bar
   *
   */
  public void actionPerformed(ActionEvent evt){
    String action = evt.getActionCommand();

    if     (action.equalsIgnoreCase("setup"))
      setup_dialog.show();

    else if(action.equalsIgnoreCase("load")) {
      file_dialog.setMode(FileDialog.LOAD);
      file_dialog.setTitle("Loading Batch file");
      file_dialog.show();
      loadFile(file_dialog.getDirectory(), file_dialog.getFile());
    } 
    else if(action.equalsIgnoreCase("save")) {
      file_dialog.setMode(FileDialog.SAVE);
      file_dialog.setTitle("Saving batch file");
      file_dialog.show();
      saveFile(file_dialog.getDirectory(), file_dialog.getFile());
    }
    else if(action.equalsIgnoreCase("quit"))
      quit();
  }


  /** Start the KraftMonitor
    *
    * Can be given command-line args for quick setup:
    *<pre>
    * eg: java kraft.monitor.KraftMonitor [-lh linda_host] 
    *			                  [-lp linda_port] 
    *                                     [-n monitor_name]
    *                                     [-s local_site]
    *                                     [[-rs remote_site_name]...]
    *<\pre>
    */

  public static void main(String[] args) {
    //parse in command line args
    String       host  = null,  name = null, site = null;
    int          port  = 0;
    Vector       sites;

    KraftMonitor km;

    if(args.length > 0){
      sites = new Vector(args.length);
      try{
	for(int i=0; i < args.length; i++) {
	  if(args[i].equals("-lh"))
	    host = args[++i];
	  else if (args[i].equals("-lp"))
	    port = Integer.parseInt(args[++i]);
	  else if(args[i].equals("-n"))   // name for monitor
	    name = args[++i];
	  else if(args[i].equals("-s"))
	    site = args[++i];
	  else if(args[i].equals("-rs"))  // remote site
	    sites.addElement(args[++i]);
	  else throw new Exception();
	}
      }
      catch(Exception e){
	System.err.println("Usage: java KraftMonitor [-lh linda_host] "+
			   "[-lp linda_port] [-n monitor_name] "+
			   "[-s local_site]"+
			   "[[-rs remote_site_name]...]");
	System.exit(1);
      }
      
      if(site!=null) {
	name = "krl("+site+","+name+")";
      }

      System.out.println("===========================");
      System.out.println("Starting Monitor");
      System.out.println("---------------------------");
      System.out.println("linda:        " +host+":"+port);
      System.out.println("my name:      " +name);

      if(!sites.isEmpty()) {
	System.out.println("remote sites: ");
	Enumeration e = sites.elements();
	while(e.hasMoreElements())
	  System.out.println("\t"+e.nextElement());
      }
      System.out.println("===========================\n");

      String[] s = new String[sites.size()];
      sites.copyInto(s);
      
      km = new KraftMonitor(host,port,name,s);
    }//end if(args...
    
    else 
      km = new KraftMonitor();

  }//end main

}//end class




/** Dialog window for connecting the KraftMonitor to 
  * the correct linda server and monitoring the correct sites.
  */

class SetupDialog extends Dialog implements ActionListener {

  boolean MONITOR_REMOTE=true;

  TextField my_name, my_site, remote_sites, linda_host, linda_port;
  Button ok_button;
  Button cancel_button;
  
  KraftMonitor kraftMonitor;
  
  //
  //constructor
  //
  public SetupDialog(Frame frame, String title)
    {
      super(frame,title);
      
      Font font = new Font("SanSerif",Font.PLAIN,12);
      setFont(font);
      addWindowListener(new WindowAdapter(){
	public void windowClosing(WindowEvent e){
	  setVisible(false);
	}
      });
    
      this.kraftMonitor = (KraftMonitor)frame;
      setLayout(new GridLayout(0,2)); //any no. of rows, two cols
      
      add(new Label("Monitor name:",Label.LEFT));
      my_name=new TextField("monitor");
      my_name.requestFocus();
      add(my_name);

      add(new Label("Linda Host", Label.LEFT));
      linda_host=new TextField("");
      add(linda_host);
      
      add(new Label("Linda Port:",Label.LEFT));
      linda_port=new TextField("0");
      add(linda_port);

      Label ls_label = new Label("Local site:",Label.LEFT);
      add(ls_label);
      my_site=new TextField("");
      add(my_site);
      
      Label rs_label =new Label("Remote sites:", Label.LEFT);
      add(rs_label);
      remote_sites=new TextField("");
      add(remote_sites);

      ok_button=new Button("Ok");
      ok_button.setFont(new Font(font.getName(), Font.BOLD, font.getSize()));
      add(ok_button);
      ok_button.addActionListener(this);
      
      cancel_button=new Button("Cancel");
      add(cancel_button);
      cancel_button.addActionListener(this);

      rs_label.setEnabled(MONITOR_REMOTE);
      remote_sites.setEnabled(MONITOR_REMOTE);
      ls_label.setEnabled(MONITOR_REMOTE);
      my_site.setEnabled(MONITOR_REMOTE);      

      pack();
    }
  //
  //handler for dialog buttons
  //
  
  public void actionPerformed(ActionEvent e)
    {
      if (e.getSource()==ok_button) {
	String[] sites;
	if(remote_sites.getText().equals("")) 
	  sites = null;
	else {
	  StringTokenizer tok = new StringTokenizer(remote_sites.getText(),
						    " ,");
	  sites = new String[tok.countTokens()];
	  int i=0;
	  while(tok.hasMoreTokens()){
	    sites[i++] = tok.nextToken();
	  }
	}//end else
	
	if(my_name.getText().equals(""))
	  System.out.println("Monitor must have a name");
	else if	(linda_host.getText().equals(""))
	  System.out.println("Please specify Linda host");
	else if	(linda_port.getText().equals(""))
	  System.out.println("Please specify Linda port");
	else {
	  kraftMonitor.setup_linda(linda_host.getText(),
				   Integer.parseInt(linda_port.getText()),
				   my_name.getText(),
				   my_site.getText(),
				   sites);
	  setVisible(false);
	}
      }
      else if(e.getSource()==cancel_button)
	setVisible(false);
    
    }//end actionperformed
  
}//end SetupDialog


