/*
 * Aleph Toolkit
 *
 * Copyright 1999, Brown University, Providence, RI.
 * 
 *                         All Rights Reserved
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose other than its incorporation into a
 * commercial product is hereby granted without fee, provided that the
 * above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Brown University not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 * 
 * BROWN UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ANY
 * PARTICULAR PURPOSE.  IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE FOR
 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package aleph.gui;

import aleph.Aleph;
import aleph.AlephDebug;
import aleph.Config;
import aleph.comm.Client;
import java.awt.Button;
import java.awt.Color;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.OutputStream;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * Panel containing Start Button, command line, and Clear button.
 *
 * @author Maurice Herlihy
 * @date   January 1999;
 **/

public class CommandPanel extends Panel {

  private State    state;       // misc gui state
  private TextArea textArea;	// output area
  private Client   client;      // client object

  // my components
  private TextField cmdField;   // command appears here
  private Button startButton;	// start/stop button
  private Button clearButton;	// clean slate

  private StartActionListener listener; // handle start/stop/termination

  /**
   * Sole Constructor.
   * @param state misc gui state
   * @param args  user's original args
   **/
  public CommandPanel (State state, String[] args, TextArea textArea) {
    super();
    this.state    = state;
    this.textArea = textArea;
    this.listener = new StartActionListener();

    GridBagLayout layout = new GridBagLayout();
    setLayout(layout);
    GridBagConstraints c = new GridBagConstraints();

    // Start button
    c.weightx = 0.0;		// don't become wider
    c.weighty = 0.0;		// nor higher
    c.fill = GridBagConstraints.HORIZONTAL;
    startButton = new Button("Start");
    startButton.setBackground(Color.green);
    startButton.addActionListener(listener);
    add(startButton, c);

    StringBuffer text = new StringBuffer(80);
    for (int i = 0; i < args.length; i++) {
      text = text.append(args[i]);
      text = text.append(' ');
    }
    this.cmdField = new TextField(text.toString(), 80);
    // color in text field
    cmdField.setBackground(Config.background);
    cmdField.setForeground(Config.foreground);
    cmdField.addActionListener(listener);

    // define constraints
    c.weightx = 1.0;		// become wider
    add(cmdField, c);
    c.gridwidth = GridBagConstraints.REMAINDER;	// last in row

    // Clear button
    c.weightx = 0.0;		// no wider
    c.fill = GridBagConstraints.BOTH;
    clearButton = new Button("Clear");
    clearButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	CommandPanel.this.cmdField.setText("");
	CommandPanel.this.textArea.setText("");
      }
    });
    add(clearButton, c);
  }

  // Inner class: what to do when command issued.
  class StartActionListener implements ActionListener {
    public synchronized void actionPerformed(ActionEvent e) {
      String command = e.getActionCommand(); // what happened?
      if (command.equals("Stop"))
        stopHandler();
      else if (command.equals("Done"))
        doneHandler();
      else                      // Start or carriage-return
        startHandler();
    }
    // Start the computation
    private void startHandler() {
      // Avoid long-wided Java naming conventions
      State  state         = CommandPanel.this.state;
      Vector selectedHosts = state.selectedHosts;
      Frame  frame         = state.frame;

      // If default numPEs is zero, make one for each live host.
      int numPEs = (state.numPEs == 0) ? state.liveHosts.size() : state.numPEs;

      // freeze command field and clear button
      cmdField.setEditable(false);
      cmdField.setEnabled(false);
      clearButton.setEnabled(false);

      // Check for selected hosts 
      if (selectedHosts.isEmpty()) { // no selected hosts
	new ErrorDialog(frame, "No hosts selected.");
	restore();
	return;
      }
      // switch button mode
      startButton.setBackground(Color.red);
      startButton.setLabel("Stop");
      OutputStream[] output = new OutputStream[numPEs];
      switch (state.outChoice) {
      case State.CONSOLE :
	OutputStream out = new TextAreaOutputStream(textArea);
	for (int i = 0; i < numPEs; i++)
	  output[i] = out;
	break;
      case State.PANED :
	PanedWindow panedWindow = new PanedWindow(numPEs);
	for (int i = 0; i < numPEs; i++)
	  output[i] = new TextAreaOutputStream(panedWindow.getTextArea(i));
	break;
      case State.SINGLE :
	for (int i = 0; i < numPEs; i++) {
	  output[i] = new TextAreaOutputStream((new SingleWindow()).getTextArea());
	}
	break;
      case State.STDOUT :
	for (int i = 0; i < numPEs; i++) {
	  output[i] = System.out;
	}
	break;
      default:
        Aleph.panic("Unexpected output option code: " +
                    state.outChoice);
      }
      // Display command line
      if (Aleph.getVerbosity() > 0)
          textArea.append(Aleph.getLongBanner());
      String line = cmdField.getText();
      textArea.append("\n");
      textArea.append(line);
      textArea.append("\n\n");
      StringTokenizer toke = new StringTokenizer(line);
      if (toke.countTokens() == 0) {
	new ErrorDialog(frame, "No class specified.");
	restore();
	return;
      }
      String[] args = new String[toke.countTokens()];
      int i = 0;
      while (toke.hasMoreTokens())
	args[i++] = toke.nextToken();

      // encode remaining state as properties
      Aleph.setProperty("aleph.verbosity", Integer.toString(Aleph.getVerbosity()));
      if (state.java_g)
        Aleph.setProperty("aleph.java_g", "");
      if (state.profile)
        Aleph.setProperty("aleph.profile", "");

      // Create and start client
      try {
        client = new Client(listener,
                            numPEs,
                            selectedHosts,
                            args,
                            output,
                            state.logging);
        client.start();
      } catch (Exception e) {
        Aleph.panic("CommandPanel\t" + e);
      }
    }

    // User aborts computation
    private void stopHandler() {
      textArea.append("Shutting down PEs\n");
      client.exit();
      restore();
    }

    private void doneHandler() {
      textArea.setCaretPosition(textArea.getText().length());
      textArea.setVisible(true);
      restore();
    }

    private void restore () {
      startButton.setBackground(Color.green);
      startButton.setLabel("Start");
      cmdField.setEditable(true); // unfreeze command field
      cmdField.setEnabled(true);
      clearButton.setEnabled(true); // unfreeze start button
    }
  }

  /* Inner class to turn TextArea into outputStream */
  private class TextAreaOutputStream extends OutputStream {
    TextArea textArea;
    public TextAreaOutputStream (TextArea textArea) {
      this.textArea = textArea;
    }

    public void write (int b) throws IOException {
      byte[] a = {(byte) b};
      write(a);
    }

    public void write (byte b[]) throws IOException {
      textArea.append(new String(b));
    }

    public void write (byte b[],
		       int off,
		       int len) throws IOException {
      textArea.append(new String(b, off, len));
    }

  }
}
