/* Copyright (C) 2022 Free Software Foundation

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses>.  */

package org.gprofng.mpmt.collect;

import org.gprofng.mpmt.AnChooser;
import org.gprofng.mpmt.AnLocale;
import org.gprofng.mpmt.AnVariable;
import org.gprofng.mpmt.AnWindow;
import org.gprofng.mpmt.Analyzer;
import org.gprofng.mpmt.KeyboardShortcuts;
import org.gprofng.mpmt.PsParser;
import org.gprofng.mpmt.util.gui.AnUtility;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.accessibility.AccessibleContext;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.table.DefaultTableModel;

/** Creates first page for Collector GUI to profile a running application */
public class CollectPanel2 extends JPanel {

  private final String STR_ACTION_UPDATE = AnLocale.getString("Refresh");
  private final String STR_ACTION_SORTBY = AnLocale.getString("Sort by");
  private final String STR_ACTION_COPY_ALL = AnLocale.getString("Copy All");

  private JPanel workPanel;
  private AnWindow anWindow;
  private AnMenuListener menuListener;
  private String processID;
  private String[] tableColumns = new String[6];

  /*
   * Creates new form CollectPanel2
   */
  public CollectPanel2(final AnWindow aWindow) {
    workPanel = this;
    this.anWindow = aWindow;
    initComponents();
    postInitComponents();
  }

