<commit_before>package kg.apc.jmeter.vizualizers;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.border.Border;
import kg.apc.jmeter.JMeterPluginsUtils;
import kg.apc.jmeter.gui.JAbsrtactDialogPanel;

/**
 *
 * @author <NAME>
 */
public class JPerfmonParamsPanel extends JAbsrtactDialogPanel {

    private final static int OPTION_PRIMARY_METRIC = 1;
    private final static int OPTION_ADDITIONAL_METRIC = 1 << 1;
    private final static int OPTION_PROCESS_SCOPE = 1 << 2;
    private final static int OPTION_CPU_CORE_SCOPE = 1 << 3;
    private final static int OPTION_FILESYSTEM_SCOPE = 1 << 4;
    private final static int OPTION_NET_INTERFACE_SCOPE = 1 << 5;
    private final static int OPTION_EXEC = 1 << 6;
    private final static int OPTION_TAIL = 1 << 7;
    private final static int OPTION_JMX = 1 << 8;
    private final static int OPTION_UNIT = 1 << 9;
    private JTextField parent = null;
    private String type = null;

    private final static String defaultMarker = " (default)";
    private final static String separator = ":";
    private final static String METRIC_CPU = "CPU";
    private final static String METRIC_MEM = "Memory";
    private final static String METRIC_SWAP = "Swap";
    private final static String METRIC_DISKIO = "Disks I/O";
    private final static String METRIC_NETIO = "Network I/O";
    private final static String METRIC_TCP = "TCP";
    private final static String METRIC_EXEC = "EXEC";
    private final static String METRIC_TAIL = "TAIL";
    private final static String METRIC_JMX = "JMX";

    private HashMap<String, Integer> rules = new HashMap<String, Integer>();

    private ArrayList<String> unitRules = new ArrayList<String>();

    //CPU metrics
    private static String[] cpuMetricsPrimary = {
        "combined" + defaultMarker, "Get the combined CPU usage, in percent (%)",
        "idle", "Get the idle CPU usage, in percent (%)",
        "system", "Get the system CPU usage, in percent (%)",
        "user", "Get the user CPU usage, in percent (%)",
        "iowait", "Get the iowait CPU usage, in percent (%)"
    };
    private static String[] cpuProcessMetricsPrimary = {
        "percent" + defaultMarker, "Get the process combined CPU usage, in percent (%)",
        "total", "Get the process total CPU usage, in percent (%)",
        "system", "Get the system CPU usage, in percent (%)",
        "user", "Get the user CPU usage, in percent (%)"
    };
    private static String[] cpuMetricsAdditional = {
        "irq", "Get the irq CPU usage, in percent (%)",
        "nice", "Get the nice CPU usage, in percent (%)",
        "softirq", "Get the softirq CPU usage, in percent (%)",
        "stolen", "Get the stolen CPU usage, in percent (%)",};
    //Memory Metrics
    private static String[] memMetricsPrimary = {
        "usedperc" + defaultMarker, "Relative memory usage, in percent (%)",
        "freeperc", "Relative free memory, in percent (%)",
        "used", "Size of Memory used",
        "free", "Size of Free memory"};
    private static String[] memMetricsAdditional = {
        "actualused", "Size of Actual memory usage",
        "actualfree", "Size of Actual free memory",
        "ram", "Server physical memory in Mb",
        "total", "Size of Total memory"};
    private static String[] memProcessMetricsPrimary = {
        "resident" + defaultMarker, "Size of Process resident memory usage",
        "virtual", "Size of Process virtual memory usage",
        "shared", "Size of Process shared memory usage"
    };
    private static String[] memProcessMetricsAdditional = {
        "pagefaults", "Process page faults count",
        "majorfaults", "Process major faults count",
        "minorfaults", "Process minor faults count"
    };
    //Disk I/O
    private static String[] diskIOMetricsPrimary = {
        "queue" + defaultMarker, "Description to update",
        "reads", "Number of read access",
        "writes", "Number of write access",
        "readbytes", "Number of bytes read",
        "writebytes", "Number of bytes written"
    };
    private static String[] diskIOMetricsAdditional = {
        "available", "Description to update",
        "service", "Description to update",
        "files", "Description to update",
        "free", "Description to update",
        "freefiles", "Description to update",
        "total", "Description to update",
        "useperc", "Description to update",
        "used", "Description to update"
    };
    //Network I/O
    private static String[] netIOMetricsPrimary = {
        "bytesrecv" + defaultMarker, "Number of bytes received",
        "bytessent", "Number of bytes sent",
        "rx", "Description to update",
        "tx", "Description to update"
    };
    private static String[] netIOMetricsAdditional = {
        "used", "Description to update",
        "speed", "Description to update",
        "rxdrops", "Description to update",
        "rxerr", "Description to update",
        "rxframe", "Description to update",
        "rxoverruns", "Description to update",
        "txcarrier", "Description to update",
        "txcollisions", "Description to update",
        "txdrops", "Description to update",
        "txerr", "Description to update",
        "txoverruns", "Description to update"
    };
    //TCP
    private static String[] tcpMetricsPrimary = {
        "estab" + defaultMarker, "Number of established connections",
        "time_wait", "Description to update",
        "close_wait", "Description to update"
    };
    private static String[] tcpMetricsAdditional = {
        "bound", "Description to update",
        "close", "Description to update",
        "closing", "Description to update",
        "fin_wait1", "Description to update",
        "fin_wait2", "Description to update",
        "idle", "Description to update",
        "inbound", "Description to update",
        "last_ack", "Description to update",
        "listen", "Description to update",
        "outbound", "Description to update",
        "syn_recv", "Description to update"
    };
    //SWAP
    private static String[] swapMetricsPrimary = {
        "used" + defaultMarker, "Size of used swap",
        "pagein", "Number of page in",
        "pageout", "Number of page out",
        "free", "Size of free swap",
        "total", "Size of total swap"
    };

