/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2006, University of Maryland
 * 
 * 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
 */

package edu.umd.cs.findbugs.gui2;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.TreeModel;

import edu.umd.cs.findbugs.filter.Filter;
import edu.umd.cs.findbugs.filter.Matcher;


/**
 * User Preferences
 */

/*
 * 
 * Preferences, which should really be renamed Filters And Suppressions (fas, like fas file!)
 * since thats all that's actually here now
 *
 */
@SuppressWarnings("serial")
public class PreferencesFrame extends FBDialog {

	private static int PROPERTIES_TAB = 0;
	private static int FILTERS_TAB = 1;
	private static int SUPPRESSIONS_TAB = 2;

	JTabbedPane mainTabPane;

	private static PreferencesFrame instance;
	private CheckBoxList filterCheckBoxList = new CheckBoxList();
	private JButton addButton;
	
	JButton removeButton;
	JButton removeAllButton;
	boolean frozen=false;

	//Variables for Properties tab.
	private JTextField tabTextField;
	private JTextField fontTextField;
	private JTextField packagePrefixLengthTextField;
	private static int TAB_MIN = 1;
	private static int TAB_MAX = 20;
	private static int FONT_MIN = 10;
	private static int FONT_MAX = 99;

	public static PreferencesFrame getInstance()
	{
//		MainFrame.getInstance().getSorter().freezeOrder();
		if(instance == null)
			instance = new PreferencesFrame();

		return instance;
	}

	private PreferencesFrame(){
		setTitle(edu.umd.cs.findbugs.L10N.getLocalString("dlg.fil_sup_ttl", "Filters/Suppressions"));
		setModal(true);

		mainTabPane = new JTabbedPane();

		mainTabPane.add("Properties", createPropertiesPane());

		mainTabPane.add(edu.umd.cs.findbugs.L10N.getLocalString("pref.filters", "Filters"), createFilterPane());		

		MainFrame.getInstance().updateStatusBar();
		getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));

		JPanel top = new JPanel();
		top.setLayout(new BoxLayout(top, BoxLayout.X_AXIS));
		top.add(Box.createHorizontalStrut(5));
		top.add(mainTabPane);
		top.add(Box.createHorizontalStrut(5));

		JPanel bottom = new JPanel();
		bottom.setLayout(new BoxLayout(bottom, BoxLayout.X_AXIS));
		bottom.add(Box.createHorizontalGlue());
		bottom.add(new JButton(new AbstractAction(edu.umd.cs.findbugs.L10N.getLocalString("pref.close", "Close"))
		{
			public void actionPerformed(ActionEvent evt)
			{
//				MainFrame.getInstance().getSorter().thawOrder();
				PreferencesFrame.this.setVisible(false);
				TreeModel bt= (MainFrame.getInstance().getTree().getModel());
				if (bt instanceof BugTreeModel)		
					((BugTreeModel)bt).checkSorter();

				resetPropertiesPane();
			}
		}));
		bottom.add(Box.createHorizontalStrut(5));

		add(Box.createVerticalStrut(5));
		add(top);
		add(Box.createVerticalStrut(5));
		add(bottom);
		add(Box.createVerticalStrut(5));

		setDefaultCloseOperation(HIDE_ON_CLOSE);

		pack();
	}

