/*

  Interface classes

*/


/*
   For future improvement:

   For textboxes which have changed their values,
     re-assign the variables to them WHEN THE USER CLICKS OKAY.
     Then the user doesn't have to press return in each box!
   Get rid of status window, have just one window to run an input
     through the net and report the result.
*/


import java.awt.*;
import java.io.*;


public class NetInterface extends Frame {

  public Net net;

  // Constructor for user interface

  public NetInterface (Net nt) {
    int layer, node;

    net = nt;

    setBackground(new Color(220, 220, 220));

    Menu fileMenu = new Menu("File");
    
    MenuItem dataSet = new MenuItem("Specify Input Data Set");
    fileMenu.add (dataSet);
    MenuItem loadContents = new MenuItem("Load Neurons' Contents");
    fileMenu.add (loadContents);
    MenuItem saveContents = new MenuItem("Save Neurons' Contents");
    fileMenu.add (saveContents);
    MenuItem quit_choice = new MenuItem("Quit");
    fileMenu.add (quit_choice);
    
    Menu netMenu = new Menu("Net");

    MenuItem clearContents = new MenuItem("Clear Contents");
    netMenu.add (clearContents);
    MenuItem topog = new MenuItem("Specify Topography");
    netMenu.add (topog);
    MenuItem train_choice = new MenuItem("Train Network");
    netMenu.add (train_choice);
    MenuItem run_choice = new MenuItem("Run One Input");
    netMenu.add (run_choice);
    MenuItem runAll_choice = new MenuItem("Run All Inputs");
    netMenu.add (runAll_choice);

    MenuBar menubar = new MenuBar();
    menubar.add(fileMenu);
    menubar.add(netMenu);

    Frame screen = new Frame("");
    menubar.setFont (new Font ("Dialog", Font.PLAIN, 14));
    setMenuBar(menubar);
    setTitle("Neural Network");

    repaint();
  }



  // Handle events

  public boolean action (Event e, Object o) {
    if (e.target instanceof MenuItem) {
      return handleItem ((MenuItem) e.target, (String) o);
      } else {   // unable to handle event
        return false;
      }
  }



  // Handle all the menu items

  public boolean handleItem (MenuItem m, String s) {
    if (s.equals("Load Neurons' Contents")) {

    } else if (s.equals("Save Neurons' Contents")) {

    } else if (s.equals("Specify Input Data Set")) {
      InputDataDialog d = new InputDataDialog(net);
      d.show();
    } else if (s.equals("Quit")) {
//      QuitDialog d = new QuitDialog(net);
//      d.show();
      net.quitProgram();
    } else if (s.equals("Clear Contents")) {
      net.clearNet();
    } else if (s.equals("Specify Topography")) {
      TopogDialog d = new TopogDialog(net);
      d.show();
    } else if (s.equals("Train Network")) {
      TrainDialog d = new TrainDialog(net);
      d.show();
    } else if (s.equals("Run One Input")) {
      RunDialog d = new RunDialog(net);
      d.show();
    } else if (s.equals("Run All Inputs")) {
      net.outputsWindow = new OutputsWindow(net);
      net.outputsWindow.show();
      net.outputsWindow.resize(100, 100);
    }
    return true;
  }



  public void paint (Graphics g) {
    int layer, node, i;
    Neuron cur_neuron, next_neuron;

  // wait for other things to draw first in Netscape
  for (i = 0; i < 100000; i ++);

  net.calcCoords(size().width, size().height);
  for (layer = 1; layer <= net.num_layers; layer ++)
    for (node = 1; node <= net.num_nodes[layer]; node ++) {
      cur_neuron = net.neuron[layer][node];
      cur_neuron.paint(getGraphics());
    }
  }

}



// Dialog box to specify input data file

class InputDataDialog extends Dialog {
  
  Net net;
  TextField fileField;  

  InputDataDialog (Net nt) {

    super(new Frame(), "Input Data File", true);
    net = nt;

    Panel p_title = new Panel();
    p_title.setLayout (new FlowLayout());
    p_title.add ("Center", new Label
        ("Enter a filename containing input data."));
    add("North", p_title);

    Panel p_input = new Panel();
    p_input.setLayout (new FlowLayout());
    fileField = new TextField(new String(net.inputFileNm), 30);
    fileField.setEditable(true);
    p_input.add ("Center", fileField);
    add("Center", p_input);

    Panel p_buttons = new Panel();
    Button b_OK = new Button("OK");
    p_buttons.setLayout (new FlowLayout());
    p_buttons.add (b_OK);
    p_buttons.add (new Label ("     "));
    p_buttons.add (new Button ("Cancel"));
    add("South", p_buttons);

    Font textFont = new Font ("Dialog", Font.PLAIN, 14);
    setFont (textFont);
    resize (350, 200);
  }

