package click.gui;
/*
 * ClickController.java -- main ClickController program
 * Eddie Kohler, Douglas S. J. De Couto
 *
 * Copyright (c) 2000 Massachusetts Institute of Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * Further elaboration of this license, including a DISCLAIMER OF ANY
 * WARRANTY, EXPRESS OR IMPLIED, is provided in the LICENSE file, which is
 * also accessible at http://www.pdos.lcs.mit.edu/click/license.html
 */

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.InetAddress;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;

import jist.runtime.guilog.LogInterface;
import jist.swans.Constants;

import click.runtime.ClickAdapter;
import click.runtime.ClickException;
import click.runtime.ClickInterfaceSimImpl;
import click.runtime.HandlerInfo;
import click.runtime.remote.ControlSocket;

public class ClickController extends JPanel implements LogInterface {

  /**
   *
   */
  private static final long serialVersionUID = -7214998460521754917L;

  /**
   * Object that acquires the lock for pausing the Controller.
   */
  private static Object pauselock = new Object();

  /**
   * Pause-status of the Controller.
   */
  private static boolean paused = true;

  /**
   * Re-used Icon.  Displayed on pauseButton.
   */
  public static ImageIcon pauseIcon  = createImageIcon("images/pause.jpg");

  /**
   * Re-used Icon.  Displayed on pauseButton.
   */
  public static ImageIcon resumeIcon = createImageIcon("images/resume.jpg");

  /**
   * Re-used Icon.  Displayed on untilButton.
   */
  public static ImageIcon untilIcon = createImageIcon("images/clock.jpg");

  /**
   * Creates an ImageIcon.
   *
   * @param path String The path where the image is located.
   * @return ImageIcon with the specified image.
   */
  public static ImageIcon createImageIcon(String path)
  {
    java.net.URL imgURL = ClickController.class.getResource(path);
    if(imgURL==null)
    {
      System.err.println("Couldn't find file: " + path);
      return null;
    }
    return new ImageIcon(imgURL);
  }

  static
  {
    JFrame.setDefaultLookAndFeelDecorated(true);
  }

  /**
   * The JButton that pauses and resumes the gui.
   */
  private static JButton pauseButton;

  /**
   * The JButton that starts the Until-timer dialog.
   */
  private static JButton untilButton;

  private static JLabel timeLabel;

  private JFrame _frame;
  private JMenuItem _closeItem;
  private JLabel _statusLine;

  private JPanel _infoPanel;
  private JLabel _handlerLabel;
  private JTextArea _handlerText;
  private JButton _changeButton;
  private HandlerInfo _selectedHandler;

  private List _listControllers;

  private long untilTime;

  private static Vector controllers = new Vector();

  //
  // Filter constants
  //
  public final static int ALL = 0;
  public final static int MESHNODE = 1; //MeshNode
  public final static int AP = 2; //MeshNode/access_point
  public final static int BRN_IAPP = 3; //MeshNode/brn_iapp
  public final static int DHT = 4; //MeshNode/dht
  public final static int DSR = 5; //MeshNode/dsr
  public final static int SERVICES = 6; //MeshNode/services
  public final static int VLAN = 7; //MeshNode/vlan
  public final static int CUSTOM = 8;


  public ClickController controller() {
    return this;
  }

  public void enableClose() {
    if (_closeItem != null)
      _closeItem.setEnabled(countControllers() > 1
              || !firstController().empty());
  }

  private static int countControllers() {
    synchronized (controllers) {
      return controllers.size();
    }
  }

  private static void addController(ClickController cntr) {
    synchronized (controllers) {
      if (!controllers.contains(cntr)) {
        controllers.addElement(cntr);
        for (int i = 0; i < controllers.size() - 1; i++)
          ((ClickController) controllers.elementAt(i)).enableClose();
      }
    }
  }

  private static void removeController(ClickController cntr) {
    synchronized (controllers) {
      if (controllers.removeElement(cntr)) {
        for (int i = 0; i < controllers.size(); i++)
          ((ClickController) controllers.elementAt(i)).enableClose();
        if (controllers.size() == 0)
          System.exit(0);
      }
    }
  }

  private static ClickController firstController() {
    synchronized (controllers) {
      return (ClickController) controllers.elementAt(0);
    }
  }

  public boolean isApplet() {
    return false;
  }

  public boolean empty() {
    return (_listControllers == null) || (_listControllers.size() == 0);
  }

  /**
   * Returns the frame instance
   */
  public JFrame getFrame() {
    return _frame;
  }