    //JMX
    private static String[] jmxMetricsPrimary = {
        "gc-time", "Time spent in garbage collection, milliseconds",
        "memory-usage", "Heap memory used by VM, bytes",
        "memory-committed", "Heap memory committed by VM, bytes",
        "memorypool-usage", "Heap memory pool usage, bytes",
        "memorypool-committed", "Heap memory pool committed size, bytes",
        "class-count", "Loaded class count in VM",
        "compile-time", "Time spent in compilation, milliseconds"
    };
    
    /** Creates new form JPerfmonParamsDialog */
    public JPerfmonParamsPanel(String type, JTextField parentField) {
        this.parent = parentField;
        this.type = type;
        initRules();
        initUnitRules();
        initComponents();
        initMetrics(type);
        showProcessScopePanels();
        makePtqlLink();
        initFields();
    }

    private void addUnitRule(String type, String metric) {
       if(metric.endsWith(defaultMarker)) metric = metric.substring(0, metric.length() - defaultMarker.length());
       unitRules.add(type + metric);
    }

    private void initUnitRules() {
       //TODO: map all metrics...
       //memory
       addUnitRule(METRIC_MEM, memMetricsPrimary[4]);
       addUnitRule(METRIC_MEM, memMetricsPrimary[6]);
       addUnitRule(METRIC_MEM, memMetricsAdditional[0]);
       addUnitRule(METRIC_MEM, memMetricsAdditional[2]);
       addUnitRule(METRIC_MEM, memMetricsAdditional[6]);

       addUnitRule(METRIC_MEM, memProcessMetricsPrimary[0]);
       addUnitRule(METRIC_MEM, memProcessMetricsPrimary[2]);
       addUnitRule(METRIC_MEM, memProcessMetricsPrimary[4]);

       //swap
       addUnitRule(METRIC_SWAP, swapMetricsPrimary[0]);
       addUnitRule(METRIC_SWAP, swapMetricsPrimary[6]);
       addUnitRule(METRIC_SWAP, swapMetricsPrimary[8]);
       //tcp

       //diskio

       //netio
    }

    private boolean isUnitRelevent(String metric) {
       return unitRules.contains(type + metric);
    }

    private void enableUnit(String metric) {
       boolean show = isUnitRelevent(metric);
       jLabelUnit.setEnabled(show);
       jComboBoxUnit.setEnabled(show);
    }

    private String getUnit() {
       int unit = jComboBoxUnit.getSelectedIndex();
       switch(unit) {
          case 1:
             return "kb";
          case 2:
             return "mb";
          default:
             return "b";
       }
    }

    private void setUnit(String unit) {
       if("kb".equalsIgnoreCase(unit)) jComboBoxUnit.setSelectedIndex(1);
       else if("mb".equalsIgnoreCase(unit)) jComboBoxUnit.setSelectedIndex(2);
       else jComboBoxUnit.setSelectedIndex(0);
    }

    //extract exec or tail command (handle label)
    private String extractExecTailCmd(String params) {
        String ret;
        String[] tmp = params.split("(?<!\\\\)" + separator);
        String labelString = null;
        for(int i=0; i<tmp.length; i++) {
            if(tmp[i].startsWith("label=")) {
                labelString = tmp[i];
            }
        }

        if(labelString != null) {
            if(params.startsWith(labelString)) {
                ret = params.substring(labelString.length() + separator.length());
            } else {
                ret = params.substring(0, params.indexOf(labelString) - separator.length());
            }
        } else {
            ret = params;
        }

        return ret;
    }