  // Handle the buttons

  public boolean action (Event e, Object o) {
  
    if (e.target instanceof Button) {
      if (o.equals("OK")) {
	net.inputFileNm = new StringBuffer(fileField.getText());
        net.readInputFile();
        hide();
        dispose();
      } else if (o.equals("Cancel")) {
        hide();
        dispose();
      }
    }
    else if (e.target instanceof TextField) {
      // don't need this?:
      // net.inputFileNm = new StringBuffer(fileField.getText());
    }
    return false;
  }
}



// Train dialog box

class TrainDialog extends Dialog {
  
  Net net;
  TextField trialField, learnField;
  Checkbox clearbox;
  boolean clear_net = false;

  TrainDialog (Net nt) {

    super (new Frame(), "Training parameters", true);
    net = nt;

    Panel p_title = new Panel();
    p_title.setLayout (new GridLayout(3,1));
    p_title.add (new Label (" "));
    p_title.add (new Label
        ("    Please press <return> after specifying a value."));
    add ("North", p_title);

    Panel p_list = new Panel();
    p_list.setLayout (new GridLayout(3, 1, 0, 0));

    Panel p_trial = new Panel();
    p_trial.setLayout(new FlowLayout(FlowLayout.RIGHT));
    trialField = new TextField(String.valueOf(net.iterations), 5);
    trialField.setEditable(true);
    p_trial.add(new Label("Number of training iterations"));
    p_trial.add(trialField);

    Panel p_learn = new Panel();
    p_learn.setLayout(new FlowLayout(FlowLayout.RIGHT));
    learnField = new TextField(String.valueOf(net.learn_rate), 5);
    learnField.setEditable(true);
    p_learn.add(new Label("Learning rate"));
    p_learn.add(learnField);

    Panel p_clear = new Panel();
    p_clear.setLayout(new FlowLayout(FlowLayout.RIGHT));
    clearbox = new Checkbox("Clear weights before learning");
    clearbox.setState(clear_net);
    p_clear.add(clearbox);

    p_list.add(p_trial);
    p_list.add(p_learn);
    p_list.add(p_clear);

    add("Center", p_list);

    add("East", new Label(" "));
    add("West", new Label(" "));

    Panel p_buttons = new Panel();
    p_buttons.setLayout (new FlowLayout());
    p_buttons.add (new Button ("Begin learning"));
    p_buttons.add (new Label ("  "));
    p_buttons.add (new Button ("Cancel"));
    add("South", p_buttons);

    Font f = new Font ("Dialog", Font.PLAIN, 12);
    setFont (f);
    resize (340, 300);
  }

  // Handle the buttons

  public boolean action (Event e, Object o) {
    int i, old_iter, new_iter;
    float old_rate, new_rate;
  
    if (e.target instanceof Button) {
      if (o.equals("Begin learning")) {
        hide();
        dispose();
        if (clear_net) net.clearNet();
//        IterationDialog d_iter = new IterationDialog(net);
//        d_iter.show();
        net.train();
//        DoneDialog dd = new DoneDialog(net);
//        dd.show();
      } else if (o.equals("Cancel")) {
        hide();
        dispose();
      }
    }
    else if (e.target instanceof TextField) {
      old_rate = 0;
      old_iter = 0;
      for (i = 1; i <= net.input_nodes; i ++) {
        try {
          old_iter = net.iterations;
          new_iter = Integer.parseInt(trialField.getText());
          if ((new_iter < 1) || (new_iter >= 100000))
            { throw new NumberFormatException(); }
        } catch (NumberFormatException ex) { new_iter = old_iter; }
        net.iterations = new_iter;
        trialField.setText(String.valueOf(net.iterations));
        try {
          old_rate = net.learn_rate;
          new_rate = (Float.valueOf(learnField.getText())).floatValue();
          if ((new_rate < 0.00001) || (new_rate >= 1))
            { throw new NumberFormatException(); }
        } catch (NumberFormatException ex) { new_rate = old_rate; }
        net.learn_rate = new_rate;
        learnField.setText(Float.toString(net.learn_rate));
      }
    }
    else if (e.target instanceof Checkbox) {
      if (clearbox.getState() == true)
        clear_net = true;
      else clear_net = false;
    }
    return false;
  }
}