  /**
   * This method is called from within the constructor to initialize the form. WARNING: Do NOT
   * modify this code. The content of this method is always regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {
    java.awt.GridBagConstraints gridBagConstraints;

    jPanel2 = new javax.swing.JPanel();
    jLabel2 = new javax.swing.JLabel();
    jLabel1 = new javax.swing.JLabel();
    jComboBox1 = new javax.swing.JComboBox();
    jButton1 = new javax.swing.JButton();
    jScrollPane2 = new javax.swing.JScrollPane();
    processesTable = new javax.swing.JTable();
    jPanel3 = new javax.swing.JPanel();
    jTextField1 = new javax.swing.JTextField();
    jLabel3 = new javax.swing.JLabel();
    jComboBox2 = new javax.swing.JComboBox();
    jLabel4 = new javax.swing.JLabel();
    jComboBox3 = new javax.swing.JComboBox();
    jLabel5 = new javax.swing.JLabel();
    jComboBox4 = new javax.swing.JComboBox();
    jLabel6 = new javax.swing.JLabel();
    jTextField2 = new javax.swing.JTextField();
    jLabel7 = new javax.swing.JLabel();
    jButton2 = new javax.swing.JButton();
    jButton6 = new javax.swing.JButton();

    setLayout(new java.awt.GridBagLayout());

    jPanel2.setLayout(new java.awt.GridBagLayout());

    jLabel2.setLabelFor(processesTable);
    jLabel2.setText("Select the Process to profile.");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    jPanel2.add(jLabel2, gridBagConstraints);

    jLabel1.setLabelFor(jComboBox1);
    jLabel1.setText("Filter:");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.insets = new java.awt.Insets(12, 0, 0, 0);
    jPanel2.add(jLabel1, gridBagConstraints);

    jComboBox1.setEditable(true);
    jComboBox1.setModel(new javax.swing.DefaultComboBoxModel(new String[] { " " }));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
    gridBagConstraints.insets = new java.awt.Insets(12, 4, 0, 0);
    jPanel2.add(jComboBox1, gridBagConstraints);

    jButton1.setText("Refresh");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 2;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
    gridBagConstraints.insets = new java.awt.Insets(12, 4, 0, 0);
    jPanel2.add(jButton1, gridBagConstraints);

    processesTable.setAutoCreateRowSorter(true);
    processesTable.setModel(new javax.swing.table.DefaultTableModel(
      new Object [][] {
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null},
        {null, null, null, null, null, null}
      },
      new String [] {
        "User Name", "PID", "PPID", "Start Time", "CPU time", "Command"
      }
    ) {
      Class[] types = new Class [] {
        java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class
      };
      boolean[] canEdit = new boolean [] {
        false, false, false, false, false, false
      };

      public Class getColumnClass(int columnIndex) {
        return types [columnIndex];
      }

      public boolean isCellEditable(int rowIndex, int columnIndex) {
        return canEdit [columnIndex];
      }
    });
    processesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
    jScrollPane2.setViewportView(processesTable);
    processesTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 3;
    gridBagConstraints.gridwidth = 4;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(4, 0, 0, 0);
    jPanel2.add(jScrollPane2, gridBagConstraints);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(12, 12, 0, 12);
    add(jPanel2, gridBagConstraints);

    jPanel3.setLayout(new java.awt.GridBagLayout());

    jTextField1.setColumns(12);
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
    jPanel3.add(jTextField1, gridBagConstraints);

    jLabel3.setLabelFor(jTextField1);
    jLabel3.setText("Target PID:");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    jPanel3.add(jLabel3, gridBagConstraints);

    jComboBox2.setEditable(true);
    jComboBox2.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "test.1.er" }));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.gridwidth = 3;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(6, 4, 0, 0);
    jPanel3.add(jComboBox2, gridBagConstraints);

    jLabel4.setLabelFor(jComboBox2);
    jLabel4.setText("Experiment Name:");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
    jPanel3.add(jLabel4, gridBagConstraints);

    jComboBox3.setEditable(true);
    jComboBox3.setModel(new javax.swing.DefaultComboBoxModel(new String[] { " " }));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 2;
    gridBagConstraints.gridwidth = 3;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(6, 4, 0, 0);
    jPanel3.add(jComboBox3, gridBagConstraints);

    jLabel5.setLabelFor(jComboBox3);
    jLabel5.setText("Experiment Directory:");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 2;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
    jPanel3.add(jLabel5, gridBagConstraints);

    jComboBox4.setEditable(true);
    jComboBox4.setModel(new javax.swing.DefaultComboBoxModel(new String[] { " " }));
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 3;
    gridBagConstraints.gridwidth = 3;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(6, 4, 0, 0);
    jPanel3.add(jComboBox4, gridBagConstraints);

    jLabel6.setLabelFor(jComboBox4);
    jLabel6.setText("Experiment Group:");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 3;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
    jPanel3.add(jLabel6, gridBagConstraints);

    jTextField2.setEditable(false);
    jTextField2.setText("gp-display-text");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 3;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.gridwidth = 4;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
    jPanel3.add(jTextField2, gridBagConstraints);

    jLabel7.setLabelFor(jTextField2);
    jLabel7.setText("Target Name:");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 2;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    gridBagConstraints.insets = new java.awt.Insets(0, 8, 0, 0);
    jPanel3.add(jLabel7, gridBagConstraints);

    jButton2.setText("...");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 5;
    gridBagConstraints.gridy = 2;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
    gridBagConstraints.insets = new java.awt.Insets(6, 4, 0, 0);
    jPanel3.add(jButton2, gridBagConstraints);

    jButton6.setText("...");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 5;
    gridBagConstraints.gridy = 3;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
    gridBagConstraints.insets = new java.awt.Insets(6, 4, 0, 0);
    jPanel3.add(jButton6, gridBagConstraints);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTH;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(12, 12, 12, 12);
    add(jPanel3, gridBagConstraints);
  }// </editor-fold>//GEN-END:initComponents

  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton jButton1;
  private javax.swing.JButton jButton2;
  private javax.swing.JButton jButton6;
  private javax.swing.JComboBox jComboBox1;
  private javax.swing.JComboBox jComboBox2;
  private javax.swing.JComboBox jComboBox3;
  private javax.swing.JComboBox jComboBox4;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JLabel jLabel2;
  private javax.swing.JLabel jLabel3;
  private javax.swing.JLabel jLabel4;
  private javax.swing.JLabel jLabel5;
  private javax.swing.JLabel jLabel6;
  private javax.swing.JLabel jLabel7;
  private javax.swing.JPanel jPanel2;
  private javax.swing.JPanel jPanel3;
  private javax.swing.JScrollPane jScrollPane2;
  private javax.swing.JTextField jTextField1;
  private javax.swing.JTextField jTextField2;
  private javax.swing.JTable processesTable;
  // End of variables declaration//GEN-END:variables

  /** Initialize combo boxes with default values */
  public void postInitComponents() {
    AnUtility.setTextAndAccessibleContext(
        jLabel2, AnLocale.getString("Select the Process to profile."));
    AnUtility.setTextAndAccessibleContext(jLabel1, AnLocale.getString("Filter:"));
    jLabel1.setDisplayedMnemonic(AnLocale.getString('F', "MN_PROFILE_RUNNING_PROCESS_Filter"));
    jLabel1.setLabelFor(jComboBox1);
    AnUtility.setTextAndAccessibleContext(jLabel3, AnLocale.getString("Target PID:"));
    AnUtility.setTextAndAccessibleContext(jLabel4, AnLocale.getString("Experiment Name:"));
    AnUtility.setTextAndAccessibleContext(jLabel5, AnLocale.getString("Experiment Directory:"));
    AnUtility.setTextAndAccessibleContext(jLabel6, AnLocale.getString("Experiment Group:"));
    AnUtility.setTextAndAccessibleContext(jLabel7, AnLocale.getString("Target Name:"));
    AnUtility.setTextAndAccessibleContext(jButton1, AnLocale.getString("Refresh"));
    jButton1.setMnemonic(AnLocale.getString('R', "MN_PROFILE_RUNNING_PROCESS_Refresh"));
    AnUtility.setAccessibleContext(jButton2.getAccessibleContext(), AnLocale.getString("Browse"));
    AnUtility.setAccessibleContext(jButton6.getAccessibleContext(), AnLocale.getString("Browse"));
    AnUtility.setAccessibleContext(
        processesTable.getTableHeader().getAccessibleContext(),
        AnLocale.getString("Process Table"));

    processesTable.getSelectionModel().addListSelectionListener(new SelectionHandler());
    jComboBox1.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
    jComboBox1.setSelectedItem(""); // Filter
    String user = System.getProperty("user.name");
    jComboBox1.addItem(user); // Filter
    jComboBox2.setSelectedItem("test.1.er"); // Experiment name
    // jComboBox2.addItem(empty_str); // Experiment name
    jComboBox3.setSelectedItem(""); // Experiment directory
    // jComboBox3.addItem(empty_str); // Experiment directory
    jComboBox4.setSelectedItem(""); // Experiment group
    // jComboBox4.addItem(empty_str); // Experiment group
    jButton1.addActionListener(new RefreshActionHandler()); // Refresh
    jButton2.addActionListener(new ExpDirActionHandler()); // Experiment directory browser
    jButton6.addActionListener(new ExpGroupActionHandler()); // Experiment group browser
    // Init column names
    tableColumns[0] = AnLocale.getString("User Name") + " (UID)";
    tableColumns[1] = AnLocale.getString("Process ID") + " (PID)";
    tableColumns[2] = AnLocale.getString("Parent Process ID") + " (PPID)";
    tableColumns[3] = AnLocale.getString("Start Time") + " (START TIME)";
    tableColumns[4] = AnLocale.getString("CPU Time") + " (CPU TIME)";
    tableColumns[5] = AnLocale.getString("Process name") + " (COMMAND)";

    // Popup menu
    menuListener = new AnMenuListener(this, processesTable);
    processesTable.addMouseListener(menuListener);
    KeyStroke ks = KeyboardShortcuts.contextMenuActionShortcut;
    processesTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(ks, ks);
    processesTable
        .getActionMap()
        .put(
            ks,
            new AbstractAction() {
              @Override
              public void actionPerformed(ActionEvent ev) {
                JPopupMenu popup = initPopup(processesTable);
                if (popup != null) {
                  JTable src = (JTable) ev.getSource();
                  Rectangle cellRect, visRect;

                  visRect = src.getVisibleRect();
                  cellRect = visRect;
                  if (cellRect.width > 0) {
                    // calculate a good place to show the menu
                    cellRect.x += cellRect.width / 4;
                  }
                  if (cellRect.height > 0) {
                    cellRect.y += cellRect.height / 4;
                  }
                  popup.show(src, cellRect.x, cellRect.y);
                }
              }
            });
    jTextField2.setColumns(50);
  }

