/**
 *  Copyright (C) 2010 by Mar'yan Rachynskyy
 *  mrach@users.sourceforge.net
 *  
 *  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 net.sf.linuxorg.pcal.bbtgui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;

import net.sf.linuxorg.pcal.MainWindow;
import net.sf.linuxorg.pcal.PCalendar;
import net.sf.linuxorg.pcal.engine.BBTSympthomsSetDefinition;
import net.sf.linuxorg.pcal.messages.Messages;

/**
 * This class provides BBT Preferences widget.
 * Use the factory methods to get new instances of the widgets.
 * @author Mar'yan Rachynskyy
 *
 */
public class BBTPrefsWidget {
	private static BBTSympthomsSetDefinition sympthomsContainer = PCalendar.engine.getBBTSympthoms();

	// the created components references
	public JTable sympthomsTable;
	public JTable valuesTable;
	public JSplitPane prefsSplitPane;
	public JButton sympthomsUpButton;
	public JButton sympthomsDownButton;
	public JButton valuesAddButton;
	public JButton valuesRemoveButton;
	public JButton valuesUpButton;
	public JButton valuesDownButton;

	private JRadioButton rbCelsius;
	private JRadioButton rbFahrenheit;

	// the cached number of the selected sympthom
	public int cachedSympthomID = -1;