//	/**
//	 * Creates the general tab. This lets the user change the font
//	 * size and the total number of possible previous comments.
//	 * @return
//	 */
//	private JPanel createGeneralPane() {
//		JPanel generalPanel = new JPanel();
//		
//		generalPanel.add(new JLabel("Clear Recent Projects List"));
//		JButton clearButton=new JButton("Clear");
//		clearButton.addActionListener(new ActionListener()
//		{
//			public void actionPerformed(ActionEvent e)
//			{
//				GUISaveState.getInstance().clear();
//			}
//		});
//		return generalPanel;
//	}
//	

	private JPanel createPropertiesPane()
	{
		JPanel contentPanel = new JPanel(new BorderLayout());
		JPanel mainPanel = new JPanel();

		float currFS = Driver.getFontSize();

		JPanel temp = new JPanel();
		temp.add(new JLabel("Tab size"));
		tabTextField = new JTextField(Integer.toString(GUISaveState.getInstance().getTabSize()));
		tabTextField.setPreferredSize(new Dimension((int)(currFS*2), (int)(currFS*1.3)));
		temp.add(tabTextField);
		mainPanel.add(temp);
		mainPanel.add(Box.createVerticalStrut(5));

		temp = new JPanel();
		temp.add(new JLabel("Font size"));
		fontTextField = new JTextField(Float.toString(GUISaveState.getInstance().getFontSize()));
		fontTextField.setPreferredSize(new Dimension((int)(currFS*3), (int)(currFS*1.3)));
		temp.add(fontTextField);
		mainPanel.add(temp);
		mainPanel.add(Box.createVerticalGlue());

		temp = new JPanel();
		temp.add(new JLabel("Package prefix length"));
		packagePrefixLengthTextField = new JTextField(Integer.toString(GUISaveState.getInstance().getPackagePrefixSegments()));
		packagePrefixLengthTextField.setPreferredSize(new Dimension((int)(currFS*2), (int)(currFS*1.3)));
		temp.add(packagePrefixLengthTextField);
		mainPanel.add(temp);
		mainPanel.add(Box.createVerticalGlue());
		
		contentPanel.add(mainPanel, BorderLayout.CENTER);

		JPanel bottomPanel = new JPanel();
		bottomPanel.add(new JButton(new AbstractAction("Apply")
		{
			public void actionPerformed(ActionEvent evt)
			{
				changeTabSize();
				changeFontSize();
				changePackagePrefixLength();
			}
		}));

		bottomPanel.add(new JButton(new AbstractAction("Reset")
		{
			public void actionPerformed(ActionEvent evt)
			{
				resetPropertiesPane();
			}
		}));

		contentPanel.add(bottomPanel, BorderLayout.SOUTH);

		addWindowListener(new WindowAdapter(){
			@Override
			public void windowDeactivated(WindowEvent e) {
				resetPropertiesPane();
			}
		});

		return contentPanel;
	}

	private void changeTabSize(){
		int tabSize = 0;

		try{
			tabSize = Integer.decode(tabTextField.getText()).intValue();					
		}
		catch(NumberFormatException exc){
			JOptionPane.showMessageDialog(instance,	"Error in tab size field.",
					"Tab Size Error", JOptionPane.INFORMATION_MESSAGE);
			return;
		}

		if(tabSize < TAB_MIN || tabSize > TAB_MAX){
			JOptionPane.showMessageDialog(instance,	"Tab size exceedes range ("+TAB_MIN+" - "+TAB_MAX+").",
					"Tab Size Excedes Range", JOptionPane.INFORMATION_MESSAGE);
			return;
		}

		if(tabSize != GUISaveState.getInstance().getTabSize()){
			GUISaveState.getInstance().setTabSize(tabSize);
			MainFrame.getInstance().displayer.clearCache();
			//This causes the GUI to redisplay the current code
			MainFrame.getInstance().syncBugInformation();
		}
	}

	private void changeFontSize(){
		float fontSize = 0;

		try{
			fontSize = Float.parseFloat(fontTextField.getText());				
		}
		catch(NumberFormatException exc){
			JOptionPane.showMessageDialog(instance,	"Error in font size field.",
					"Font Size Error", JOptionPane.INFORMATION_MESSAGE);
			return;
		}

		if(fontSize < FONT_MIN || fontSize > FONT_MAX){
			JOptionPane.showMessageDialog(instance,	"Font size exceedes range ("+FONT_MIN+" - "+FONT_MAX+").",
					"Font Size Exceedes Range", JOptionPane.INFORMATION_MESSAGE);
			return;
		}

		if(fontSize != GUISaveState.getInstance().getFontSize()){
			GUISaveState.getInstance().setFontSize(fontSize);
			JOptionPane.showMessageDialog(instance,	"To implement the new font size please restart FindBugs.",
					"Changing Font", JOptionPane.INFORMATION_MESSAGE);
		}
	}
	private void changePackagePrefixLength(){
		int value = 0;

		try{
			value = Integer.parseInt(packagePrefixLengthTextField.getText());		
		}
		catch(NumberFormatException exc){
			JOptionPane.showMessageDialog(instance,	"Error in package prefix length field.",
					"Package Prefix Length Error", JOptionPane.INFORMATION_MESSAGE);
			return;
		}

		if(value < 1 || value > 12){
			JOptionPane.showMessageDialog(instance,	"package prefix length exceedes range ("+1+" - "+12+").",
					"package prefix lengthe exceedes range", JOptionPane.INFORMATION_MESSAGE);
			return;
		}

		if (value !=GUISaveState.getInstance().getPackagePrefixSegments()) {
			GUISaveState.getInstance().setPackagePrefixSegments(value);
			BugTreeModel bt=(BugTreeModel) (MainFrame.getInstance().getTree().getModel());
			bt.needToRebuild();
			bt.checkSorter();
		}
		
		
	}
	
	private void resetPropertiesPane()
	{
		tabTextField.setText(Integer.toString(GUISaveState.getInstance().getTabSize()));
		fontTextField.setText(Float.toString(GUISaveState.getInstance().getFontSize()));
	}

	
	/**
	 * Create a JPanel to display the filtering controls.
	 */
	private JPanel createFilterPane()
	{
		addButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.add_dot_btn", "Add..."));
		removeButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.remove_btn", "Remove"));
		removeAllButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.remove_all_btn", "Remove All"));
		JPanel filterPanel = new JPanel();
		filterPanel.setLayout(new GridBagLayout());
		GridBagConstraints gbc = new GridBagConstraints();

		gbc.gridheight = 4;
		gbc.gridwidth = 1;
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.fill = GridBagConstraints.BOTH;
		gbc.weightx = 1;
		gbc.weighty = 1;
		filterPanel.add(new JScrollPane(filterCheckBoxList), gbc);
		updateFilterPanel();

		gbc.gridheight = 1;
		gbc.gridx = 1;
		gbc.gridy = 0;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.weightx = 0;
		gbc.weighty = 0;
		filterPanel.add(addButton, gbc);
		addButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt)
			{
				NewFilterFrame.open();
			}
		});

		gbc.gridy = 1;
		gbc.insets = new Insets(5, 0, 0, 0);
		filterPanel.add(removeButton, gbc);
		removeButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt)
			{
				Object[] selected=filterCheckBoxList.getSelectedValues();
				boolean needsRebuild=false;
				if (selected.length == 0)
					return;
				for (Object o: selected)
				{
					MatchBox box=(MatchBox) o;
					if (MainFrame.getInstance().getProject().getSuppressionFilter().isEnabled(box.getMatcher()))
						needsRebuild=true;
					MainFrame.getInstance().getProject().getSuppressionFilter().removeChild(box.getMatcher());
				}
				FilterActivity.notifyListeners(FilterListener.Action.UNFILTERING, null);
				MainFrame.getInstance().setProjectChanged(true);
				updateFilterPanel();
			}
		});
		gbc.gridy = 2;
		gbc.weighty = 0;
		gbc.insets = new Insets(5, 0, 0, 0);
		filterPanel.add(removeAllButton, gbc);
		removeAllButton.addActionListener(new ActionListener()
				{
					public void actionPerformed(ActionEvent evt)
					{
						boolean needsRebuild=false;
						Filter suppressionFilter = MainFrame.getInstance().getProject().getSuppressionFilter();
						if (!suppressionFilter.isEmpty())
							needsRebuild=true;
						suppressionFilter.clear();

						if (needsRebuild)//TODO This will rebuild even if all the filters being cleared were disabled
							FilterActivity.notifyListeners(FilterListener.Action.UNFILTERING, null);
						MainFrame.getInstance().setProjectChanged(true);
						updateFilterPanel();
					}
				});		
		gbc.gridy = 3;
		gbc.weighty = 1;
		gbc.insets = new Insets(0, 0, 0, 0);
		filterPanel.add(Box.createGlue(), gbc);

		return filterPanel;
	}

	private static class MatchBox extends JCheckBox{
		Matcher m;
		MatchBox(String text, Matcher m){
			super(text);
			this.m=m;
		}
		Matcher getMatcher(){return m;}
	}
	
	void updateFilterPanel()
	{
		ArrayList<MatchBox> boxes = new ArrayList<MatchBox>();
		final Filter f = MainFrame.getInstance().getProject().getSuppressionFilter();
		
		for(Iterator<Matcher> i = f.childIterator(); i.hasNext(); ) {
			final Matcher m = i.next();
			MatchBox box = new MatchBox(m.toString(),m);
			box.addItemListener(new ItemListener(){
				public void itemStateChanged(ItemEvent evt) {
					boolean isSelected = ((JCheckBox) evt.getSource()).isSelected();
					boolean wasSelected = f.isEnabled(m);
					if (isSelected == wasSelected) return;
					f.setEnabled(m, isSelected);
					FilterActivity.notifyListeners(isSelected ? FilterListener.Action.FILTERING :  FilterListener.Action.UNFILTERING, null);
					MainFrame.getInstance().updateStatusBar();
					MainFrame.getInstance().setProjectChanged(true);
	                
                }});
			box.setSelected(f.isEnabled(m));
			boxes.add(box);
		}

		filterCheckBoxList.setListData(boxes.toArray(new MatchBox[boxes.size()]));
	}

	private static class UneditableTableModel extends DefaultTableModel
	{
		public UneditableTableModel(Object[][] tableData, String[] strings) {
			super(tableData,strings);
		}
		@Override
		public boolean isCellEditable(int x, int y)
		{
			return false;
		}
	}

	private static class FilterCheckBoxListener implements ItemListener
	{
		FilterMatcher filter;

		FilterCheckBoxListener(FilterMatcher filter)
		{
			this.filter = filter;
		}

		public void itemStateChanged(ItemEvent evt)
		{
//			RebuildThreadMonitor.waitForRebuild();
			filter.setActive(((JCheckBox) evt.getSource()).isSelected());
			MainFrame.getInstance().updateStatusBar();
			MainFrame.getInstance().setProjectChanged(true);
		}	
	}


	void freeze()
	{
		frozen=true;
		filterCheckBoxList.setEnabled(false);
		addButton.setEnabled(false);
		removeButton.setEnabled(false);
	}

	void thaw()
	{
		filterCheckBoxList.setEnabled(true);
		addButton.setEnabled(true);
		removeButton.setEnabled(true);
		frozen=false;
	}

	void setSelectedTab(int index)
	{
		if(index > 0 && index <= mainTabPane.getTabCount())
			mainTabPane.setSelectedIndex(index);
	}
}
