/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2003,2004 University of Maryland
 * Copyright (C) 2004 Dave Brosius <dbrosius@users.sourceforge.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * ConfigureDetectorsDialog.java
 *
 * Created on June 3, 2003, 3:52 PM
 */

package edu.umd.cs.findbugs.gui;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;

import edu.umd.cs.findbugs.BugPattern;
import edu.umd.cs.findbugs.DetectorFactory;
import edu.umd.cs.findbugs.DetectorFactoryCollection;
import edu.umd.cs.findbugs.L10N;
import edu.umd.cs.findbugs.config.UserPreferences;

/**
 * Configure Detectors by enabling/disabling them.
 *
 * @author David Hovemeyer
 */
public class ConfigureDetectorsDialog extends javax.swing.JDialog {
    private static final long serialVersionUID = 1L;

    private static final int SPEED_COLUMN = 1;
    private static final int ENABLED_COLUMN = 2;

    /**
     * Creates new form ConfigureDetectorsDialog
     */
    public ConfigureDetectorsDialog(java.awt.Frame parent, boolean modal) {
        super(parent, modal);
        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.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        java.awt.GridBagConstraints gridBagConstraints;

        detectorTableScrollPane = new javax.swing.JScrollPane();
        detectorTable = new javax.swing.JTable();
        detectorDescriptionScrollPane = new javax.swing.JScrollPane();
        detectorDescription = new javax.swing.JEditorPane();
        jSeparator1 = new javax.swing.JSeparator();
        okButton = new javax.swing.JButton();
        cancelButton = new javax.swing.JButton();
        spacer = new javax.swing.JLabel();
        restoreDefaultsButton = new javax.swing.JButton();

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

        setTitle("Configure Detectors");
        addWindowListener(new java.awt.event.WindowAdapter() {
            @Override
            public void windowClosing(java.awt.event.WindowEvent evt) {
                closeDialog(evt);
            }
            @Override
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });

        detectorTableScrollPane.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
        detectorTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "Bug Detector", "Speed", "Enabled"
            }
        ) {
            private static final long serialVersionUID = 1L;
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class, java.lang.Boolean.class
            };
            boolean[] canEdit = new boolean [] {
                false, false, true
            };

            @Override
            public Class<?> getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            @Override
            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        populateTable();
        detectorTable.getColumnModel().getColumn(ENABLED_COLUMN).setMaxWidth(60);
        detectorTable.getColumnModel().getColumn(SPEED_COLUMN).setMaxWidth(60);
        detectorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        {
            DefaultTableModel m = (DefaultTableModel)detectorTable.getModel();
            m.setColumnIdentifiers( new String[]
                {
                    L10N.getLocalString("dlg.bugdetector_lbl", "Bug Detector"),
                    L10N.getLocalString("dlg.speed_lbl", "Speed"),
                    L10N.getLocalString("dlg.enabled_lbl", "Enabled"),
                });

                DefaultSortedTableModel sortedModel = new DefaultSortedTableModel(m, detectorTable.getTableHeader());
                detectorTable.setModel(sortedModel);
            }

            detectorTableScrollPane.setViewportView(detectorTable);

            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridwidth = 4;
            gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
            gridBagConstraints.weightx = 1.0;
            gridBagConstraints.weighty = 0.8;
            gridBagConstraints.insets = new java.awt.Insets(6, 6, 2, 6);
            getContentPane().add(detectorTableScrollPane, gridBagConstraints);

            detectorDescriptionScrollPane.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
            detectorDescriptionScrollPane.setPreferredSize(new java.awt.Dimension(110, 120));
            detectorDescriptionScrollPane.setViewportView(detectorDescription);

            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 1;
            gridBagConstraints.gridwidth = 4;
            gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
            gridBagConstraints.weighty = 0.3;
            gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
            getContentPane().add(detectorDescriptionScrollPane, gridBagConstraints);

            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 2;
            gridBagConstraints.gridwidth = 4;
            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
            gridBagConstraints.insets = new java.awt.Insets(3, 0, 3, 0);
            getContentPane().add(jSeparator1, gridBagConstraints);

            okButton.setMnemonic('O');
            okButton.setText("OK");
            okButton.setText(L10N.getLocalString("dlg.ok_btn","OK"));
            okButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    okButtonActionPerformed(evt);
                }
            });

            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 2;
            gridBagConstraints.gridy = 3;
            gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 2);
            getContentPane().add(okButton, gridBagConstraints);

            cancelButton.setMnemonic('C');
            cancelButton.setText("Cancel");
            cancelButton.setText(L10N.getLocalString("dlg.cancel_btn", "Cancel"));
            cancelButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    cancelButtonActionPerformed(evt);
                }
            });

            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 3;
            gridBagConstraints.gridy = 3;
            gridBagConstraints.insets = new java.awt.Insets(0, 2, 4, 6);
            getContentPane().add(cancelButton, gridBagConstraints);

            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 1;
            gridBagConstraints.gridy = 3;
            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
            gridBagConstraints.weightx = 1.0;
            getContentPane().add(spacer, gridBagConstraints);

            restoreDefaultsButton.setText("Restore Defaults");
            restoreDefaultsButton.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
            restoreDefaultsButton.setText(L10N.getLocalString("dlg.restoredefaults_btn", "Restore Defaults"));
            restoreDefaultsButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    restoreDefaultsButtonActionPerformed(evt);
                }
            });

            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 3;
            gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0);
            getContentPane().add(restoreDefaultsButton, gridBagConstraints);

            pack();
        }//GEN-END:initComponents

    private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
        setTitle(L10N.getLocalString("dlg.configuredetectors_ttl", "Configure Detectors"));
    }//GEN-LAST:event_formWindowOpened

    /**
     * reverts the selected state of all the detectors to their defaults as specified in the findbugs.xml file
     *
     * @param evt the swing event corresponding to the mouse click of the Restore Defaults button
     */
    private void restoreDefaultsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_restoreDefaultsButtonActionPerformed
        Iterator<DetectorFactory> i = DetectorFactoryCollection.instance().factoryIterator();
        DefaultSortedTableModel sorter = (DefaultSortedTableModel) detectorTable.getModel();
        TableModel model = sorter.getBaseTableModel();
        int row = 0;
        while (i.hasNext()) {
            DetectorFactory factory = i.next();
            if (factory.isHidden())
                continue;
            model.setValueAt(factory.isDefaultEnabled() ? Boolean.TRUE : Boolean.FALSE, row++, ENABLED_COLUMN);
        }
    }//GEN-LAST:event_restoreDefaultsButtonActionPerformed

    private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
        closeDialog();
    }//GEN-LAST:event_cancelButtonActionPerformed

    private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
        // Update new enabled/disabled status for the Detectors
        int num = factoryList.size();
        DefaultSortedTableModel sorter = (DefaultSortedTableModel) detectorTable.getModel();
        TableModel model = sorter.getBaseTableModel();
        for (int i = 0; i < num; ++i) {
            DetectorFactory factory = factoryList.get(i);
            Boolean enabled = (Boolean) model.getValueAt(i, ENABLED_COLUMN);
            UserPreferences.getUserPreferences().enableDetector(
                    factory, enabled.booleanValue());
        }
        closeDialog();
    }//GEN-LAST:event_okButtonActionPerformed

    /**
     * Closes the dialog
     */
    private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
        setVisible(false);
        dispose();
    }//GEN-LAST:event_closeDialog

    /**
     * installs a list selection listener to populate the bottom details page based on selection changes in top grid.
     * A conversion from the table sorter index to the base model index is done to get the correct details
     */
    private void postInitComponents() {
        // Listen to detector table selections so we can (hopefully)
        // display the description of the detector

        ListSelectionModel rowSM = detectorTable.getSelectionModel();
        rowSM.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) return;

                ListSelectionModel lsm = (ListSelectionModel) e.getSource();
                if (!lsm.isSelectionEmpty()) {
                    int selectedRow = lsm.getMinSelectionIndex();
                    DefaultSortedTableModel sorter = (DefaultSortedTableModel) detectorTable.getModel();
                    viewDetectorDetails(factoryList.get(sorter.getBaseModelIndex(selectedRow)));
                }
            }
        });
    }

    /**
     * populates the bottom detector details pane based on the detector selected
     *
     * @param factory the detector that is currently selected
     */
    private void viewDetectorDetails(DetectorFactory factory) {
        String detailHTML = factory.getDetailHTML();
        if (detailHTML == null) {
            detectorDescription.setText("");
        } else {
            detectorDescription.setContentType("text/html");
            detectorDescription.setText(detailHTML);
            StringBuilder toolTip = new StringBuilder(100);
            toolTip.append("<html><body><b>");
            toolTip.append(factory.getFullName());
            toolTip.append("</b><br><br><table border='1' width='100%'><tr><th>");
            toolTip.append(L10N.getLocalString("msg.bugpatternsreported_txt", "Bug Patterns Reported"));
            toolTip.append("</th></tr>");

            Collection<BugPattern> patterns = factory.getReportedBugPatterns();
            for (BugPattern pattern : patterns) {
                toolTip.append("<tr><td align='center'>");
                toolTip.append("[");
                toolTip.append(pattern.getAbbrev());
                toolTip.append("] ");
                toolTip.append(pattern.getType());
                toolTip.append("</td></tr>");
            }
            toolTip.append("</body></html>");
            detectorDescription.setToolTipText(toolTip.toString());
        }
    }

    /**
     * populates the Detector JTable model with all available detectors
     * Due to Netbeans form builder, populate table gets called before the tablesorter is installed,
     * so it is correct for the model retrieved from the table to be assumed to be the base DefaultTableModel.
     */
    private void populateTable() {
        Iterator<DetectorFactory> i = DetectorFactoryCollection.instance().factoryIterator();
        while (i.hasNext()) {
            DetectorFactory factory = i.next();
            if (factory.isHidden())
                continue;
            DefaultTableModel model = (DefaultTableModel) detectorTable.getModel();
            model.addRow(new Object[]{
                    factory.getShortName(),
                    factory.getSpeed(),
                    UserPreferences.getUserPreferences().isDetectorEnabled(factory)
                        ? Boolean.TRUE : Boolean.FALSE
                    });
            factoryList.add(factory);
        }
    }

    private void closeDialog() {
        setVisible(false);
        dispose();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        new ConfigureDetectorsDialog(new javax.swing.JFrame(), true).setVisible(true);
    }


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton cancelButton;
    private javax.swing.JEditorPane detectorDescription;
    private javax.swing.JScrollPane detectorDescriptionScrollPane;
    private javax.swing.JTable detectorTable;
    private javax.swing.JScrollPane detectorTableScrollPane;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JButton okButton;
    private javax.swing.JButton restoreDefaultsButton;
    private javax.swing.JLabel spacer;
    // End of variables declaration//GEN-END:variables

    // My variables
    private ArrayList<DetectorFactory> factoryList = new ArrayList<DetectorFactory>();
}