	/**
	 * The BBT preferences widget factory.
	 * @return the new instance of the widget
	 */
	public JComponent getBBTPreferences() {
		JPanel mainPanel = new JPanel(new GridBagLayout());
		mainPanel.setBorder(BorderFactory.createEtchedBorder());

		GridBagConstraints c = new GridBagConstraints();

		JLabel l = new JLabel(Messages.getString("BBTPrefsWidget.0")); //$NON-NLS-1$
		c.insets.bottom = 10;
		c.anchor = GridBagConstraints.WEST;
		mainPanel.add(l, c);

		// the sympthoms pane
		JPanel sympthomsPanel = new JPanel(new BorderLayout());

		Border b = BorderFactory.createEtchedBorder();
		b = BorderFactory.createTitledBorder(b, Messages.getString("BBTPrefsWidget.1")); //$NON-NLS-1$
		sympthomsPanel.setBorder(b);		

		sympthomsTable = new JTable(new BBTSympthomsListModel(this, sympthomsContainer));
		sympthomsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		sympthomsTable.setPreferredSize(new Dimension(100, 100));
		sympthomsPanel.add(sympthomsTable, BorderLayout.CENTER);

		ActionListener prefActionListener = getPreferencesActionListener();

		JToolBar sympthomsToolbar =  new JToolBar();
		sympthomsToolbar.setFloatable(false);
		sympthomsToolbar.setOrientation(JToolBar.VERTICAL);
		sympthomsUpButton = MainWindow.createToolbarButton("16x16/up.png", //$NON-NLS-1$ 
				"sympthomUp", Messages.getString("BBTPrefsWidget.2"), //$NON-NLS-1$ //$NON-NLS-2$
				prefActionListener);
		sympthomsToolbar.add(sympthomsUpButton);
		sympthomsDownButton = MainWindow.createToolbarButton("16x16/down.png", //$NON-NLS-1$ 
				"sympthomDown", Messages.getString("BBTPrefsWidget.3"), //$NON-NLS-1$ //$NON-NLS-2$
				prefActionListener);
		sympthomsToolbar.add(sympthomsDownButton);

		sympthomsPanel.add(sympthomsToolbar, BorderLayout.LINE_END);

		//values panel		
		JPanel valuesPanel = new JPanel(new BorderLayout());

		b = BorderFactory.createEtchedBorder();
		b = BorderFactory.createTitledBorder(b, Messages.getString("BBTPrefsWidget.4")); //$NON-NLS-1$
		valuesPanel.setBorder(b);		

		valuesTable = new JTable(new BBTSympthomValuesModel(this, sympthomsContainer));
		valuesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		valuesTable.setTableHeader(null);
		JScrollPane scrollPane = new JScrollPane(valuesTable, 
				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		valuesPanel.add(scrollPane, BorderLayout.CENTER);

		JToolBar valuesToolbar =  new JToolBar();
		valuesToolbar.setFloatable(false);
		valuesToolbar.setOrientation(JToolBar.VERTICAL);
		valuesAddButton = MainWindow.createToolbarButton("16x16/add.png", //$NON-NLS-1$ 
				"valueAdd", Messages.getString("BBTPrefsWidget.5"), //$NON-NLS-1$ //$NON-NLS-2$
				prefActionListener);
		valuesToolbar.add(valuesAddButton);
		valuesRemoveButton = MainWindow.createToolbarButton("16x16/remove.png", //$NON-NLS-1$ 
				"valueRemove", Messages.getString("BBTPrefsWidget.6"), //$NON-NLS-1$ //$NON-NLS-2$
				prefActionListener); 
		valuesToolbar.add(valuesRemoveButton);
		valuesUpButton = MainWindow.createToolbarButton("16x16/up.png", //$NON-NLS-1$
				"valueUp", Messages.getString("BBTPrefsWidget.7"), //$NON-NLS-1$ //$NON-NLS-2$
				prefActionListener);
		valuesToolbar.add(valuesUpButton);
		valuesDownButton = MainWindow.createToolbarButton("16x16/down.png", //$NON-NLS-1$ 
				"valueDown", Messages.getString("BBTPrefsWidget.8"), //$NON-NLS-1$ //$NON-NLS-2$
				prefActionListener);
		valuesToolbar.add(valuesDownButton);

		valuesPanel.add(valuesToolbar, BorderLayout.LINE_END);

		//splitter
		prefsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
				sympthomsPanel, valuesPanel);
		prefsSplitPane.setBorder(null);
		prefsSplitPane.setPreferredSize(new Dimension(400,200));
		c.gridy = 1;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		mainPanel.add(prefsSplitPane, c);

		// set up the listeners
		ListSelectionModel listSelectionModel = sympthomsTable.getSelectionModel();
		listSelectionModel.addListSelectionListener(getSympthomSelectionListener());
		sympthomsTable.setSelectionModel(listSelectionModel);

		ListSelectionModel valuesSelectionModel = valuesTable.getSelectionModel();
		valuesSelectionModel.addListSelectionListener(getValuesSelectionListener());
		valuesTable.setSelectionModel(valuesSelectionModel);

		sympthomsTable.changeSelection(0, 0, false, false);
		valuesTable.changeSelection(0, 0, false, false);

		JPanel pT = new JPanel(new FlowLayout());

		l = new JLabel(Messages.getString("BBTPrefsWidget.9")); //$NON-NLS-1$
		pT.add(l);

		rbCelsius = new JRadioButton(Messages.getString("BBTPrefsWidget.10")); //$NON-NLS-1$
		rbCelsius.setMnemonic(KeyEvent.VK_E);
		rbCelsius.addActionListener(getScaleActionListener());
		pT.add(rbCelsius);

		rbFahrenheit = new JRadioButton(Messages.getString("BBTPrefsWidget.11")); //$NON-NLS-1$
		rbFahrenheit.setMnemonic(KeyEvent.VK_F);
		rbFahrenheit.addActionListener(getScaleActionListener());
		pT.add(rbFahrenheit);

		ButtonGroup group = new ButtonGroup();
		group.add(rbCelsius);
		group.add(rbFahrenheit);

		c.gridy = 2;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.WEST;
		mainPanel.add(pT, c);

		return mainPanel;
	}