  /** Copy all lines to the system clipboard */
  protected void copyAll() {
    String text = "";
    int rows = processesTable.getRowCount();
    int columns = processesTable.getColumnCount();
    int collen[] = new int[columns];
    // Print table header
    for (int j = 0; j < columns; j++) {
      if (tableColumns.length > j) {
        String s = tableColumns[j];
        text += s;
        collen[j] = s.length();
      }
      if (columns - 1 == j) {
        break;
      }
      text += " | ";
    }
    text += "\n";
    // Print table rows
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < columns; j++) {
        String s = processesTable.getValueAt(i, j).toString();
        text += s;
        // Try to format the string
        if (columns - 1 == j) {
          break;
        }
        int k = collen[j] - s.length();
        while (k > 0) {
          text += " ";
          k--;
        }
        text += " | ";
      }
      text += "\n";
    }
    // copyToClipboard(text);
    StringSelection data = new StringSelection(text);
    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    clipboard.setContents(data, data);
  }

  public void update() {
    AnUtility.dispatchOnSwingThread(
        new Runnable() {
          @Override
          public void run() {
            updateTable();
            updateSelection();
            updateTextFields();
          }
        });
  }

  private void updateTable() {
    AnUtility.checkIfOnAWTThread(true);
    final String f = "/bin/ps -ef";
    AnUtility.checkIPCOnWrongThread(false);
    String p = Collector.getRunningProcesses(f); // IPC call
    String o = Collector.getOSFamily(); // IPC call
    AnUtility.checkIPCOnWrongThread(true);
    PsParser psParser = PsParser.getDefault(o);
    psParser.setPsOutput(p);
    List<String> th = psParser.getData(true).header(); // Headers
    // Customize column names
    for (int i = 0; i < th.size(); i++) {
      String s = th.get(i);
      if ("STIME".equals(s)) {
        th.set(i, "START TIME");
      }
      if ("TIME".equals(s)) {
        th.set(i, "CPU TIME");
      }
      if ("CMD".equals(s)) {
        th.set(i, "COMMAND");
      }
    }
    java.util.regex.Pattern re = java.util.regex.Pattern.compile(".");
    String filter = getFilter();
    if (!"".equals(filter)) {
      re = java.util.regex.Pattern.compile(".*" + filter + ".*");
    }
    List<List<String>> tp = psParser.getData(true).processes(re); // Processes
    // Convert List<List<String>> to Object[][]....
    List<Object[]> tpList = new ArrayList<Object[]>();
    for (List<String> object : tp) {
      tpList.add(object.toArray());
    }
    Object[][] tpArray = new Object[tpList.size()][];
    int index = 0;
    for (Object[] x : tpList) {
      tpArray[index++] = x;
    }
    ((DefaultTableModel) processesTable.getModel()).setDataVector(tpArray, th.toArray());

    // Set coulumn widths (available space goes to last column)
    for (int i = 0; i < processesTable.getColumnCount() - 1; i++) {
      processesTable.getColumnModel().getColumn(i).setPreferredWidth(80);
      processesTable.getColumnModel().getColumn(i).setMaxWidth(300);
    }
  }

  /**
   * Get Filter
   *
   * @return String filter
   */
  private String getFilter() {
    String filter = "";
    if (null != jComboBox1.getSelectedItem()) {
      filter = jComboBox1.getSelectedItem().toString();
    }
    return filter;
  }

  /**
   * Get Process ID
   *
   * @return String PID
   */
  public String getProcessID() {
    String PID = "";
    processID = jTextField1.getText();
    if (null != processID) {
      PID = processID;
    }
    return PID;
  }

  /**
   * Get Experiment Directory
   *
   * @return String directory
   */
  public String getExperimentDirectory() {
    String expdir = "";
    if (null != jComboBox3.getSelectedItem()) {
      expdir = jComboBox3.getSelectedItem().toString();
    }
    if (expdir.length() < 1) { // XXX should it be here?
      expdir = CGetCurDir(); // IPC call
    }
    return expdir;
  }

  /**
   * Set Experiment Directory
   *
   * @param expdir
   */
  public void setExperimentDirectory(String expdir) {
    jComboBox3.setSelectedItem(expdir);
  }

  /**
   * Get Experiment Name
   *
   * @return String directory
   */
  public String getExperimentName() {
    String expdir = "";
    if (null != jComboBox2.getSelectedItem()) {
      expdir = jComboBox2.getSelectedItem().toString();
    }
    return expdir;
  }

  /**
   * Get Experiment Group
   *
   * @return String group
   */
  public String getExperimentGroup() {
    String group = "";
    if (null != jComboBox4.getSelectedItem()) {
      group = jComboBox4.getSelectedItem().toString();
    }
    return group;
  }

  /**
   * Set Experiment Group
   *
   * @param expdir
   */
  public void setExperimentGroup(String group) {
    jComboBox4.setSelectedItem(group);
  }

  /**
   * Get Current Directory
   *
   * @return
   */
  private String CGetCurDir() {
    return Analyzer.getInstance().getWorkingDirectory();
  }

  /**
   * Get Preview Command
   *
   * @return String command
   */
  public String getPreviewCommand() {
    String col_cmd = "collect";
    col_cmd += " -P " + getProcessID(); // jTextField1.getText();
    col_cmd += " -d " + getExperimentDirectory(); // jComboBox3.getItemAt(0);
    col_cmd += " -o " + getExperimentName(); // jComboBox2.getItemAt(0);
    col_cmd += " -g " + getExperimentGroup(); // jComboBox4.getItemAt(0);
    System.out.println(col_cmd); // DEBUG
    return col_cmd;
  }

  private void actionPerformed2(final ActionEvent event) {
    int i;
    final String cmd = event.getActionCommand();
    final String empty = "";
    final String slash = "/";

    if (cmd.equals(empty)) {
      int theState = event.getID(); // state change event

      if (theState == Collector.COLLECTING_RUNNING) {
        // if (buttons[0].isEnabled()) { // the collection has just started
        // writeln(AnLocale.getString("Running: ") + new File(target.getText()).getName(),
        // getOutLog());
        //    set_runtime_buttons();
        // } else { // the collection has been resumed
        // writeln(AnLocale.getString("Data collection resumed"), getOutLog());
        // writeln("(" + AnLocale.getString("process id ") + m_collector.getProcessPID() + ")",
        // getOutLog());
        //    buttons[2].setText(AnLocale.getString("Pause"));
        //    buttons[2].setActionCommand(AnLocale.getString("Pause"));
        // }
      } else if (theState == Collector.COLLECTING_PAUSED) {
        // writeln(AnLocale.getString("Data collection paused"), getOutLog());
        // writeln("(" + AnLocale.getString("process id ") + m_collector.getProcessPID() + ")",
        // getOutLog());
        //    buttons[2].setText(AnLocale.getString("Resume"));
        //    buttons[2].setActionCommand(AnLocale.getString("Resume"));

      } else if (theState == Collector.COLLECTING_TERMINATING) {
        // writeln(AnLocale.getString("Data collection terminating"), getOutLog());

      } else if ((theState == Collector.COLLECTING_TERMINATED)
          || (theState == Collector.COLLECTING_COMPLETED)) {

        if (theState == Collector.COLLECTING_TERMINATED) {
          // writeln(AnLocale.getString("Data collection terminated"), getOutLog());
        }

        // NM if (output_thread != null) {
        // NM     Thread stopped = output_thread;
        // NM     output_thread=null;
        // NM     stopped.interrupt();
        // NM }
        // writeln(AnLocale.getString("Process ID: ") + m_collector.getProcessPID(), getOutLog());
        // writeln(AnLocale.getString("Elapsed Time: ") + m_collector.getElapsedTime() + " ms",
        // getOutLog());
        // writeln(AnLocale.getString("Execution completed, exit status is ") +
        //    m_collector.getProcessExitValue(), getOutLog());
        // final String actual_expname = m_collector.getActualExpName();
        // getBtClear().setEnabled(true);
        // resetButtons();
        // exp_name.setValue(m_collector.getNextExpName());
      }

    } else if (cmd.equals(AnLocale.getString("Refresh"))) {
      if (anWindow != null) {
        update();
      } else { // TEST
        refreshPS(processesTable);
        updateSelection();
        updateTextFields();
      }
    } else if (cmd.equals(AnLocale.getString("Pause"))) {
      // if (m_collector.pause(AnUtility.getSignalValue(pause_sig.getValue()))) {
      // }
      //    tab_pane.setSelectedIndex(2);
    } else if (cmd.equals(AnLocale.getString("Resume"))) {
      // if (m_collector.resume(AnUtility.getSignalValue(pause_sig.getValue()))) {
      // }
      //    tab_pane.setSelectedIndex(2);
    } else if (cmd.equals(AnLocale.getString("Sample"))) {
      // if (m_collector.sample(AnUtility.getSignalValue(sample_sig.getValue()))) {
      //    writeln(AnLocale.getString("Manual sample"), getOutLog());
      // }
      //    tab_pane.setSelectedIndex(2);
    } else if (cmd.equals(AnLocale.getString("Terminate"))) {
    } else if (cmd.equals(AnLocale.getString("Preview Command:"))) {
      System.out.println(getPreviewCommand());
    } else if (cmd.equals(AnLocale.getString("Cancel"))) {
    } else {
      // showChooser(cmd);
    }
  }

  private void updateSelection() {
    AnUtility.checkIfOnAWTThread(true);
    String last_pid = processID; // jTextField1.getText();
    if ((last_pid != null) && (last_pid.length() > 0)) {
      // Check if selection is still correct
      int row = processesTable.getSelectedRow();
      int count = processesTable.getRowCount();
      if (count < row) {
        row = -1;
      }
      if (row >= 0) {
        String s = processesTable.getModel().getValueAt(row, 1).toString();
        if (!last_pid.equals(s)) {
          row = -1;
        }
      }
      if (row < 0) {
        // Find last_pid in the table
        for (row = 0; row < count; row++) {
          String s = processesTable.getModel().getValueAt(row, 1).toString();
          if (last_pid.equals(s)) {
            if (row != processesTable.getSelectedRow()) {
              processesTable.setRowSelectionInterval(row, row);
            }
            return;
          }
        }
        // Not found - select first row
        row = 0;
      }
      // New selection
      if (row < count) {
        processesTable.setRowSelectionInterval(row, row);
        return;
      }
    }
    if (null == processID) { // first time
      processesTable.clearSelection();
    } else {
      int count = processesTable.getRowCount();
      if (count > 0) {
        // select first row
        int row = 0;
        processesTable.setRowSelectionInterval(row, row);
      }
    }
  }

  private void updateTextFields() {
    AnUtility.checkIfOnAWTThread(true);
    int rows = processesTable.getRowCount();
    if (rows <= 0) {
      return;
    }
    int row = processesTable.getSelectedRow();
    if (row < 0) {
      row = 0;
    }
    if (rows < row) {
      row = 0;
    }
    int lastCol = processesTable.getColumnCount() - 1;
    if (lastCol > 0) {
      // Get process ID
      String p = processesTable.getValueAt(row, 1).toString();
      jTextField1.setText(p); // Process ID
      processID = p;
      // Get process name
      String s = processesTable.getValueAt(row, lastCol).toString();
      int spaceind = s.indexOf(' ');
      if (spaceind > 0) {
        s = s.substring(0, spaceind);
      }
      jTextField2.setText(s); // Process name
    }
  }

  private final class SelectionHandler implements ListSelectionListener {

    @Override
    public void valueChanged(ListSelectionEvent evt) {
      updateTextFields();
    }
  }

  /** Refresh Action handler */
  private final class RefreshActionHandler implements ActionListener {

    @Override
    public void actionPerformed(final ActionEvent event) {
      processID = jTextField1.getText();
      update();
    }
  }

  /**
   * "Choose Experiment Directory" action handler Call file chooser and update Experiment Directory
   */
  private final class ExpDirActionHandler implements ActionListener {

    AnChooser ac = null;
    String dir = null;

    @Override
    public void actionPerformed(final ActionEvent event) {
      String title = AnLocale.getString("Experiment Directory");
      if (null == ac) {
        ac = anWindow.getAnChooser(title, AnChooser.DIR_CHOOSER, dir);
      }
      if ((ac.showOpenDialog(workPanel) == AnChooser.APPROVE_OPTION)
          && ((ac.getSelectedFile()) != null)) {
        dir = ac.getSelectedFile().getAbsolutePath();
        setExperimentDirectory(dir);
      }
    }
  }

  /** "Choose Experiment Group" action handler Call file chooser and update Experiment Group */
  private final class ExpGroupActionHandler implements ActionListener {

    AnChooser ac = null;
    String dir = null;

    @Override
    public void actionPerformed(final ActionEvent event) {
      String title = AnLocale.getString("Experiment Group");
      if (null == ac) {
        ac = anWindow.getAnChooser(title, AnChooser.EXP_GROUP_CHOOSER, dir);
      }
      if ((ac.showOpenDialog(workPanel) == AnChooser.APPROVE_OPTION)
          && ((ac.getSelectedFile()) != null)) {
        dir = ac.getSelectedFile().getParent();
        String group = ac.getSelectedFile().getName();
        setExperimentGroup(group);
      }
    }
  }

  // ================================= UNIT TESTING ===================================== //
  // For unit testing
  public static void main(final String[] args) {

    final int size;
    String str_args;

    if (args != null) {
      size = args.length;
      if (size > 0) {
        str_args = "";
        for (int i = 1; i < size; i++) {
          if (i != 1) {
            str_args += " ";
          }
          str_args += args[i];
        }
      }
    }
    CollectPanel2 cp = new CollectPanel2(null);
    cp.Test(cp);
  }

  public void Test(CollectPanel2 cp) {
    final JFrame mframe = new JFrame();
    // CollectPanel2 cp = new CollectPanel2();
    cp.jButton1.addActionListener(new ActionHandler());
    // cp.jButton5.addActionListener(new ActionHandler()); // Preview
    JScrollPane work_pane = new JScrollPane(cp);
    work_pane.setMinimumSize(new Dimension(AnVariable.WIN_SIZE));
    mframe.add(work_pane);
    processesTable.setColumnSelectionAllowed(false);
    processesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    processesTable.getSelectionModel().addListSelectionListener(new SelectionHandler());
    jTextField1.setText("25457");
    refreshPS(processesTable);
    updateSelection();
    processesTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
    // jTable1.setAutoResizeMode(Table.AUTO_RESIZE_LAST_COLUMN);
    processesTable.doLayout();
    mframe.pack();
    mframe.setVisible(true);
  }

  // Action handler
  private final class ActionHandler implements ActionListener {

    @Override
    public void actionPerformed(final ActionEvent event) {
      if (anWindow == null) { // TEST
        refreshPS(processesTable);
      }
      actionPerformed2(event);
    }
  }

  private static String[] ps = {
    "tester   883   882   Nov_09      35:55 /usr/lib/ssh/sshd",
    "tester  1022  1012   Nov_09       0:00 -csh",
    "tester   327 23523   16:18:05     0:00 ps -aef",
    "tester 27385 27384   Dec_04       0:05 /usr/lib/ssh/sshd",
    "tester 11582   893   Nov_20       0:00 /bin/sh /bin/firefox",
    "tester  4664  2140   Nov_28       0:00 vertool"
  };

  static boolean resized = false;

  // Window handler
  private void refreshPS(JTable t) {
    int ps_index = 0;
    String empty_str = "";
    String filter = empty_str;
    if (null != jComboBox1.getSelectedItem()) {
      filter = jComboBox1.getSelectedItem().toString();
    }
    for (int i = 0; i < t.getRowCount(); i++) {
      char space = ' ';
      String s1, s2, s3, s4, s5, s6;
      String s = empty_str;
      if ((i < ps.length) && (ps_index < ps.length)) {
        for (; ps_index < ps.length; ps_index++) {
          s1 = ps[ps_index];
          if (filter.length() > 0) {
            // Apply filter
            if (s1.indexOf(filter) < 0) {
              continue;
            }
          }
          s = s1;
          ps_index++;
          break;
        }
      }
      if (s.length() <= 0) {
        t.setValueAt(s, i, 0);
        t.setValueAt(s, i, 1);
        t.setValueAt(s, i, 2);
        t.setValueAt(s, i, 3);
        t.setValueAt(s, i, 4);
        t.setValueAt(s, i, 5);
        continue;
      }
      int p1 = 0;
      int p2 = s.indexOf(space);
      // user name
      s1 = s.substring(p1, p2);
      t.setValueAt(s1, i, 0);
      while (s.charAt(p2) == space) {
        p2++;
      }
      p1 = p2;
      p2 = s.indexOf(space, p1);
      // PID
      s2 = s.substring(p1, p2);
      t.setValueAt(s2, i, 1);
      while (s.charAt(p2) == space) {
        p2++;
      }
      p1 = p2;
      p2 = s.indexOf(space, p1);
      // PPID
      s3 = s.substring(p1, p2);
      t.setValueAt(s3, i, 2);
      while (s.charAt(p2) == space) {
        p2++;
      }
      p1 = p2;
      p2 = s.indexOf(space, p1);
      // Start time
      s4 = s.substring(p1, p2);
      t.setValueAt(s4, i, 3);
      while (s.charAt(p2) == space) {
        p2++;
      }
      p1 = p2;
      p2 = s.indexOf(space, p1);
      // CPU time
      s5 = s.substring(p1, p2);
      t.setValueAt(s5, i, 4);
      while (s.charAt(p2) == space) {
        p2++;
      }
      p1 = p2;
      p2 = s.length();
      // Command
      s6 = s.substring(p1, p2);
      t.setValueAt(s6, i, 5);
    }
    //        if (!resized) {
    //            resized = true;
    //            // Resize jTable1
    //            TableColumnModel tcm = t.getColumnModel();
    //            TableColumn tc = tcm.getColumn(0);
    //            int w = tc.getWidth();
    //            tc.setPreferredWidth((w * 2) / 3);
    //            tc.setWidth((w * 2) / 3);
    //            tc = tcm.getColumn(1);
    //            w = tc.getWidth();
    //            tc.setPreferredWidth(w / 2);
    //            tc.setWidth(w / 2);
    //            tc = tcm.getColumn(2);
    //            w = tc.getWidth();
    //            tc.setPreferredWidth(w / 2);
    //            tc.setWidth(w / 2);
    //            tc = tcm.getColumn(3);
    //            w = tc.getWidth();
    //            tc.setPreferredWidth(w / 2);
    //            tc.setWidth(w / 2);
    //            tc = tcm.getColumn(4);
    //            w = tc.getWidth();
    //            tc.setPreferredWidth(w / 2);
    //            tc.setWidth(w / 2);
    //            tc = tcm.getColumn(5);
    //            w = tc.getWidth();
    //            tc.setPreferredWidth(w * 4);
    //            tc.setWidth(w * 4);
    //        }
  }

  /*
   * Generic action for context menu items.
   * Action name is passed as String.
   */
  class UpdateAction extends AbstractAction {

    String actionName = null;

    public UpdateAction(String name) {
      super(name);
      this.actionName = name;
    }

    public UpdateAction(String name, String actionName) {
      super(name);
      this.actionName = actionName;
    }

    @Override
    public void actionPerformed(ActionEvent ev) {
      // Use AWT thread to perform some actions
      if (actionName.equals(STR_ACTION_UPDATE)) {
        update();
        return;
      }
      if (actionName.equals(STR_ACTION_COPY_ALL)) {
        copyAll();
        return;
      }
      // if (actionName.startsWith(STR_ACTION_SORTBY)) {
      for (int i = 0; i < tableColumns.length; i++) {
        if (actionName.contains(tableColumns[i])) {
          processesTable.getRowSorter().toggleSortOrder(i);
          return;
        }
      }
      // }
    }
  }

  private JPopupMenu initPopup(JTable table) {
    AccessibleContext ac;
    JMenuItem mi;
    JPopupMenu popup = new JPopupMenu();
    String txt;

    boolean row_selected = false;
    int row = table.getSelectedRow();
    if (row >= 0) {
      row_selected = true;
    }

    // Add "Update List of Processes" action
    txt = STR_ACTION_UPDATE;
    mi = new JMenuItem(new UpdateAction(txt));
    mi.setMnemonic(AnLocale.getString('R', "MN_PROFILE_PROCESS_Refresh_MENU_ITEM"));
    ac = mi.getAccessibleContext();
    ac.setAccessibleDescription(txt);
    mi.setEnabled(true);
    //        String hotkey = "CTRL_U";
    //        KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.CTRL_DOWN_MASK,
    // false);
    //        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(ks, hotkey);
    //        UpdateAction act = new UpdateAction(txt);
    //        table.getActionMap().put(hotkey, act);
    //        mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, ActionEvent.CTRL_MASK));
    mi.setEnabled(true);
    popup.add(mi);

    // Add "Set Metric" action
    txt = STR_ACTION_SORTBY;
    UpdateAction sm = new UpdateAction(txt);
    mi = new JMenuItem(sm);
    ac = mi.getAccessibleContext();
    ac.setAccessibleDescription(txt);
    if (tableColumns.length <= 1) {
      mi.setEnabled(false);
      popup.add(mi);
    } else {
      JMenu submenu1;
      submenu1 = new JMenu(txt);
      for (int i = 0; i < tableColumns.length; i++) {
        txt = tableColumns[i];
        sm = new UpdateAction(txt);
        mi = new JMenuItem(sm);
        ac = mi.getAccessibleContext();
        ac.setAccessibleDescription(STR_ACTION_SORTBY + ":" + txt);
        mi.setEnabled(true);
        submenu1.add(mi);
      }
      popup.add(submenu1);
    }

    // Add separator
    popup.addSeparator();
    // Add menu item "Copy All"
    txt = STR_ACTION_COPY_ALL;
    UpdateAction cs = new UpdateAction(txt);
    mi = new JMenuItem(cs);
    ac = mi.getAccessibleContext();
    ac.setAccessibleDescription(txt);
    popup.add(mi);

    return popup;
  }

  // ------- Private classes to implement popup menu items ------- //
  private class AnMenuListener extends MouseAdapter {

    private boolean debug;
    CollectPanel2 cp;
    JTable ptable;

    AnMenuListener(CollectPanel2 collectPanel, JTable table) {
      this.cp = collectPanel;
      this.ptable = table;
      debug = false;
    }

    // Experimental code, mostly "quick and dirty hack"
    public JPopupMenu initPopup(MouseEvent event) {
      return cp.initPopup(ptable);
    }

    /** Check for double click to performs default action */
    @Override
    public void mouseClicked(final MouseEvent e) {}

    @Override
    public void mousePressed(MouseEvent e) {
      maybeShowPopup(e);
    }

    @Override
    public void mouseReleased(MouseEvent e) {
      maybeShowPopup(e);
    }

    private void maybeShowPopup(MouseEvent e) {
      if (e.isPopupTrigger()) {
        JPopupMenu popup = initPopup(e);
        if (popup != null) {
          popup.show(e.getComponent(), e.getX(), e.getY());
        }
      }
    }

    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
      if (debug) {
        System.out.println("AnMenuListener:popupMenuWillBecomeInvisible(" + e + ")");
      }
    }

    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
      if (debug) {
        System.out.println("AnMenuListener:popupMenuWillBecomeVisible(" + e + ")");
      }
    }

    public void popupMenuCanceled(PopupMenuEvent e) {
      if (debug) {
        System.out.println("AnMenuListener:popupMenuCanceled(" + e + ")");
      }
    }
  }
}