// Done Training dialog box

class DoneDialog extends Dialog {

  Net net;

  DoneDialog (Net nt) {
  
    super (new Frame(), "Done Training", true);
    net = nt;

    setLayout (new GridLayout(2, 1));

    Panel p_msg = new Panel();
    p_msg.setLayout (new FlowLayout());
    p_msg.add (new Label ("Done training."));
    add(p_msg);

    Panel p_buttons = new Panel();
    p_buttons.setLayout (new FlowLayout());
    p_buttons.add (new Button ("Train more"));
    p_buttons.add (new Label ("    "));
    p_buttons.add (new Button ("Okay"));
    add(p_buttons);

    Font f = new Font ("Dialog", Font.PLAIN, 12);
    setFont (f);

    resize (200, 120);
  }

  // Handle the button

  public boolean action (Event e, Object o) {

    if (e.target instanceof Button) {
      if (o.equals("Okay")) {
        hide();
        dispose();
      } else if (o.equals("Train more")) {
        hide();
        dispose();
        TrainDialog d = new TrainDialog(net);
        d.show();
      }
    }
    return false;
  }
}



// Run dialog box

class RunDialog extends Dialog {
  
  Net net;
  TextField inputsField[] = new TextField[net.input_nodes+1];
  float traindata[] = new float[net.input_nodes+1];
  
  RunDialog (Net nt) {

    super (new Frame(), "Run an input", true);
    net = nt;
    int i;

    for (i = 1; i <= net.input_nodes; i ++)
      traindata[i] = 0;

    Panel p_title = new Panel();
    p_title.setLayout (new GridLayout(3,1));
    p_title.add (new Label (" "));
    p_title.add (new Label
        ("    Please press <return> after specifying each value."));

    add ("North", p_title);

    Panel p_list = new Panel();
    p_list.setLayout (new GridLayout(2, 1, 0, 0));

    Panel p_prompt = new Panel();
    p_prompt.setLayout(new FlowLayout());
    p_prompt.add(new Label("Specify network inputs:"));

    Panel p_inputs = new Panel();
    p_inputs.setLayout(new FlowLayout());
    for (i = 1; i <= net.input_nodes; i ++) {
      inputsField[i] = new TextField(String.valueOf(traindata[i]), 3);
      inputsField[i].setEditable(true);
      p_inputs.add(inputsField[i]);
    }

    p_list.add(p_prompt);
    p_list.add(p_inputs);

    add("Center", p_list);

    add("East", new Label(" "));

    Panel p_buttons = new Panel();
    p_buttons.setLayout (new FlowLayout());
    p_buttons.add (new Button ("Run"));
    p_buttons.add (new Label ("  "));
    p_buttons.add (new Button ("Cancel"));
    add("South", p_buttons);

    Font f = new Font ("Dialog", Font.PLAIN, 12);
    setFont (f);

    int box_width = 340;
    if (net.input_nodes > 5)
      box_width = 340 + (net.input_nodes - 5) * 30;
    if (box_width > 600)
      box_width = 600;
    resize (box_width, 250);
  }

  // Handle the buttons and text fields

  public boolean action (Event e, Object o) {
    int i;
    float num;
    float old_num = 0;

    if (e.target instanceof Button) {
      if (o.equals("Run")) {
        hide();
        dispose();
        net.runNet(traindata);
      } else if (o.equals("Cancel")) {
        hide();
        dispose();
      }
    }
    else if (e.target instanceof TextField) {
      for (i = 1; i <= net.input_nodes; i ++) {
        try {
          old_num = traindata[i];
          num = (Float.valueOf(inputsField[i].getText())).floatValue();
          // if (num < 0) { throw new NumberFormatException(); }
        } catch (NumberFormatException ex) { num = old_num; }
        traindata[i] = num;
        inputsField[i].setText(String.valueOf(traindata[i]));
      }
    }
    return false;
  }

}



// Topography dialog box

class TopogDialog extends Dialog {

  public Net net;
  Panel p[] = new Panel[net.MAX_LAYERS+2];
  TextField t_layer[] = new TextField[net.MAX_LAYERS+2];