	/**
	 * @return a selection listener for the preferences sympthoms list
	 */
	private ListSelectionListener getSympthomSelectionListener() {
		return new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				//Ignore extra messages.
				if (e.getValueIsAdjusting()) return;

				//enable/disable the toolbar buttons
				checkPreferencesToolbarActiveness();


				//kick the values table to be refreshed
				if(valuesTable.isEditing()) {
					valuesTable.getCellEditor().stopCellEditing();
				}
				cachedSympthomID = sympthomsTable.getSelectedRow();
				((AbstractTableModel) valuesTable.getModel()).fireTableDataChanged();
			}
		};
	}

	/**
	 * @return a selection listener for the preferences values list
	 */
	private ListSelectionListener getValuesSelectionListener() {
		return new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				//Ignore extra messages.
				if (e.getValueIsAdjusting()) return;

				boolean upEnabled = false;
				boolean downEnabled = false;
				boolean removeEnabled = false;

				if(cachedSympthomID != -1) {

					//enable/disable the toolbar buttons
					int selectedRow = valuesTable.getSelectedRow();

					String[] shownValues = sympthomsContainer.getSympthomValues(cachedSympthomID);

					if(shownValues != null) { 
						upEnabled = (selectedRow>0) &&
						(shownValues[selectedRow]!=null); //$NON-NLS-1$

						downEnabled = (selectedRow>=0)&&
						(selectedRow<shownValues.length-1) &&
						(shownValues[selectedRow+1]!=null); //$NON-NLS-1$

						removeEnabled = (selectedRow>=0)&&
						(selectedRow<shownValues.length);
					}

				}
				valuesUpButton.setEnabled(upEnabled);
				valuesDownButton.setEnabled(downEnabled);
				valuesRemoveButton.setEnabled(removeEnabled);
			}
		};
	}

	private ActionListener getPreferencesActionListener() {
		return new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				int nextSwapRow = 0; //zero means no sympthoms swap

				int selectedSympthomsRow = sympthomsTable.getSelectedRow();
				int selectedValuesRow = valuesTable.getSelectedRow();

				stopEditing();

				String actionCommand = e.getActionCommand();

				if(actionCommand.equals("sympthomUp")) { //$NON-NLS-1$
					nextSwapRow = -1;
				} else 
					if(actionCommand.equals("sympthomDown")) { //$NON-NLS-1$
						nextSwapRow = 1;
					}

				if(nextSwapRow != 0) {
					int newPosition = selectedSympthomsRow+nextSwapRow;
					PCalendar.engine.swapSympthoms(newPosition, selectedSympthomsRow);

					((AbstractTableModel) sympthomsTable.getModel()).fireTableDataChanged();
					sympthomsTable.getSelectionModel().setSelectionInterval(newPosition, newPosition);
					sympthomsTable.changeSelection(newPosition, 0, false, false);
					return;
				}
				// end of sympthoms

				// values block
				if(actionCommand.equals("valueUp")) { //$NON-NLS-1$
					nextSwapRow = -1;
				} else 
					if(actionCommand.equals("valueDown")) { //$NON-NLS-1$
						nextSwapRow = 1;
					} else {
						nextSwapRow = 0;
					}
				if(nextSwapRow != 0) {
					int newPosition = selectedValuesRow+nextSwapRow;
					if((newPosition<0) || (selectedValuesRow<0)) return;

					PCalendar.engine.swapSympthomValues(cachedSympthomID, newPosition, selectedValuesRow);

					((AbstractTableModel) valuesTable.getModel()).fireTableDataChanged();
					valuesTable.changeSelection(newPosition, 0, false, false);
					return;
				}

				if(actionCommand.equals("valueAdd")) { //$NON-NLS-1$
					// add a new line after the currently selected one
					int newLinePos = selectedValuesRow+1;
					PCalendar.engine.insertSypmthomValue(cachedSympthomID, newLinePos);
					// switch the new row to the edit mode
					((AbstractTableModel) valuesTable.getModel()).fireTableDataChanged();
					valuesTable.requestFocus();
					valuesTable.editCellAt(newLinePos, 0);
					valuesTable.changeSelection(newLinePos, 0, false, false);
					return;
				} 

				if(actionCommand.equals("valueRemove")) { //$NON-NLS-1$
					if(selectedValuesRow<0) return;
					// Just show a confirmation request here
					int q_res = JOptionPane.showConfirmDialog(
							prefsSplitPane.getTopLevelAncestor(),
							Messages.getString("BBTPrefsWidget.12") + //$NON-NLS-1$
							Messages.getString("BBTPrefsWidget.13"), //$NON-NLS-1$
							Messages.getString("BBTPrefsWidget.14"), //$NON-NLS-1$
							JOptionPane.YES_NO_OPTION);
					if(q_res==JOptionPane.YES_OPTION) {
						PCalendar.engine.deleteSypmthomValue(cachedSympthomID, selectedValuesRow);
						((AbstractTableModel) valuesTable.getModel()).fireTableDataChanged();
						if(selectedValuesRow>=valuesTable.getRowCount()) {
							selectedValuesRow = valuesTable.getRowCount() - 1; 
						}
						valuesTable.changeSelection(selectedValuesRow, 0, false, false);
					}
				} 
			}
		};
	}

	/**
	 * Enables/disables the toolbar buttons
	 */
	protected void checkPreferencesToolbarActiveness() {
		int selectedRow = sympthomsTable.getSelectedRow();
		if(selectedRow == -1) return;
		String selectedSympthom = sympthomsContainer.getSympthoms()[selectedRow];
		boolean upEnabled = (selectedRow>0) &&
		(selectedSympthom!=null) &&
		(!selectedSympthom.equals("")); //$NON-NLS-1$
		sympthomsUpButton.setEnabled(upEnabled);

		boolean downEnabled = (selectedRow>=0)&&
		(selectedRow<3) &&
		(sympthomsContainer.getSympthoms()[selectedRow+1]!=null) &&
		(!sympthomsContainer.getSympthoms()[selectedRow+1].equals("")); //$NON-NLS-1$     
		sympthomsDownButton.setEnabled(downEnabled);

		boolean addEnabled = (selectedSympthom!=null) &&
		(!selectedSympthom.equals("")); 	//$NON-NLS-1$
		valuesAddButton.setEnabled(addEnabled);
	}

	private ActionListener getScaleActionListener() {
		return new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if(PCalendar.engine.isFileBBTCelsiusScale() == rbCelsius.isSelected()) return;
				int q_res = JOptionPane.showConfirmDialog(
						prefsSplitPane.getTopLevelAncestor(),
						Messages.getString("BBTPrefsWidget.15") + //$NON-NLS-1$
						Messages.getString("BBTPrefsWidget.16") + //$NON-NLS-1$
						Messages.getString("BBTPrefsWidget.17"), //$NON-NLS-1$
						Messages.getString("BBTPrefsWidget.18"), //$NON-NLS-1$
						JOptionPane.YES_NO_OPTION);
				if(q_res==JOptionPane.YES_OPTION) {
					PCalendar.engine.changeBBTScale(rbCelsius.isSelected());
				} else {
					// reload the scale state
					if(PCalendar.engine.isFileBBTCelsiusScale()) {
						rbCelsius.setSelected(true);
					} else {
						rbFahrenheit.setSelected(true);
					}
				}
			}
		};
	}

	/**
	 * This method is called before the dialog is going to be closed
	 */
	public void prepareForClose() {
		stopEditing();
	}

	/**
	 * Stops editing operations if any  
	 */
	private void stopEditing() {
		if(sympthomsTable.isEditing()) {
			sympthomsTable.getCellEditor().stopCellEditing();
		}

		if(valuesTable.isEditing()) {
			valuesTable.getCellEditor().stopCellEditing();
		}
	}

	public void refreshWidgets() {
		if(PCalendar.engine.isFileBBTCelsiusScale()) {
			rbCelsius.setSelected(true);
		} else {
			rbFahrenheit.setSelected(true);
		}
		sympthomsTable.changeSelection(0, 0, false, false);
	}
}