  class OpenAction extends AbstractAction {
    ClickController _cntr;

    OpenAction(ClickController cntr) {
      _cntr = cntr;
    }

    public void actionPerformed(ActionEvent e) {
      new NewConnectionDialog(_cntr);
    }
  }

  public JMenuBar createMenus() {
    JMenuItem mi;
    JMenuBar menuBar = new JMenuBar();

    JMenu connMenu = (JMenu) menuBar.add(new JMenu("Connections"));
    connMenu.setMnemonic('C');

    mi = createMenuItem(connMenu, "Open...", 'O', new OpenAction(this));
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, Event.CTRL_MASK));

    mi = createMenuItem(connMenu, "Refresh", 'R', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        setControlSocket(_listControllers, ALL);
      }
    });
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, Event.CTRL_MASK));

    _closeItem = createMenuItem(connMenu, "Close", 'C', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        if (countControllers() > 1) {
          getFrame().dispose();
          removeController(controller());
        } else {
          setControlSocket(null, ALL);
          setStatusLine("Not connected");
          enableClose();
        }
      }

    });
    _closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, Event.CTRL_MASK));

    if (!isApplet()) {
      connMenu.addSeparator();
      mi = createMenuItem(connMenu, "Exit", 'x', new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
          System.exit(0);
        }
      });
      mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK));
    }

    // Filter menue
    JMenu filterMenu = (JMenu) menuBar.add(new JMenu("Filter"));
    filterMenu.setMnemonic('F');

    mi = createMenuItem(filterMenu, "All", 'A',  new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        filterNodes(ALL);
      }
    });

    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, Event.CTRL_MASK));

    mi = createMenuItem(filterMenu, "MeshNode", 'M', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        filterNodes(MESHNODE);
      }
    });
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, Event.CTRL_MASK));

    mi = createMenuItem(filterMenu, "AP", 'P', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        filterNodes(AP);
      }
    });
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK));

    mi = createMenuItem(filterMenu, "BRN_IAPP", 'I', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        filterNodes(BRN_IAPP);
      }
    });
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.CTRL_MASK));

    mi = createMenuItem(filterMenu, "DHT", 'D', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        filterNodes(DHT);
      }
    });
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, Event.CTRL_MASK));

    mi = createMenuItem(filterMenu, "DSR", 'R', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        filterNodes(DSR);
      }
    });
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, Event.CTRL_MASK));

    mi = createMenuItem(filterMenu, "SERVICES", 'S', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        filterNodes(SERVICES);
      }
    });
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK));

    mi = createMenuItem(filterMenu, "VLAN", 'V', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        filterNodes(VLAN);
      }
    });
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, Event.CTRL_MASK));

    mi = createMenuItem(filterMenu, "custom", 'C', new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        String filter = (String)JOptionPane.showInputDialog(_frame,
        "Enter custom filter: ");
        if (null == filter)
          return;
        filterNodesCustom(filter);
      }
    });
    mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.CTRL_MASK));

    return menuBar;
  }

  /**
   * Creates a generic menu item
   */
  public JMenuItem createMenuItem(JMenu menu, String label, char mnemonic,
                                  Action action) {
    JMenuItem mi = (JMenuItem) menu.add(new JMenuItem(label));
    mi.setMnemonic(mnemonic);
    mi.addActionListener(action);
    if (action == null) {
      mi.setEnabled(false);
    }
    return mi;
  }


  private static class CloseWindowAdapter extends WindowAdapter {
    private ClickController _cntr;

    public CloseWindowAdapter(ClickController cntr) {
      _cntr = cntr;
    }

    public void windowClosing(WindowEvent e) {
      _cntr.doClose();
    }
  }

  public static void main(String argv[]) {
    ClickController cntr = newWindow();
    if (argv.length >= 2)
      cntr.connectTo(argv[0], argv[1]);
    else
      cntr.setStatusLine("Not connected");
  }


  public ClickController(JFrame frame) {
    _frame = frame;
    addController(this);
    untilTime = Long.MAX_VALUE;

    setLayout(new BorderLayout());

    JMenuBar menuBar = createMenus();
    add(menuBar, BorderLayout.NORTH);

    _infoPanel = new JPanel(new BorderLayout());
    add(_infoPanel, BorderLayout.CENTER);

    _statusLine = new JLabel("");
    add(_statusLine, BorderLayout.SOUTH);

    _listControllers = new LinkedList();
  }


  private class HandlerSelectAction implements TreeSelectionListener {

    public void valueChanged(TreeSelectionEvent e) {
      TreePath path = e.getNewLeadSelectionPath();
      if (path == null)
        return;

      DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
      Object o = node.getUserObject();
      if (o == null)
        return;

      HandlerInfo hi = null;
      if (o instanceof HandlerInfo)
        hi = (HandlerInfo) o;
      else if (o instanceof RouterTreeModel.HandlerUserObject)
        hi = ((RouterTreeModel.HandlerUserObject) o)._hinfo;
      if (hi == null)
        return;
      selectHandler(hi);
    }

  }

  private class ChangeButtonAction extends AbstractAction {

    public void actionPerformed(ActionEvent event) {
      if (_selectedHandler != null && _selectedHandler.canWrite) {
        try {
          _selectedHandler.getControl().write(_selectedHandler, _handlerText.getText());
          if (_selectedHandler.canRead)
            _handlerText.setText(_selectedHandler.getControl().readString(_selectedHandler));
          else
            _handlerText.setText("");
        } catch (ClickException e) {
          JOptionPane.showMessageDialog(getFrame(), e.getMessage(), "Write Handler Error", JOptionPane.ERROR_MESSAGE);
        } catch (java.io.IOException e) {
          JOptionPane.showMessageDialog(getFrame(), e.getMessage(), "Write Handler Network Error", JOptionPane.ERROR_MESSAGE);
        }
      }
    }

  }

  //////////////////////////////////////////////////
  // Button handler
  //

  /**
   * Defines how mouse clicks on the buttons are handled.
   */
  public class ButtonHandler implements ActionListener
  {
    /**
     * Decides what action to perform depending on which button is pressed.
     *
     * @param e ActionEvent The event of clicking on the button.
     */
    public void actionPerformed(ActionEvent e)
    {
      JButton button = (JButton)e.getSource();

      if("Pause".equals(e.getActionCommand()))
      {
        //updateUI(true);
        pause();
      }
      else if("Resume".equals(e.getActionCommand()))
      {
        //updateUI(false);
        resume();
      }
      else if("Until".equals(e.getActionCommand()))
      {
        // Start the guis timer
        untilTimer();
      }
    }
  }

  //////////////////////////////////////////////////
  // Button handler
  //

  //////////////////////////////////////////////////
  // Until Task
  //

  /**
   * UntilTask is a task that can be scheduled to either Pause or Resume the gui.
   */
  public class UntilTask extends TimerTask
  {
    /**
     * The gui method to be called.
     */
    private String command;

    /**
     * The constructor initializes command.
     *
     * @param c String The gui method.
     */
    public UntilTask(String c)
    {
      command = c;
    }

    /**
     * Call the appropriate gui method according to command.
     */
    public void run()
    {
      if(command.equals("Pause"))
      {
        pause();
      }
      else if(command.equals("Resume"))
      {
        resume();
      }
      else
      {
        System.out.println("Should not reach here.");
      }
    }
  }

  public void setControlSocket(List listControllers, int filter_type) {
    setControlSocket(listControllers, filter_type, "");
  }

  public void setControlSocket(List listControllers, int filter_type, String filter) {
    _listControllers = listControllers;
    _selectedHandler = null;
    _infoPanel.removeAll();

    if (null != listControllers) {
      JPanel result_panel = new JPanel();
      result_panel.setLayout(new BorderLayout());

      JPanel buttonPanel = new JPanel();
      buttonPanel.setBorder(new javax.swing.border.EmptyBorder(3, 5, 2, 5));
      buttonPanel.setLayout(new BorderLayout());
      result_panel.add(buttonPanel, BorderLayout.NORTH);

      _handlerLabel = new JLabel(" ");
      buttonPanel.add(_handlerLabel, BorderLayout.CENTER);

      _changeButton = new JButton("Change");
      buttonPanel.add(_changeButton, BorderLayout.EAST);
      _changeButton.setEnabled(false);

      _handlerText = new JTextArea(" ");
      _handlerText.setMargin(new Insets(0, 3, 0, 3));
      JScrollPane result_scroll = new JScrollPane
              (_handlerText, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                      JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
      result_scroll.setPreferredSize(new Dimension(280, 250));
      result_panel.add(result_scroll, BorderLayout.CENTER);

      JTree rtree = new JTree(new RouterTreeModel(listControllers, filter_type, filter));
      _changeButton.addActionListener(new ChangeButtonAction());
      rtree.addTreeSelectionListener(new HandlerSelectAction());
      JScrollPane router_scroll = new JScrollPane(rtree);

      JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
              router_scroll, result_panel);
      _infoPanel.add(split, BorderLayout.CENTER);
    } else {
      _infoPanel.repaint();
      _handlerLabel = null;
      _handlerText = null;
      _changeButton = null;
    }

    getFrame().getRootPane().validate();
  }

  void filterNodes(int type) {
    // refresh view
    setControlSocket(_listControllers, type);
  }

  void filterNodesCustom(String filter) {
    // refresh view
    setControlSocket(_listControllers, CUSTOM, filter);
  }

  void selectHandler(HandlerInfo hinfo) {
    _selectedHandler = hinfo;
    if (hinfo == null) {
      _handlerLabel.setText("");
      _handlerText.setText("");
      _handlerText.setEditable(false);
      _changeButton.setEnabled(false);
      return;
    }

    boolean writeEnabled = hinfo.canWrite;
    _handlerLabel.setText(hinfo.getDescription());
    if (hinfo.canRead) {
      try {
        String s = _selectedHandler.getControl().readString(hinfo);
        _handlerText.setText(s);
        _handlerText.setCaretPosition(0);
      } catch (Throwable t) {
        _handlerText.setText(t.toString());
        writeEnabled = false;
      }
    } else {
      _handlerText.setText("");
    }
    _handlerText.setEditable(writeEnabled);
    _changeButton.setEnabled(writeEnabled);
  }

  public Dimension getPreferredSize() {
    return new Dimension(500, 320);
  }

  public void setStatusLine(String s) {
    _statusLine.setText(s);
  }

  public void doClose() {
    removeController(this);
  }

  public static ClickController newWindow() {
    JFrame frame = new JFrame("Click Controller");

    // Create click controller
    ClickController cntr = new ClickController(frame);
    //frame.getContentPane().add(cntr, BorderLayout.CENTER);

    // create table panel
    GridBagLayout layout = new GridBagLayout();// added
    JPanel panel = new JPanel(layout); // added
    panel.setOpaque(true);
    frame.setContentPane(panel);

    // create the ButtonHandler that is used by multiple
    ButtonHandler buttonHandler = cntr.new ButtonHandler();

    // create the pauseButton
    pauseButton = new JButton("Resume", resumeIcon);
    pauseButton.setSize(new Dimension(1,1));
    pauseButton.addActionListener(buttonHandler);

    // create the untilButton
    untilButton = new JButton("Until", untilIcon);
    untilButton.setSize(new Dimension(1,1));
    //untilButton.setEnabled(false);
    untilButton.addActionListener(buttonHandler);

    timeLabel = new JLabel();
    timeLabel.setSize(new Dimension(1,1));

    // add the components to the JPanel
    GridBagConstraints constraints = new GridBagConstraints();
    // constraints for the scrollPane
    constraints.fill    = GridBagConstraints.BOTH;
    constraints.anchor  = GridBagConstraints.NORTH;
    constraints.weightx = 100;
    constraints.weighty = 100;
    // add the scrollPane
    addComponent(panel, cntr, constraints, 0, 1, 1, 1);
    // constraints for the pauseButton
    constraints.fill    = GridBagConstraints.NONE;
    constraints.anchor  = GridBagConstraints.WEST;
    constraints.weightx = 0;
    constraints.weighty = 0;
    // add the pauseButton
    addComponent(panel, pauseButton,constraints,0,0,1,1);
    // constraints for the untilButton
    constraints.fill    = GridBagConstraints.NONE;
    constraints.anchor  = GridBagConstraints.EAST;
    constraints.weightx = 0;
    constraints.weighty = 0;
    addComponent(panel, untilButton,constraints,0,0,1,1);
    // constraints for the timeLabel
    constraints.fill    = GridBagConstraints.NONE;
    constraints.anchor  = GridBagConstraints.CENTER;
    constraints.weightx = 0;
    constraints.weighty = 0;
    addComponent(panel, timeLabel,constraints,0,0,1,1);


    frame.addWindowListener(new CloseWindowAdapter(cntr));
    cntr.enableClose();
    frame.pack();
    frame.setVisible(true);
    return cntr;
  }

  /**
   * helper method.  Adds a component with layout constraints to the gui JPanel.
   *
   * @param c Component The component to be added.
   * @param constraints GridBagConstraints The constraints for the GridBagLayout manager.
   * @param x int The GridBagConstraints' gridx field.
   * @param y int The GridBagConstraints' gridy field.
   * @param w int The GridBagConstraints' gridwidth field.
   * @param h int The GridBagConstraints' gridheight field.
   */
  static private void addComponent(JPanel panel, Component c, GridBagConstraints constraints, int x, int y, int w, int h)
  {
    constraints.gridx      = x;
    constraints.gridy      = y;
    constraints.gridwidth  = w;
    constraints.gridheight = h;
    panel.add(c,constraints);
  }

  /**
   * Getter method.
   * @return JButton
   */
  public static JButton getPauseButton()
  {
    return pauseButton;
  }

  /**
   * Getter method.
   * @return JButton
   */
  public static JButton getUntilButton()
  {
    return untilButton;
  }

  public void connectTo(String hostname, String portname) {
    ControlSocket cs = null;
    String statusLine = "Not connected";

    try {
      InetAddress host_inet = InetAddress.getByName(hostname);
      int port = Integer.parseInt(portname);
      cs = new ControlSocket();
      cs.connect(host_inet, port);
      statusLine = "Connected to " + hostname + ":" + port;
    } catch (java.net.UnknownHostException ex) {
      statusLine = "Connection error: no such host `" + hostname + "'";
    } catch (NumberFormatException ex) {
      statusLine = "Connection error: port number not an integer";
    } catch (Throwable ex) {
      statusLine = "Connection error: " + ex.getMessage();
    }

    _listControllers.add(cs);
    setControlSocket(_listControllers, ALL);
    enableClose();
    setStatusLine(statusLine);
  }

  public void addSimNode(ClickAdapter adapter) {
    assert (null != _listControllers);
    _listControllers.add(new ClickInterfaceSimImpl(adapter));
    setControlSocket(_listControllers, ALL);
    enableClose();
  }

  /**
   * Prompts the user for the number of seconds to unpause the gui, then unpauses it.  After the user-entered time,
   * pauses the gui.
   */
  public void untilTimer()
  {
    try {
      // Prompt the user for the number of seconds to run the simulation.
      String s = (String)JOptionPane.showInputDialog(_frame,
      "Enter point in time to pause simulation: ");
      if (null == s)
        return;
      double  numSeconds   = Double.parseDouble(s);

      untilTime = (long)(numSeconds * (double)Constants.SECOND);
      // Schedule a task to Resume the GuiLog immediately
      Timer timerStart = new Timer();
      timerStart.schedule(new UntilTask("Resume"),0);
    }
    catch (NumberFormatException e) {
      // pass
    }
  }


  /**
   * Sets paused to true.
   */
  public void pause()
  {
    paused = true;
  }

  /**
   * Sets paused to false and notifies all methods that are synchronized on pauseLock.
   */
  public void resume()
  {
    paused = false;
    synchronized(pauselock)
    {
      pauselock.notify();
    }
  }

  /**
   * Adds an Event to the GUI.
   *
   * @param id Event
   * @param parent Event
   */
  public void add(jist.runtime.Event id, jist.runtime.Event parent) {
  }

  public void execute(jist.runtime.Event id) {
    if (untilTime <= id.time) {
      untilTime = Long.MAX_VALUE;
      pause();
    }

    // If the GUI is paused, wait.
    checkLock(id.time);
  }

  /**
   * Deletes an Event from the GUI.
   *
   * @param id Event
   */
  public void del(jist.runtime.Event id) {
  }

  /**
   * If not stepping through one Event, checkLock attempts to acquire a lock on pauseLock.  If it is successful,
   * the method returns; otherwise, it waits.
   * If stepping through one Event, then checkLock returns once so that one Event can be added.
   */
  public void checkLock(long time)
  {
    synchronized(pauselock)
    {
      // If not stepping one Event, proceed as normally and attempt to acquire a lock
      while(paused)
      {
        try
        {
          updateUI(true, time);
          pauselock.wait();
          updateUI(false, time);
        }
        catch(InterruptedException exception)
        {
          System.out.println("InterruptedException: " + exception.toString());
        }
      }
    }
  }

  private void updateUI(boolean paused, long time) {

    if(paused)
    {
      // Change the Pause button to the Resume button.
      pauseButton.setText("Resume");
      pauseButton.setIcon(resumeIcon);
      // Enable the Step and Until button.
      untilButton.setEnabled(true);

      double dtime = (double)time / (double)Constants.SECOND;
      timeLabel.setText("SimTime: " + dtime);
    }
    else
    {
      // Change the Resume button to the Pause button.
      pauseButton.setText("Pause");
      pauseButton.setIcon(pauseIcon);
      // Disable the Step and Until button.
      untilButton.setEnabled(false);

      timeLabel.setText("running...");
    }
  }

}