  TopogDialog (Net nt) {

    super (new Frame(), "Specify Network Topography", true);
    net = nt;
    int i;
    for (i = 0; i < net.MAX_LAYERS+2; i++)
      p[i] = new Panel();

    Panel p_title = new Panel();
    p_title.setLayout (new GridLayout(2,1));
    p_title.add (new Label("Please press <Return> after specifying a value."));
    p_title.add (new Label (" "));
    add ("North", p_title);

    Panel p_list = new Panel();
    p_list.setLayout (new GridLayout(net.MAX_LAYERS+2, 1, 10, 2));
    t_layer[1] = new TextField(String.valueOf(net.num_layers), 3);
    t_layer[1].setEditable(true);

    p[0].add(t_layer[1]);
    p[0].add(new Label("No. of layers"));
    p_list.add(p[0]);
    p[1].add(new Label("   "));
    p[1].add(new Label("   "));
    p_list.add(p[1]);
    
    for (i = 0; i < net.MAX_LAYERS; i ++) {
      t_layer[i+2] = new TextField(String.valueOf(net.num_nodes[i+1]),3);
      t_layer[i+2].setEditable(true);
      if (i >= net.num_layers) t_layer[i+2].setEditable(false);
      p[i+2].add(t_layer[i+2]);
      p[i+2].add(new Label("Neurons in layer #" + String.valueOf(i+1)));
      p_list.add(p[i+2]);
    }
     
    add("Center", p_list);

    Panel p_buttons = new Panel();
    p_buttons.setLayout (new FlowLayout());
    p_buttons.add (new Button ("OK"));
    add("South", p_buttons);

    Font f = new Font ("Dialog", Font.PLAIN, 12);
    setFont (f);
    resize (300, 460);
  }


  // Handle events

  public boolean action (Event e, Object o) {
    int i, num;

    if (e.target instanceof Button) {
      if (o.equals("OK")) {
        dispose();
        hide();
      }
    }
    else if (e.target instanceof TextField) {
      try {
        num = Integer.parseInt(t_layer[1].getText());
        if (num < 1) { num = 1; throw new NumberFormatException(); }
        if (num > net.MAX_LAYERS) { throw new NumberFormatException(); }
        net.num_layers = num;
      } catch (NumberFormatException ex) { }
      t_layer[1].setText(String.valueOf(net.num_layers));
      for (i = 1; i <= net.MAX_LAYERS; i ++) {
        try {
          num = Integer.parseInt(t_layer[i+1].getText());
          if ((num < 1) && (i <= net.num_layers))
              { num = 1; throw new NumberFormatException(); }
            else if (num < 0) { num = 0; throw new NumberFormatException(); }
          if (num > net.MAX_NODES) { throw new NumberFormatException(); }
          net.num_nodes[i] = num;
        } catch (NumberFormatException ex) { }
        t_layer[i+1].setText(String.valueOf(net.num_nodes[i]));
        t_layer[i+1].setEditable(true);
        if (i > net.num_layers) t_layer[i+1].setEditable(false);
          else if (net.num_nodes[i] < 1) {
            net.num_nodes[i] = 1;
          }
        t_layer[i+1].setText(String.valueOf(net.num_nodes[i]));
        if (net.max_nodes < net.num_nodes[i])
          net.max_nodes = net.num_nodes[i];
      }
      net.initNetwork();
    }
    return false;
  }
}



// Quit dialog box

class QuitDialog extends Dialog {
  
  Net net;
  
  QuitDialog (Net nt) {

    super(new Frame(), "Quit Program?", true);
    net = nt;

    Panel p_title = new Panel();
    p_title.setLayout (new FlowLayout());
    p_title.add ("Center", new Label
        ("Are you sure you want to exit the program?"));
    add("North", p_title);
    Panel p_buttons = new Panel();
    Button b_OK = new Button("OK");
    p_buttons.setLayout (new FlowLayout());
    p_buttons.add (b_OK);
    p_buttons.add (new Label ("     "));
    p_buttons.add (new Button ("Cancel"));
    add("Center", p_buttons);
//  JDK 1.1:  b_OK.setSize(b_OK.getSize().width + 40, b_OK.getSize().height);
//  System.out.println("b_OK.size().width: " + b_OK.size().width + "  " + b_OK.size().height);
//  b_OK.resize(100, 30);
    Font textFont = new Font ("Dialog", Font.PLAIN, 14);
    setFont (textFont);
    resize (350, 130);
  }

  // Handle the buttons

  public boolean action (Event e, Object o) {
  
    if (e.target instanceof Button) {
      if (o.equals("OK")) {
        hide();
        dispose();
        // net.outputsWindow.hide();  // null pointer exceptions here
        // net.outputsWindow.dispose();
        net.quitProgram();
      } else if (o.equals("Cancel")) {
        hide();
        dispose();
      }
    }
    return false;
  }
}