    private void initFields() {
        String existing = parent.getText();

        //for split, avoid ':' preceeded with '\', ie do not process "\:"
        String[] elements = existing.split("(?<!\\\\)" + separator);

        //check process scope
        if(METRIC_CPU.equals(type) || METRIC_MEM.equals(type)) {
            if(existing.indexOf("pid=") != -1
                    || existing.indexOf("name=") != -1
                    || existing.indexOf("ptql=") != -1) {
                jRadioScopePerProcess.setSelected(true);
                showProcessScopePanels();
            }
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("pid=")) {
                    jRadioPID.setSelected(true);
                    jTextFieldPID.setText(elements[i].substring(4));
                    break;
                } else if(elements[i].startsWith("name=")) {
                    String[] tmp = elements[i].split("#");
                    jRadioProcessName.setSelected(true);
                    jTextFieldPorcessName.setText(tmp[0].substring(5));
                    if(tmp.length == 2) jTextFieldOccurence.setText(tmp[1]);
                    break;
                } else if(elements[i].startsWith("ptql=")) {
                    jRadioPtql.setSelected(true);
                    jTextFieldPtql.setText(elements[i].substring(5));
                    break;
                }
                i++;
            }
        }
        //check cpu core
        if(METRIC_CPU.equals(type)) {
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("core=")) {
                    jRadioCustomCpuCore.setSelected(true);
                    String[] tmp = elements[i].split("=");
                    if(tmp.length > 1) {
                        jTextFieldCoreIndex.setText(tmp[1]);
                    } else {
                        jTextFieldCoreIndex.setText("0");
                    }
                    break;
                }
                i++;
            }
        }

        //check filesystem filter
        if(METRIC_DISKIO.equals(type)) {
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("fs=")) {
                    jTextFieldFileSystem.setText(elements[i].substring(3));
                    break;
                }
                i++;
            }
        }

        //check network interface filter
        if(METRIC_NETIO.equals(type)) {
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("iface=")) {
                    jTextFieldNetInterface.setText(elements[i].substring(6));
                    break;
                }
                i++;
            }
        }
        //check jmx
        if(METRIC_JMX.equals(type)) {
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("url=")) {
                    String[] tmp = elements[i].substring(4).split("\\\\:");
                    jTextFieldJmxHost.setText(tmp[0]);
                    if(tmp.length > 1) {
                        jTextFieldJmxPort.setText(tmp[1]);
                    }
                }
                if(elements[i].startsWith("user=")) {
                    jTextFieldJmxUser.setText(elements[i].substring(5));
                }
                if(elements[i].startsWith("password=")) {
                    jTextFieldJmxPassword.setText(elements[i].substring(9));
                }
                i++;
            }
        }

        //set metric selected, exec or tail command
        if(METRIC_EXEC.equals(type)) {
            jTextFieldExec.setText(extractExecTailCmd(existing));

            //hide stretch panel as stretch is done with text area
            jPanelStretch.setVisible(false);
        } else if(METRIC_TAIL.equals(type)) {
            jTextFieldTail.setText(extractExecTailCmd(existing));
        } else {
            for(int i=0; i<elements.length; i++) {
                initMetricRadios(elements[i]);
            }
        }

        //set label
        int i=0;
        while(i<elements.length) {
            if(elements[i].startsWith("label=")) {
                jTextFieldMetricLabel.setText(elements[i].substring(6));
                break;
            }
            i++;
        }

        //set unit
        i=0;
        while(i<elements.length) {
            if(elements[i].startsWith("unit=")) {
                setUnit(elements[i].substring(5));
                break;
            }
            i++;
        }
    }

    private void initMetricRadios(String metricName) {
        Enumeration<AbstractButton> enu = buttonGroupMetrics.getElements();
        while(enu.hasMoreElements()) {
            JRadioButton radio = (JRadioButton)enu.nextElement();
            if(metricName.equals(radio.getActionCommand())) {
                radio.setSelected(true);
                enableUnit(metricName);
            }
        }
    }

    private void makePtqlLink() {
        jLabelPtqlHelp.setForeground(Color.blue);
        jLabelPtqlHelp.setFont(jLabelPtqlHelp.getFont().deriveFont(Font.PLAIN));
        jLabelPtqlHelp.setCursor(new Cursor(Cursor.HAND_CURSOR));
        jLabelPtqlHelp.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
                    JMeterPluginsUtils.openInBrowser("http://support.hyperic.com/display/SIGAR/PTQL");
                }
            }
        });
        Border border = BorderFactory.createMatteBorder(0, 0, 1, 0, java.awt.Color.blue);
        jLabelPtqlHelp.setBorder(border);
    }

    private void initRules() {
        rules.put(METRIC_CPU, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_PROCESS_SCOPE | OPTION_CPU_CORE_SCOPE);
        rules.put(METRIC_MEM, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_PROCESS_SCOPE | OPTION_UNIT);
        rules.put(METRIC_SWAP, OPTION_PRIMARY_METRIC | OPTION_UNIT);
        rules.put(METRIC_DISKIO, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_FILESYSTEM_SCOPE | OPTION_UNIT);
        rules.put(METRIC_NETIO, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_NET_INTERFACE_SCOPE | OPTION_UNIT);
        rules.put(METRIC_TCP, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_UNIT);
        rules.put(METRIC_EXEC, OPTION_EXEC);
        rules.put(METRIC_TAIL, OPTION_TAIL);
        rules.put(METRIC_JMX, OPTION_PRIMARY_METRIC | OPTION_JMX);
    }

    private void fillMetrics(String[] metrics, JPanel panel) {
        if (metrics != null) {
           MetricActionListener listener = new MetricActionListener();
            for (int i = 0; i < metrics.length / 2; i++) {
                JRadioButton radio = new JRadioButton(metrics[2 * i]);
                String action = metrics[2 * i];
                if (action.endsWith(defaultMarker)) {
                    action = action.substring(0, action.length() - defaultMarker.length());
                }
                radio.setActionCommand(action);
                radio.setToolTipText(metrics[2 * i + 1]);
                radio.addActionListener(listener);
                buttonGroupMetrics.add(radio);
                panel.add(radio);
            }
        } else {
            panel.add(new JLabel(" None..."));
        }
    }

    private void initMetrics(String metricType) {
        //init params
        String[] primaryMetrics = null;
        String[] additionalMetrics = null;
        if (type.equals(METRIC_CPU)) {
            primaryMetrics = cpuMetricsPrimary;
            additionalMetrics = cpuMetricsAdditional;
        } else if (type.equals(METRIC_MEM)) {
            primaryMetrics = memMetricsPrimary;
            additionalMetrics = memMetricsAdditional;
        } else if (type.equals(METRIC_DISKIO)) {
            primaryMetrics = diskIOMetricsPrimary;
            additionalMetrics = diskIOMetricsAdditional;
        } else if (type.equals(METRIC_NETIO)) {
            primaryMetrics = netIOMetricsPrimary;
            additionalMetrics = netIOMetricsAdditional;
        } else if (type.equals(METRIC_TCP)) {
            primaryMetrics = tcpMetricsPrimary;
            additionalMetrics = tcpMetricsAdditional;
        } else if (type.equals(METRIC_SWAP)) {
            primaryMetrics = swapMetricsPrimary;
            additionalMetrics = null;
        } else if (type.equals(METRIC_JMX)) {
            primaryMetrics = jmxMetricsPrimary;
            additionalMetrics = null;
        }

        //show/hide relevent panels
        if (rules.containsKey(metricType)) {
            //handle metric layouts, 2 collumn if one panel only
            if((rules.get(metricType) & OPTION_PRIMARY_METRIC) != 0
                && (rules.get(metricType) & OPTION_ADDITIONAL_METRIC) == 0) {
                jPanelPrimaryMetrics.setLayout(new java.awt.GridLayout(0, 2));
            } else {
                jPanelPrimaryMetrics.setLayout(new java.awt.GridLayout(0, 1));
            }
            
            jPanelScope.setVisible((rules.get(metricType) & OPTION_PROCESS_SCOPE) != 0);
            jPanelPID.setVisible((rules.get(metricType) & OPTION_PROCESS_SCOPE) != 0);
            if ((rules.get(metricType) & OPTION_PRIMARY_METRIC) != 0) {
                fillMetrics(primaryMetrics, jPanelPrimaryMetrics);
            } else {
                jPanelPrimaryMetrics.setVisible(false);
            }
            if ((rules.get(metricType) & OPTION_ADDITIONAL_METRIC) != 0) {
                fillMetrics(additionalMetrics, jPanelAdditionaMetrics);
            } else {
                jPanelAdditionaMetrics.setVisible(false);
            }
            jPanelCustomCommand.setVisible((rules.get(metricType) & OPTION_EXEC) != 0);
            jPanelCpuCore.setVisible((rules.get(metricType) & OPTION_CPU_CORE_SCOPE) != 0);
            jPanelTailCommand.setVisible((rules.get(metricType) & OPTION_TAIL) != 0);
            jPanelFileSystem.setVisible((rules.get(metricType) & OPTION_FILESYSTEM_SCOPE) != 0);
            jPanelNetInterface.setVisible((rules.get(metricType) & OPTION_NET_INTERFACE_SCOPE) != 0);
            jPanelJmxParams.setVisible((rules.get(metricType) & OPTION_JMX) != 0);
            jPanelUnit.setVisible((rules.get(metricType) & OPTION_UNIT) != 0);
        }
    }

    private int getIntValue(String str, int defaultValue) {
        int ret;
        try {
            ret = Integer.valueOf(str);
        } catch (NumberFormatException ex) {
            ret = defaultValue;
        }

        return ret;
    }

    private String getProcessScopeString() {
        String ret = "";
        if (jRadioScopePerProcess.isSelected()) {
            if (buttonGroupPID.getSelection() != null) {
                String tmp = buttonGroupPID.getSelection().getActionCommand();
                if ("pid".equals(tmp)) {
                    ret += "pid=" + getIntValue(jTextFieldPID.getText().trim(), 0);
                } else if ("name".equals(tmp)) {
                    String name = jTextFieldPorcessName.getText().trim();
                    if (name.length() == 0) {
                        name = "unknown";
                    }
                    ret += "name=" + name + "#" + getIntValue(jTextFieldOccurence.getText().trim(), 1);
                } else if ("ptql".equals(tmp)) {
                    String query = jTextFieldPtql.getText().trim();
                    if (query.length() == 0) {
                        query = "query";
                    }
                    ret += "ptql=" + query;
                }
            } else {
                ret += "pid=0";
            }
        }
        return ret;
    }

    private String getJmxParamsString() {
        StringBuilder ret = new StringBuilder("");

        String host = jTextFieldJmxHost.getText().trim();
        int port = getIntValue(jTextFieldJmxPort.getText(), -1);

        if(port != -1 && host.length() == 0) {
            host = "localhost";
        }

        String user = jTextFieldJmxUser.getText().trim();
        String password = jTextFieldJmxPassword.getText().trim();

        if(host.length()>0) {
            String url = "url=" + host;
            if(port != -1) {
               url = url + "\\:" + port;
            }
            addStringItem(ret, url);
        }

        if(user.length() != 0 && password.length() != 0) {
            addStringItem(ret, "user=" + user);
            addStringItem(ret, "password=" + password);
        }

        return ret.toString();
    }

    private void addStringItem(StringBuilder buf, String item) {
        if(item != null && item.length()>0) {
            if(buf.length() > 0) buf.append(separator);
            buf.append(item);
        }
    }

    private String getParamsString() {
        StringBuilder ret = new StringBuilder("");
        String tmp = null;

        //special preprocessing
        if (type.equals(METRIC_CPU)) {
            if (buttonGroupCpuCores.getSelection() != null) {
                tmp = buttonGroupCpuCores.getSelection().getActionCommand();
                if ("index".equals(tmp)) {
                    addStringItem(ret, "core=" + getIntValue(jTextFieldCoreIndex.getText().trim(), 0));
                }
            }
             addStringItem(ret, getProcessScopeString());
        } else if (type.equals(METRIC_MEM)) {
            addStringItem(ret, getProcessScopeString());
        } else if (type.equals(METRIC_NETIO)) {
            tmp = jTextFieldNetInterface.getText();
            if(tmp.trim().length() > 0) {
                addStringItem(ret, "iface=" + tmp.trim());
            }
        } else if (type.equals(METRIC_TCP)) {
        } else if (type.equals(METRIC_SWAP)) {
        } else if (type.equals(METRIC_JMX)) {
            addStringItem(ret, getJmxParamsString());
        }

        //add the metric label
        tmp = jTextFieldMetricLabel.getText();
        if(tmp.trim().length() > 0) {
            addStringItem(ret, "label=" + tmp.trim());
        }

        //add the metric unit
        if (buttonGroupMetrics.getSelection() != null) {
           if(isUnitRelevent(buttonGroupMetrics.getSelection().getActionCommand())) {
              addStringItem(ret, "unit=" + getUnit());
           }
        }

        if (type.equals(METRIC_EXEC)) {
            addStringItem(ret, jTextFieldExec.getText().trim());
        } else if (type.equals(METRIC_TAIL)) {
            addStringItem(ret, jTextFieldTail.getText().trim());
        }

        //add the metric
        if (buttonGroupMetrics.getSelection() != null) {
            addStringItem(ret, buttonGroupMetrics.getSelection().getActionCommand());
        }

        //add filesystem at the end to avoid issue with win filesystem ending with \ which<commit_msg>Removed unnecessary unit param in perfmon params if unit is byte<commit_after>package kg.apc.jmeter.vizualizers;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.border.Border;
import kg.apc.jmeter.JMeterPluginsUtils;
import kg.apc.jmeter.gui.JAbsrtactDialogPanel;

/**
 *
 * @author <NAME>
 */
public class JPerfmonParamsPanel extends JAbsrtactDialogPanel {

    private final static int OPTION_PRIMARY_METRIC = 1;
    private final static int OPTION_ADDITIONAL_METRIC = 1 << 1;
    private final static int OPTION_PROCESS_SCOPE = 1 << 2;
    private final static int OPTION_CPU_CORE_SCOPE = 1 << 3;
    private final static int OPTION_FILESYSTEM_SCOPE = 1 << 4;
    private final static int OPTION_NET_INTERFACE_SCOPE = 1 << 5;
    private final static int OPTION_EXEC = 1 << 6;
    private final static int OPTION_TAIL = 1 << 7;
    private final static int OPTION_JMX = 1 << 8;
    private final static int OPTION_UNIT = 1 << 9;
    private JTextField parent = null;
    private String type = null;

    private final static String defaultMarker = " (default)";
    private final static String separator = ":";
    private final static String METRIC_CPU = "CPU";
    private final static String METRIC_MEM = "Memory";
    private final static String METRIC_SWAP = "Swap";
    private final static String METRIC_DISKIO = "Disks I/O";
    private final static String METRIC_NETIO = "Network I/O";
    private final static String METRIC_TCP = "TCP";
    private final static String METRIC_EXEC = "EXEC";
    private final static String METRIC_TAIL = "TAIL";
    private final static String METRIC_JMX = "JMX";

    private HashMap<String, Integer> rules = new HashMap<String, Integer>();

    private ArrayList<String> unitRules = new ArrayList<String>();

    //CPU metrics
    private static String[] cpuMetricsPrimary = {
        "combined" + defaultMarker, "Get the combined CPU usage, in percent (%)",
        "idle", "Get the idle CPU usage, in percent (%)",
        "system", "Get the system CPU usage, in percent (%)",
        "user", "Get the user CPU usage, in percent (%)",
        "iowait", "Get the iowait CPU usage, in percent (%)"
    };
    private static String[] cpuProcessMetricsPrimary = {
        "percent" + defaultMarker, "Get the process combined CPU usage, in percent (%)",
        "total", "Get the process total CPU usage, in percent (%)",
        "system", "Get the system CPU usage, in percent (%)",
        "user", "Get the user CPU usage, in percent (%)"
    };
    private static String[] cpuMetricsAdditional = {
        "irq", "Get the irq CPU usage, in percent (%)",
        "nice", "Get the nice CPU usage, in percent (%)",
        "softirq", "Get the softirq CPU usage, in percent (%)",
        "stolen", "Get the stolen CPU usage, in percent (%)",};
    //Memory Metrics
    private static String[] memMetricsPrimary = {
        "usedperc" + defaultMarker, "Relative memory usage, in percent (%)",
        "freeperc", "Relative free memory, in percent (%)",
        "used", "Size of Memory used",
        "free", "Size of Free memory"};
    private static String[] memMetricsAdditional = {
        "actualused", "Size of Actual memory usage",
        "actualfree", "Size of Actual free memory",
        "ram", "Server physical memory in Mb",
        "total", "Size of Total memory"};
    private static String[] memProcessMetricsPrimary = {
        "resident" + defaultMarker, "Size of Process resident memory usage",
        "virtual", "Size of Process virtual memory usage",
        "shared", "Size of Process shared memory usage"
    };
    private static String[] memProcessMetricsAdditional = {
        "pagefaults", "Process page faults count",
        "majorfaults", "Process major faults count",
        "minorfaults", "Process minor faults count"
    };
    //Disk I/O
    private static String[] diskIOMetricsPrimary = {
        "queue" + defaultMarker, "Description to update",
        "reads", "Number of read access",
        "writes", "Number of write access",
        "readbytes", "Number of bytes read",
        "writebytes", "Number of bytes written"
    };
    private static String[] diskIOMetricsAdditional = {
        "available", "Description to update",
        "service", "Description to update",
        "files", "Description to update",
        "free", "Description to update",
        "freefiles", "Description to update",
        "total", "Description to update",
        "useperc", "Description to update",
        "used", "Description to update"
    };
    //Network I/O
    private static String[] netIOMetricsPrimary = {
        "bytesrecv" + defaultMarker, "Number of bytes received",
        "bytessent", "Number of bytes sent",
        "rx", "Description to update",
        "tx", "Description to update"
    };
    private static String[] netIOMetricsAdditional = {
        "used", "Description to update",
        "speed", "Description to update",
        "rxdrops", "Description to update",
        "rxerr", "Description to update",
        "rxframe", "Description to update",
        "rxoverruns", "Description to update",
        "txcarrier", "Description to update",
        "txcollisions", "Description to update",
        "txdrops", "Description to update",
        "txerr", "Description to update",
        "txoverruns", "Description to update"
    };
    //TCP
    private static String[] tcpMetricsPrimary = {
        "estab" + defaultMarker, "Number of established connections",
        "time_wait", "Description to update",
        "close_wait", "Description to update"
    };
    private static String[] tcpMetricsAdditional = {
        "bound", "Description to update",
        "close", "Description to update",
        "closing", "Description to update",
        "fin_wait1", "Description to update",
        "fin_wait2", "Description to update",
        "idle", "Description to update",
        "inbound", "Description to update",
        "last_ack", "Description to update",
        "listen", "Description to update",
        "outbound", "Description to update",
        "syn_recv", "Description to update"
    };
    //SWAP
    private static String[] swapMetricsPrimary = {
        "used" + defaultMarker, "Size of used swap",
        "pagein", "Number of page in",
        "pageout", "Number of page out",
        "free", "Size of free swap",
        "total", "Size of total swap"
    };

    //JMX
    private static String[] jmxMetricsPrimary = {
        "gc-time", "Time spent in garbage collection, milliseconds",
        "memory-usage", "Heap memory used by VM, bytes",
        "memory-committed", "Heap memory committed by VM, bytes",
        "memorypool-usage", "Heap memory pool usage, bytes",
        "memorypool-committed", "Heap memory pool committed size, bytes",
        "class-count", "Loaded class count in VM",
        "compile-time", "Time spent in compilation, milliseconds"
    };
    
    /** Creates new form JPerfmonParamsDialog */
    public JPerfmonParamsPanel(String type, JTextField parentField) {
        this.parent = parentField;
        this.type = type;
        initRules();
        initUnitRules();
        initComponents();
        initMetrics(type);
        showProcessScopePanels();
        makePtqlLink();
        initFields();
    }

    private void addUnitRule(String type, String metric) {
       if(metric.endsWith(defaultMarker)) metric = metric.substring(0, metric.length() - defaultMarker.length());
       unitRules.add(type + metric);
    }

    private void initUnitRules() {
       //TODO: map all metrics...
       //memory
       addUnitRule(METRIC_MEM, memMetricsPrimary[4]);
       addUnitRule(METRIC_MEM, memMetricsPrimary[6]);
       addUnitRule(METRIC_MEM, memMetricsAdditional[0]);
       addUnitRule(METRIC_MEM, memMetricsAdditional[2]);
       addUnitRule(METRIC_MEM, memMetricsAdditional[6]);

       addUnitRule(METRIC_MEM, memProcessMetricsPrimary[0]);
       addUnitRule(METRIC_MEM, memProcessMetricsPrimary[2]);
       addUnitRule(METRIC_MEM, memProcessMetricsPrimary[4]);

       //swap
       addUnitRule(METRIC_SWAP, swapMetricsPrimary[0]);
       addUnitRule(METRIC_SWAP, swapMetricsPrimary[6]);
       addUnitRule(METRIC_SWAP, swapMetricsPrimary[8]);
       //tcp

       //diskio

       //netio
    }

    private boolean isUnitRelevent(String metric) {
       return unitRules.contains(type + metric);
    }

    private void enableUnit(String metric) {
       boolean show = isUnitRelevent(metric);
       jLabelUnit.setEnabled(show);
       jComboBoxUnit.setEnabled(show);
    }

    private String getUnit() {
       int unit = jComboBoxUnit.getSelectedIndex();
       switch(unit) {
          case 1:
             return "kb";
          case 2:
             return "mb";
          default:
             return "b";
       }
    }

    private void setUnit(String unit) {
       if("kb".equalsIgnoreCase(unit)) jComboBoxUnit.setSelectedIndex(1);
       else if("mb".equalsIgnoreCase(unit)) jComboBoxUnit.setSelectedIndex(2);
       else jComboBoxUnit.setSelectedIndex(0);
    }

    //extract exec or tail command (handle label)
    private String extractExecTailCmd(String params) {
        String ret;
        String[] tmp = params.split("(?<!\\\\)" + separator);
        String labelString = null;
        for(int i=0; i<tmp.length; i++) {
            if(tmp[i].startsWith("label=")) {
                labelString = tmp[i];
            }
        }

        if(labelString != null) {
            if(params.startsWith(labelString)) {
                ret = params.substring(labelString.length() + separator.length());
            } else {
                ret = params.substring(0, params.indexOf(labelString) - separator.length());
            }
        } else {
            ret = params;
        }

        return ret;
    }

    private void initFields() {
        String existing = parent.getText();

        //for split, avoid ':' preceeded with '\', ie do not process "\:"
        String[] elements = existing.split("(?<!\\\\)" + separator);

        //check process scope
        if(METRIC_CPU.equals(type) || METRIC_MEM.equals(type)) {
            if(existing.indexOf("pid=") != -1
                    || existing.indexOf("name=") != -1
                    || existing.indexOf("ptql=") != -1) {
                jRadioScopePerProcess.setSelected(true);
                showProcessScopePanels();
            }
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("pid=")) {
                    jRadioPID.setSelected(true);
                    jTextFieldPID.setText(elements[i].substring(4));
                    break;
                } else if(elements[i].startsWith("name=")) {
                    String[] tmp = elements[i].split("#");
                    jRadioProcessName.setSelected(true);
                    jTextFieldPorcessName.setText(tmp[0].substring(5));
                    if(tmp.length == 2) jTextFieldOccurence.setText(tmp[1]);
                    break;
                } else if(elements[i].startsWith("ptql=")) {
                    jRadioPtql.setSelected(true);
                    jTextFieldPtql.setText(elements[i].substring(5));
                    break;
                }
                i++;
            }
        }
        //check cpu core
        if(METRIC_CPU.equals(type)) {
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("core=")) {
                    jRadioCustomCpuCore.setSelected(true);
                    String[] tmp = elements[i].split("=");
                    if(tmp.length > 1) {
                        jTextFieldCoreIndex.setText(tmp[1]);
                    } else {
                        jTextFieldCoreIndex.setText("0");
                    }
                    break;
                }
                i++;
            }
        }

        //check filesystem filter
        if(METRIC_DISKIO.equals(type)) {
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("fs=")) {
                    jTextFieldFileSystem.setText(elements[i].substring(3));
                    break;
                }
                i++;
            }
        }

        //check network interface filter
        if(METRIC_NETIO.equals(type)) {
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("iface=")) {
                    jTextFieldNetInterface.setText(elements[i].substring(6));
                    break;
                }
                i++;
            }
        }
        //check jmx
        if(METRIC_JMX.equals(type)) {
            int i=0;
            while(i<elements.length) {
                if(elements[i].startsWith("url=")) {
                    String[] tmp = elements[i].substring(4).split("\\\\:");
                    jTextFieldJmxHost.setText(tmp[0]);
                    if(tmp.length > 1) {
                        jTextFieldJmxPort.setText(tmp[1]);
                    }
                }
                if(elements[i].startsWith("user=")) {
                    jTextFieldJmxUser.setText(elements[i].substring(5));
                }
                if(elements[i].startsWith("password=")) {
                    jTextFieldJmxPassword.setText(elements[i].substring(9));
                }
                i++;
            }
        }

        //set metric selected, exec or tail command
        if(METRIC_EXEC.equals(type)) {
            jTextFieldExec.setText(extractExecTailCmd(existing));

            //hide stretch panel as stretch is done with text area
            jPanelStretch.setVisible(false);
        } else if(METRIC_TAIL.equals(type)) {
            jTextFieldTail.setText(extractExecTailCmd(existing));
        } else {
            for(int i=0; i<elements.length; i++) {
                initMetricRadios(elements[i]);
            }
        }

        //set label
        int i=0;
        while(i<elements.length) {
            if(elements[i].startsWith("label=")) {
                jTextFieldMetricLabel.setText(elements[i].substring(6));
                break;
            }
            i++;
        }

        //set unit
        i=0;
        while(i<elements.length) {
            if(elements[i].startsWith("unit=")) {
                setUnit(elements[i].substring(5));
                break;
            }
            i++;
        }
    }

    private void initMetricRadios(String metricName) {
        Enumeration<AbstractButton> enu = buttonGroupMetrics.getElements();
        while(enu.hasMoreElements()) {
            JRadioButton radio = (JRadioButton)enu.nextElement();
            if(metricName.equals(radio.getActionCommand())) {
                radio.setSelected(true);
                enableUnit(metricName);
            }
        }
    }

    private void makePtqlLink() {
        jLabelPtqlHelp.setForeground(Color.blue);
        jLabelPtqlHelp.setFont(jLabelPtqlHelp.getFont().deriveFont(Font.PLAIN));
        jLabelPtqlHelp.setCursor(new Cursor(Cursor.HAND_CURSOR));
        jLabelPtqlHelp.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
                    JMeterPluginsUtils.openInBrowser("http://support.hyperic.com/display/SIGAR/PTQL");
                }
            }
        });
        Border border = BorderFactory.createMatteBorder(0, 0, 1, 0, java.awt.Color.blue);
        jLabelPtqlHelp.setBorder(border);
    }

    private void initRules() {
        rules.put(METRIC_CPU, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_PROCESS_SCOPE | OPTION_CPU_CORE_SCOPE);
        rules.put(METRIC_MEM, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_PROCESS_SCOPE | OPTION_UNIT);
        rules.put(METRIC_SWAP, OPTION_PRIMARY_METRIC | OPTION_UNIT);
        rules.put(METRIC_DISKIO, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_FILESYSTEM_SCOPE | OPTION_UNIT);
        rules.put(METRIC_NETIO, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_NET_INTERFACE_SCOPE | OPTION_UNIT);
        rules.put(METRIC_TCP, OPTION_PRIMARY_METRIC | OPTION_ADDITIONAL_METRIC | OPTION_UNIT);
        rules.put(METRIC_EXEC, OPTION_EXEC);
        rules.put(METRIC_TAIL, OPTION_TAIL);
        rules.put(METRIC_JMX, OPTION_PRIMARY_METRIC | OPTION_JMX);
    }

    private void fillMetrics(String[] metrics, JPanel panel) {
        if (metrics != null) {
           MetricActionListener listener = new MetricActionListener();
            for (int i = 0; i < metrics.length / 2; i++) {
                JRadioButton radio = new JRadioButton(metrics[2 * i]);
                String action = metrics[2 * i];
                if (action.endsWith(defaultMarker)) {
                    action = action.substring(0, action.length() - defaultMarker.length());
                }
                radio.setActionCommand(action);
                radio.setToolTipText(metrics[2 * i + 1]);
                radio.addActionListener(listener);
                buttonGroupMetrics.add(radio);
                panel.add(radio);
            }
        } else {
            panel.add(new JLabel(" None..."));
        }
    }

    private void initMetrics(String metricType) {
        //init params
        String[] primaryMetrics = null;
        String[] additionalMetrics = null;
        if (type.equals(METRIC_CPU)) {
            primaryMetrics = cpuMetricsPrimary;
            additionalMetrics = cpuMetricsAdditional;
        } else if (type.equals(METRIC_MEM)) {
            primaryMetrics = memMetricsPrimary;
            additionalMetrics = memMetricsAdditional;
        } else if (type.equals(METRIC_DISKIO)) {
            primaryMetrics = diskIOMetricsPrimary;
            additionalMetrics = diskIOMetricsAdditional;
        } else if (type.equals(METRIC_NETIO)) {
            primaryMetrics = netIOMetricsPrimary;
            additionalMetrics = netIOMetricsAdditional;
        } else if (type.equals(METRIC_TCP)) {
            primaryMetrics = tcpMetricsPrimary;
            additionalMetrics = tcpMetricsAdditional;
        } else if (type.equals(METRIC_SWAP)) {
            primaryMetrics = swapMetricsPrimary;
            additionalMetrics = null;
        } else if (type.equals(METRIC_JMX)) {
            primaryMetrics = jmxMetricsPrimary;
            additionalMetrics = null;
        }

        //show/hide relevent panels
        if (rules.containsKey(metricType)) {
            //handle metric layouts, 2 collumn if one panel only
            if((rules.get(metricType) & OPTION_PRIMARY_METRIC) != 0
                && (rules.get(metricType) & OPTION_ADDITIONAL_METRIC) == 0) {
                jPanelPrimaryMetrics.setLayout(new java.awt.GridLayout(0, 2));
            } else {
                jPanelPrimaryMetrics.setLayout(new java.awt.GridLayout(0, 1));
            }
            
            jPanelScope.setVisible((rules.get(metricType) & OPTION_PROCESS_SCOPE) != 0);
            jPanelPID.setVisible((rules.get(metricType) & OPTION_PROCESS_SCOPE) != 0);
            if ((rules.get(metricType) & OPTION_PRIMARY_METRIC) != 0) {
                fillMetrics(primaryMetrics, jPanelPrimaryMetrics);
            } else {
                jPanelPrimaryMetrics.setVisible(false);
            }
            if ((rules.get(metricType) & OPTION_ADDITIONAL_METRIC) != 0) {
                fillMetrics(additionalMetrics, jPanelAdditionaMetrics);
            } else {
                jPanelAdditionaMetrics.setVisible(false);
            }
            jPanelCustomCommand.setVisible((rules.get(metricType) & OPTION_EXEC) != 0);
            jPanelCpuCore.setVisible((rules.get(metricType) & OPTION_CPU_CORE_SCOPE) != 0);
            jPanelTailCommand.setVisible((rules.get(metricType) & OPTION_TAIL) != 0);
            jPanelFileSystem.setVisible((rules.get(metricType) & OPTION_FILESYSTEM_SCOPE) != 0);
            jPanelNetInterface.setVisible((rules.get(metricType) & OPTION_NET_INTERFACE_SCOPE) != 0);
            jPanelJmxParams.setVisible((rules.get(metricType) & OPTION_JMX) != 0);
            jPanelUnit.setVisible((rules.get(metricType) & OPTION_UNIT) != 0);
        }
    }

    private int getIntValue(String str, int defaultValue) {
        int ret;
        try {
            ret = Integer.valueOf(str);
        } catch (NumberFormatException ex) {
            ret = defaultValue;
        }

        return ret;
    }

    private String getProcessScopeString() {
        String ret = "";
        if (jRadioScopePerProcess.isSelected()) {
            if (buttonGroupPID.getSelection() != null) {
                String tmp = buttonGroupPID.getSelection().getActionCommand();
                if ("pid".equals(tmp)) {
                    ret += "pid=" + getIntValue(jTextFieldPID.getText().trim(), 0);
                } else if ("name".equals(tmp)) {
                    String name = jTextFieldPorcessName.getText().trim();
                    if (name.length() == 0) {
                        name = "unknown";
                    }
                    ret += "name=" + name + "#" + getIntValue(jTextFieldOccurence.getText().trim(), 1);
                } else if ("ptql".equals(tmp)) {
                    String query = jTextFieldPtql.getText().trim();
                    if (query.length() == 0) {
                        query = "query";
                    }
                    ret += "ptql=" + query;
                }
            } else {
                ret += "pid=0";
            }
        }
        return ret;
    }

    private String getJmxParamsString() {
        StringBuilder ret = new StringBuilder("");

        String host = jTextFieldJmxHost.getText().trim();
        int port = getIntValue(jTextFieldJmxPort.getText(), -1);

        if(port != -1 && host.length() == 0) {
            host = "localhost";
        }

        String user = jTextFieldJmxUser.getText().trim();
        String password = jTextFieldJmxPassword.getText().trim();

        if(host.length()>0) {
            String url = "url=" + host;
            if(port != -1) {
               url = url + "\\:" + port;
            }
            addStringItem(ret, url);
        }

        if(user.length() != 0 && password.length() != 0) {
            addStringItem(ret, "user=" + user);
            addStringItem(ret, "password=" + password);
        }

        return ret.toString();
    }

    private void addStringItem(StringBuilder buf, String item) {
        if(item != null && item.length()>0) {
            if(buf.length() > 0) buf.append(separator);
            buf.append(item);
        }
    }

    private String getParamsString() {
        StringBuilder ret = new StringBuilder("");
        String tmp = null;

        //special preprocessing
        if (type.equals(METRIC_CPU)) {
            if (buttonGroupCpuCores.getSelection() != null) {
                tmp = buttonGroupCpuCores.getSelection().getActionCommand();
                if ("index".equals(tmp)) {
                    addStringItem(ret, "core=" + getIntValue(jTextFieldCoreIndex.getText().trim(), 0));
                }
            }
             addStringItem(ret, getProcessScopeString());
        } else if (type.equals(METRIC_MEM)) {
            addStringItem(ret, getProcessScopeString());
        } else if (type.equals(METRIC_NETIO)) {
            tmp = jTextFieldNetInterface.getText();
            if(tmp.trim().length() > 0) {
                addStringItem(ret, "iface=" + tmp.trim());
            }
        } else if (type.equals(METRIC_TCP)) {
        } else if (type.equals(METRIC_SWAP)) {
        } else if (type.equals(METRIC_JMX)) {
            addStringItem(ret, getJmxParamsString());
        }

        //add the metric label
        tmp = jTextFieldMetricLabel.getText();
        if(tmp.trim().length() > 0) {
            addStringItem(ret, "label=" + tmp.trim());
        }

        //add the metric unit
        if (buttonGroupMetrics.getSelection() != null) {
           if(isUnitRelevent(buttonGroupMetrics.getSelection().getActionCommand())) {
              String unit = getUnit();
              if(!"b".equalsIgnoreCase(unit)) {
                 addStringItem(ret, "unit=" + getUnit());
              }
           }
        }

        if (type.equals(METRIC_EXEC)) {
            addStringItem(ret, jTextFieldExec.getText().trim());
        } else if (type.equals(METRIC_TAIL)) {
            addStringItem(ret, jTextFieldTail.getText().trim());
        }

        //add the metric
        if (buttonGroupMetrics.getSelection() != null) {
            addStringItem(ret, buttonGroupMetrics.getSelection().getActionCommand());
        }

        //add filesystem at the end to avoid issue with win filesystem ending with \ which
{'fraction_non_alphanumeric': 0.1183307130490159, 'fraction_numerical': 0.0046966108778284145, 'mean_word_length': 2.089645843580915, 'pattern_counts': {'":': 2, '<': 75, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2403057', 'n_tokens_mistral': 14322, 'n_tokens_neox': 13763, 'n_words': 3071}