/**
 *  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.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

import net.sf.linuxorg.pcal.MainWindow;
import net.sf.linuxorg.pcal.PCalDayInfo;
import net.sf.linuxorg.pcal.PCalendar;
import net.sf.linuxorg.pcal.common.gui.VerticalCaption;
import net.sf.linuxorg.pcal.engine.BBTSympthomsSetDefinition;
import net.sf.linuxorg.pcal.messages.Messages;

/**
 * This class provides BBT Table widget with contains the BBT table and the supplementing components.
 * Use the factory methods to get new instances of the widgets.
 * @author Mar'yan Rachynskyy
 */

public class BBTWidget {
	private static final String CLEAR = "<CLEAR>"; //$NON-NLS-1$ 
	private static final int BBT_TEMPERATURE_MIN_C = 360;
	private static final int BBT_TEMPERATURE_MIN_F = 968;
	public static final int BBT_TEMPERATURE_LEVELS_C = 16;
	public static final int BBT_TEMPERATURE_LEVELS_F = 22;

	private Date bbtStartDate = new Date();

	private JTable bbtTable;
	private BBTTableModel bbtTableModel;
	private JTable bbtRowHeader;
	private JScrollPane bbtScrollPane;
	private JLabel currentMonthLabel;
	private JPopupMenu bbtPopupMenu;
	private JPopupMenu sympthomsPopupMenu;
	private Action actionAddOvulation, actionDeleteRecord;

	private ActionListener sympthomsMenuActionListener;
	private Point popupActivatedCell = null;

	protected int bbtTemperatureLevelsCount = BBT_TEMPERATURE_LEVELS_C;
	private String [] verticalLabels;
	protected int verticalLabelsActiveCount = 0;
	private int genericRowHeight = 0;
	private int[] sympthomsRowHeights = new int[BBTSympthomsSetDefinition.BBT_SYMPTHOMS_COUNT];

	protected Date[] datesList = new Date[] {};
	protected String[] dateLabels = new String[] {};
	protected int[] bbtPointRow = new int[] {};
	private Color[] daysBGColors = new Color[] {};
	private Color[] daysFGColors = new Color[] {};
	private boolean[] isOvulationFlag = new boolean[] {};

	/**
	 * The main BBT Grid widget factory
	 * @param startDate the start date for the calendar. If null - the today date will be 
	 * centered.
	 * @return the new instance of the widget
	 */
	public JComponent getBBTWidget() {
		// the observations table

		bbtTableModel = new BBTTableModel(this);
		bbtTable = new JTable(bbtTableModel) {
			private static final long serialVersionUID = 1L;

			// delegate the painting stuff to the external function
			@Override
			protected void paintComponent(Graphics g) {
				super.paintComponent(g);
				paintBBTGraph(g);
			}
		};		
		bbtTable.setPreferredScrollableViewportSize(new Dimension(400, 70));
		bbtTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		bbtTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		bbtTable.getTableHeader().setResizingAllowed(false);
		bbtTable.getTableHeader().setReorderingAllowed(false);
		bbtTable.setRowSelectionAllowed(false);
		bbtTable.setColumnSelectionAllowed(false);
		bbtTable.setColumnSelectionAllowed(true);

		bbtTable.addKeyListener(getBBTKeyListener());
		bbtTable.addMouseListener(getBBTMouseListener());
		bbtTable.addMouseMotionListener(getBBTMouseMoutionListener());

		bbtTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				//Ignore extra messages.
				if (e.getValueIsAdjusting()) return;
				bbtTable.repaint();
			}
		});

		genericRowHeight = bbtTable.getRowHeight();

		resetTableColumns();
		bbtTable.setDefaultRenderer(Object.class, getBBTTableCellRenderer());

		bbtRowHeader = new JTable(getBBTRowHeadersModel());	

		//Calculate row header width basing on the current font
		FontMetrics fm = PCalendar.mainWindow.getGraphics().getFontMetrics();
		int rowHeaderWidth = fm.stringWidth(Messages.getString("BBTWidget.0")) + 8; //$NON-NLS-1$
		Dimension d = bbtRowHeader.getPreferredScrollableViewportSize();
		d.width = rowHeaderWidth;
		bbtRowHeader.setPreferredScrollableViewportSize(d);
		bbtRowHeader.setIntercellSpacing(new Dimension(0, 0));

		//bbtRowHeader.getColumnModel().getColumn(0).setPreferredWidth(); // the widest string
		bbtRowHeader.setDefaultRenderer(Object.class, new RowHeaderRenderer(bbtTable, this));

		bbtScrollPane = new JScrollPane(bbtTable);
		bbtScrollPane.setRowHeaderView(bbtRowHeader);
		bbtScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		//listen to the table resize
		bbtScrollPane.addComponentListener(
				new ComponentAdapter() {
					public void componentResized(ComponentEvent e) {
						int bbtDaysCount = bbtScrollPane.getViewportBorderBounds().width / genericRowHeight;
						if(dateLabels.length != bbtDaysCount) {
							generateHorizontalLabels(bbtDaysCount);
							resizeRowHeights();
						}
					}
				});

		actionAddOvulation = new ActionAddOvulation();
		actionDeleteRecord = new ActionDeleteRecord();

		bbtPopupMenu = new JPopupMenu();
		bbtPopupMenu.add(actionAddOvulation);
		bbtPopupMenu.add(actionDeleteRecord);
		sympthomsPopupMenu = new JPopupMenu();

		sympthomsMenuActionListener = getSympthomsMenuActionListener();

		ActionListener bbtActionListener = getBBTActionListener();

		JToolBar bbtToolBar = new JToolBar();
		bbtToolBar.setFloatable(false);
		bbtToolBar.add(MainWindow.createToolbarButton("16x16/first2.png", //$NON-NLS-1$ 
				"prevYear", Messages.getString("BBTWidget.1"),	//$NON-NLS-1$ //$NON-NLS-2$
				bbtActionListener));
		bbtToolBar.add(MainWindow.createToolbarButton("16x16/first.png", //$NON-NLS-1$ 
				"prevMonth", Messages.getString("BBTWidget.2"), //$NON-NLS-1$ //$NON-NLS-2$
				bbtActionListener));
		bbtToolBar.add(MainWindow.createToolbarButton("16x16/backward.png", //$NON-NLS-1$ 
				"prevDay", Messages.getString("BBTWidget.3"), //$NON-NLS-1$ //$NON-NLS-2$
				bbtActionListener));
		bbtToolBar.addSeparator();
		bbtToolBar.addSeparator();
		bbtToolBar.add(MainWindow.createToolbarButton("16x16/forward.png",	//$NON-NLS-1$ 
				"nextDay", Messages.getString("BBTWidget.4"),	//$NON-NLS-1$ //$NON-NLS-2$
				bbtActionListener));
		bbtToolBar.add(MainWindow.createToolbarButton("16x16/last.png", 	//$NON-NLS-1$ 
				"nextMonth", Messages.getString("BBTWidget.5"),	//$NON-NLS-1$ //$NON-NLS-2$
				bbtActionListener));
		bbtToolBar.add(MainWindow.createToolbarButton("16x16/last2.png",	//$NON-NLS-1$ 
				"nextYear", Messages.getString("BBTWidget.6"),	//$NON-NLS-1$ //$NON-NLS-2$
				bbtActionListener));
		bbtToolBar.addSeparator();
		bbtToolBar.add(MainWindow.createToolbarButton("16x16/jump-to.png",	//$NON-NLS-1$ 
				"centerToday", Messages.getString("BBTWidget.7"),	//$NON-NLS-1$ //$NON-NLS-2$
				bbtActionListener));
		bbtToolBar.addSeparator();

		currentMonthLabel = new JLabel();
		bbtToolBar.add(currentMonthLabel);

		JPanel bbtPanel = new JPanel(new BorderLayout());
		bbtPanel.add(bbtScrollPane, BorderLayout.CENTER);
		bbtPanel.add(bbtToolBar, BorderLayout.PAGE_START);

		return bbtPanel;
	}

	/**
	 * Rebuilds the table columns data
	 */
	private void resetTableColumns() {
		TableColumnModel columnModel = bbtTable.getColumnModel();
		for(int i =0;i<bbtTable.getColumnCount(); i++) {
			TableColumn column = columnModel.getColumn(i); 
			column.setPreferredWidth(genericRowHeight);
			column.setHeaderRenderer(getVerticalHeaderCellRenderer());
		}
	}

	/**
	 * @return the bbt row header model
	 */
	private TableModel getBBTRowHeadersModel() {
		TableModel tm = new AbstractTableModel() {
			private static final long serialVersionUID = 1L;

			@Override
			public int getColumnCount() {
				return 1;
			}

			@Override
			public int getRowCount() {
				return verticalLabelsActiveCount;
			}

			@Override
			public Object getValueAt(int rowIndex, int columnIndex) {
				return verticalLabels[rowIndex];
			}
		};
		return tm;
	}

	/**
	 * Fills the temperature scale and the sypmthoms list data
	 */
	private void generateVerticalLabels() {

		verticalLabelsActiveCount = verticalLabels.length;

		if(PCalendar.engine.isFileBBTCelsiusScale()) {
			// generate the scale in Celsius
			Double d = BBT_TEMPERATURE_MIN_C / 10.0;
			for(int i=bbtTemperatureLevelsCount-1; i>=0; i--) {
				verticalLabels[i] = String.format("%1$4.1f", d);	//$NON-NLS-1$
				d += 0.1;
			}
		} else {
			Double d = BBT_TEMPERATURE_MIN_F / 10.0;
			for(int i=bbtTemperatureLevelsCount-1; i>=0; i--) {
				verticalLabels[i] = String.format("%1$4.1f", d);	//$NON-NLS-1$
				d += 0.1;
			}
		}
		verticalLabels[bbtTemperatureLevelsCount] = ""; //$NON-NLS-1$

		FontMetrics fm = PCalendar.mainWindow.getGraphics().getFontMetrics();

		for(int i=bbtTemperatureLevelsCount + 1; i<verticalLabels.length; i++) {
			int sympthomIndex = i - bbtTemperatureLevelsCount -1;
			verticalLabels[i] = PCalendar.engine.getBBTSympthoms().getSympthomName(sympthomIndex);

			if((verticalLabels[i]!=null) && (!verticalLabels[i].trim().equals(""))) {  //$NON-NLS-1$
				//calculate the max of the sympthom label and values widths
				int maxWidth = fm.stringWidth(verticalLabels[i]);
				for(String sympthomValue : PCalendar.engine.getBBTSympthoms().getSympthomValues(sympthomIndex)) {
					int iwidth = fm.stringWidth(sympthomValue);
					if(maxWidth<iwidth) {
						maxWidth = iwidth;
					}
				}
				//add a margin for the height
				maxWidth += 4;
				sympthomsRowHeights[sympthomIndex] = maxWidth;
			} else {
				verticalLabelsActiveCount = i;
				break;
			}
		}
		// request the table row header to rebuild itself
		bbtRowHeader.tableChanged(null);
	}

	private void resizeRowHeights() {
		for(int i=0; i<verticalLabelsActiveCount; i++) {
			if(i<(bbtTemperatureLevelsCount + 1)) {
				bbtTable.setRowHeight(i, genericRowHeight);
				bbtRowHeader.setRowHeight(i, genericRowHeight);
			} else {
				int sympthomIndex = i - bbtTemperatureLevelsCount -1;
				bbtTable.setRowHeight(i, sympthomsRowHeights[sympthomIndex]);
				bbtRowHeader.setRowHeight(i, sympthomsRowHeights[sympthomIndex]);
			}
		}
	}

	/**
	 * @param bbtDaysCount - if 0 - the days count is recalculated, otherwise, the table is set to fit specified days count
	 */
	private void generateHorizontalLabels(int bbtDaysCount) {
		// determine how many days can be displayed in the current widget
		if(bbtDaysCount == 0) {
			bbtDaysCount = bbtScrollPane.getViewportBorderBounds().width / genericRowHeight;
		}

		datesList = new Date[bbtDaysCount];
		dateLabels = new String[bbtDaysCount];
		bbtPointRow = new int[bbtDaysCount];
		daysBGColors = new Color[bbtDaysCount];
		daysFGColors = new Color[bbtDaysCount];
		isOvulationFlag = new boolean[bbtDaysCount];

		if(bbtDaysCount>0) {
			Calendar calendar = new GregorianCalendar();
			calendar.setTime(bbtStartDate);

			int lastRecordedMonthIndex = calendar.get(Calendar.MONTH); 
			String monthsList = MainWindow.monthNames[lastRecordedMonthIndex];

			String yearString = String.valueOf(calendar.get(Calendar.YEAR));
			boolean celsiusScale = PCalendar.engine.isFileBBTCelsiusScale();

			for(int i=0; i<bbtDaysCount; i++) {
				datesList[i] = calendar.getTime();
				dateLabels[i] = String.valueOf(calendar.get(Calendar.DAY_OF_MONTH));

				PCalDayInfo dayInfo = PCalendar.engine.getDayInfo(calendar.getTime());
				Color[] dayColors = PCalendar.mainWindow.getDayColors(dayInfo);
				daysBGColors[i] = dayColors[0];
				daysFGColors[i] = dayColors[1];
				isOvulationFlag[i] = (dayInfo == null)?false:dayInfo.ovulation;

				// get the BBT records as well
				int temperature = PCalendar.engine.getBBT(calendar.getTime());
				if(temperature>0) {
					bbtPointRow[i] = (celsiusScale?BBT_TEMPERATURE_MIN_C:BBT_TEMPERATURE_MIN_F) + 
							bbtTemperatureLevelsCount - temperature - 1;
					if(bbtPointRow[i] > bbtTemperatureLevelsCount) bbtPointRow[i] = -1; 
				} else {
					bbtPointRow[i] = -1;
				}

				int currentDateMonthIndex = calendar.get(Calendar.MONTH);
				if(lastRecordedMonthIndex != currentDateMonthIndex) {
					monthsList+= Messages.getString("BBTWidget.8") + MainWindow.monthNames[currentDateMonthIndex]; //$NON-NLS-1$
					lastRecordedMonthIndex = currentDateMonthIndex;
				}
				calendar.add(Calendar.DATE, 1);
			}
			currentMonthLabel.setText(yearString+Messages.getString("BBTWidget.9")+monthsList); //$NON-NLS-1$
			bbtTableModel.fireTableStructureChanged();
			resetTableColumns();
		}
	}


	private DefaultTableCellRenderer getVerticalHeaderCellRenderer() {
		return new DefaultTableCellRenderer() {
			private static final long serialVersionUID = 1L;

			public Component getTableCellRendererComponent (JTable table,
					Object value, boolean isSelected, boolean hasFocus,
					int row, int column) {

				JLabel label = new JLabel ();
				Icon icon = VerticalCaption.getVerticalCaption (label, value.toString (), false);
				label.setIcon (icon);
				label.setHorizontalAlignment (JLabel.CENTER);
				label.setBorder (new BevelBorder (BevelBorder.RAISED));
				return label;
			}

		};
	}

	private DefaultTableCellRenderer getBBTTableCellRenderer() {
		return new DefaultTableCellRenderer() {
			private static final long serialVersionUID = 1L;

			public Component getTableCellRendererComponent (JTable table,
					Object value, boolean isSelected, boolean hasFocus,
					int row, int column) {
				if(row < bbtTemperatureLevelsCount) {
					Component renderer = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
					if((bbtTable.getSelectedRow() == row) || (bbtTable.getSelectedColumn() == column)) {
						renderer.setBackground(UIManager.getColor("Table.selectionBackground")); //$NON-NLS-1$
					} else {
						renderer.setBackground(daysBGColors[column]);
						renderer.setForeground(daysFGColors[column]);
					}
					return renderer;
				} else				
					if(row == bbtTemperatureLevelsCount) {
						Component renderer = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
						if((bbtTable.getSelectedRow() == row) || (bbtTable.getSelectedColumn() == column)) {
							renderer.setBackground(UIManager.getColor("Table.selectionBackground")); //$NON-NLS-1$
						} else {
							renderer.setBackground(UIManager.getColor("TableHeader.background")); //$NON-NLS-1$
						}
						return renderer;
					} else {
						// vertical renderers for sympthoms
						JLabel label = new JLabel ();
						label.setOpaque(true);
						Icon icon = VerticalCaption.getVerticalCaption (label, value.toString (), false);
						label.setIcon (icon);
						label.setText(null);
						label.setHorizontalAlignment (JLabel.CENTER);

						if(hasFocus) {
							Border border = null;
							if (isSelected) {
								border = UIManager.getBorder("Table.focusSelectedCellHighlightBorder"); //$NON-NLS-1$
							}
							if (border == null) {
								border = UIManager.getBorder("Table.focusCellHighlightBorder"); //$NON-NLS-1$
							}
							label.setBorder(border);
						}
						if(icon.getIconHeight() > table.getRowHeight(row)) {
							table.setRowHeight(row, icon.getIconHeight());
						}
						if((bbtTable.getSelectedRow() == row) || (bbtTable.getSelectedColumn() == column)) {
							label.setBackground(UIManager.getColor("Table.selectionBackground")); //$NON-NLS-1$
						}
						return label;
					}
			}
		};
	}

	private ActionListener getBBTActionListener() {
		return new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				String actionCommand = e.getActionCommand();
				GregorianCalendar calendar = new GregorianCalendar();
				calendar.setTime(bbtStartDate);
				if(actionCommand.equals("prevYear")) { //$NON-NLS-1$
					calendar.add(Calendar.YEAR, -1);
				} else 
					if(actionCommand.equals("prevMonth")) { //$NON-NLS-1$
						calendar.add(Calendar.MONTH, -1);
					} else 
						if(actionCommand.equals("prevDay")) { //$NON-NLS-1$
							calendar.add(Calendar.DATE, -1);
						} else 
							if(actionCommand.equals("nextDay")) { //$NON-NLS-1$
								calendar.add(Calendar.DATE, 1);
							}	else 
								if(actionCommand.equals("nextMonth")) { //$NON-NLS-1$
									calendar.add(Calendar.MONTH, 1);
								} else 
									if(actionCommand.equals("nextYear")) { //$NON-NLS-1$
										calendar.add(Calendar.YEAR, 1);
									} else 
										if(actionCommand.equals("centerToday")) { //$NON-NLS-1$
											Calendar temp_calendar1 = new GregorianCalendar();
											temp_calendar1.setTime(new Date());
											calendar.set(temp_calendar1.get(Calendar.YEAR), temp_calendar1.get(Calendar.MONTH), temp_calendar1.get(Calendar.DAY_OF_MONTH));
											calendar.add(Calendar.DATE, - dateLabels.length/2);
										}
				bbtStartDate = calendar.getTime();
				generateHorizontalLabels(dateLabels.length);
				resizeRowHeights();
			}
		};
	}

	/**
	 * Make sure the dialog is up to date according to the current preferences and engine state
	 */
	public void refreshWidgets(Date startDate) {
		if(startDate == null) {
			// get rid of hours:minutes:seconds
			Calendar temp_calendar1 = new GregorianCalendar();
			temp_calendar1.setTime(new Date());
			Calendar temp_calendar2 = new GregorianCalendar(temp_calendar1.get(Calendar.YEAR), temp_calendar1.get(Calendar.MONTH), temp_calendar1.get(Calendar.DAY_OF_MONTH));
			startDate = temp_calendar2.getTime();
		}
		bbtStartDate = startDate;

		bbtTemperatureLevelsCount = PCalendar.engine.isFileBBTCelsiusScale() ? BBT_TEMPERATURE_LEVELS_C : BBT_TEMPERATURE_LEVELS_F;
		verticalLabels = new String[bbtTemperatureLevelsCount + BBTSympthomsSetDefinition.BBT_SYMPTHOMS_COUNT + 1]; 

		generateVerticalLabels();
		generateHorizontalLabels(0);
		resizeRowHeights();

	}

	/**
	 * This method is called before the dialog is going to be closed
	 */
	public void prepareForClose() {
		if(bbtTable.isEditing()) {
			bbtTable.getCellEditor().stopCellEditing();
		}
	}

	private MouseListener getBBTMouseListener() {
		return new MouseAdapter() {

			@Override
			public void mouseClicked(MouseEvent e) {
				if(e.getClickCount() > 1) {
					int selectedRow = bbtTable.getSelectedRow(); 
					if(selectedRow < 0) return; 
					if(selectedRow < bbtTemperatureLevelsCount) {
						int selectedColumn = bbtTable.getSelectedColumn();
						toggleBBT(selectedRow, selectedColumn);
					} else
						if(selectedRow > bbtTemperatureLevelsCount) {
							showSympthomsPopup(e.getPoint());
						}
				}				
			}
			@Override
			public void mousePressed(MouseEvent e) {
				maybeShowPopup(e);
			}
			@Override
			public void mouseReleased(MouseEvent e) {
				maybeShowPopup(e);
			}

			private void maybeShowPopup(MouseEvent e) {
				if (e.isPopupTrigger()) {

					int popupRow = bbtTable.rowAtPoint(e.getPoint());

					if(popupRow < 0) return;

					if(popupRow <= bbtTemperatureLevelsCount) {
						// show the BBT Graph menu
						int selectedColumn = bbtTable.getSelectedColumn();
						boolean enabledOvulation = true;
						if(selectedColumn > 0) {
							if(isOvulationFlag[selectedColumn]) {
								enabledOvulation = false;
							}
						}
						actionAddOvulation.setEnabled(enabledOvulation);
						actionDeleteRecord.setEnabled(!enabledOvulation);

						bbtPopupMenu.show(e.getComponent(),
								e.getX(), e.getY());
					} else {
						// show the Sympthoms menu
						showSympthomsPopup(e.getPoint());
					}
				}
			}
		};
	}

	/**
	 * @param point the point where popup should be activated
	 */
	private void showSympthomsPopup(Point point) {
		sympthomsPopupMenu.removeAll();

		popupActivatedCell = new Point(bbtTable.columnAtPoint(point), bbtTable.rowAtPoint(point));

		JMenuItem clearMenuItem = sympthomsPopupMenu.add(Messages.getString("BBTWidget.10"));				 //$NON-NLS-1$
		clearMenuItem.addActionListener(sympthomsMenuActionListener);
		clearMenuItem.setActionCommand(CLEAR);
		sympthomsPopupMenu.addSeparator();

		int sympthomIndex = popupActivatedCell.y - bbtTemperatureLevelsCount - 1;				 
		String[] values = PCalendar.engine.getBBTSympthoms().getSympthomValues(sympthomIndex);
		for(int i = 0; i<values.length; i++) {
			JMenuItem menuItem = sympthomsPopupMenu.add(values[i]);
			menuItem.setActionCommand(""+i); //$NON-NLS-1$
			menuItem.addActionListener(sympthomsMenuActionListener);
		}
		sympthomsPopupMenu.show(bbtTable, point.x, point.y);
	}

	private KeyListener getBBTKeyListener() {
		return new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if(e.getKeyCode() == KeyEvent.VK_SPACE) {
					int selectedRow = bbtTable.getSelectedRow(); 
					if(selectedRow >= 0) {
						if(selectedRow < bbtTemperatureLevelsCount) {
							int selectedColumn = bbtTable.getSelectedColumn();
							toggleBBT(selectedRow, selectedColumn);
						} else
							if(selectedRow > bbtTemperatureLevelsCount) {
								int selectedColumn = bbtTable.getSelectedColumn();
								if(selectedColumn > 0) {
									showSympthomsPopup(bbtTable.getCellRect(selectedRow, selectedColumn, true).getLocation());
								}
							}
					}
				}				
			}
		};
	}

	private MouseMotionListener getBBTMouseMoutionListener() {
		return new MouseMotionListener() {
			@Override
			public void mouseMoved(MouseEvent e) {
				if(bbtPopupMenu.isShowing()) return;
				Point mouseCoordinates = e.getPoint();
				int selectedRow = bbtTable.rowAtPoint(mouseCoordinates);
				int selectedColumn = bbtTable.columnAtPoint(mouseCoordinates);
				if((selectedRow>=0) && (selectedColumn>=0)) {
					bbtTable.changeSelection(selectedRow, selectedColumn, false, false);
				}
			}

			@Override
			public void mouseDragged(MouseEvent e) {
			}
		};
	}

	private void toggleBBT(int selectedRow, int selectedColumn) {
		if(selectedColumn<0) return;

		Date selectedDate = datesList[selectedColumn];
		boolean celsiusScale = PCalendar.engine.isFileBBTCelsiusScale();

		int temperature = (celsiusScale?BBT_TEMPERATURE_MIN_C:BBT_TEMPERATURE_MIN_F) + bbtTemperatureLevelsCount - selectedRow - 1;

		int oldTemperature = PCalendar.engine.getBBT(selectedDate);

		if(oldTemperature == temperature) {
			PCalendar.engine.removeBBT(selectedDate);
			bbtPointRow[selectedColumn] = -1;
		} else {
			PCalendar.engine.addBBT(selectedDate, temperature);
			bbtPointRow[selectedColumn] = selectedRow;
		}
		bbtTable.repaint();
	}

	private ActionListener getSympthomsMenuActionListener() {
		return new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if(popupActivatedCell == null) return;
				if(popupActivatedCell.y <= bbtTemperatureLevelsCount) return;

				int sympthomIndex = popupActivatedCell.y - bbtTemperatureLevelsCount - 1;

				int newValue = -1;
				if(!e.getActionCommand().equals(CLEAR)) {
					try {
						newValue = Integer.parseInt(e.getActionCommand());
					} catch (NumberFormatException ex) {
						return;
					}
				}
				PCalendar.engine.setDateInteger(datesList[popupActivatedCell.x], sympthomIndex, newValue);
				bbtTable.repaint();
			}
		};
	}

	private void paintBBTGraph(Graphics g) {
		final int POINT_RADIUS = 6; 
		final int POINT_D = POINT_RADIUS*2;

		// paintint he BBT Graph over the table
		Graphics2D g2d = (Graphics2D)g.create();
		g2d.setColor(Color.RED);
		g2d.setStroke(new BasicStroke(3));

		int prevX = -1;
		int prevY = -1;

		for(int i=0; i<bbtPointRow.length; i++) {
			if(bbtPointRow[i]>=0) {
				//paint the point
				Rectangle currentCell = bbtTable.getCellRect(bbtPointRow[i], i, true);
				int currentX = currentCell.x + currentCell.width/2;
				int currentY = currentCell.y + currentCell.height/2;
				g2d.fillOval(currentX - POINT_RADIUS, currentY - POINT_RADIUS, POINT_D,  POINT_D);				

				if(prevX>0) {
					// connect the point to the previous one
					g2d.drawLine(prevX, prevY, currentX, currentY);
				}
				prevX = currentX;
				prevY = currentY;
			} else {
				prevX = -1;
				prevY = -1;
			}
		}

	}

	public class ActionAddOvulation extends AbstractAction {
		private static final long serialVersionUID = 1L;
		public ActionAddOvulation() {
			super(Messages.getString("MainWindow.131"), MainWindow.createResoruceIcon("16x16/ovulation.png"));   //$NON-NLS-1$ //$NON-NLS-2$
			putValue(SHORT_DESCRIPTION, Messages.getString("MainWindow.132"));  //$NON-NLS-1$
			putValue(MNEMONIC_KEY, Messages.getMnemonic("MainWindow.131.Mnemonic")); //$NON-NLS-1$
		}
		public void actionPerformed(ActionEvent e) {
			int selectedColumn = bbtTable.getSelectedColumn();

			if(selectedColumn < 0) return;

			PCalendar.engine.addOvulationDate(datesList[selectedColumn]);

			//changing the records might impact the colors
			refreshColors();
			bbtTable.repaint();
		}
	}

	public class ActionDeleteRecord extends AbstractAction {
		private static final long serialVersionUID = 6375480347509749765L;
		public ActionDeleteRecord() {
			super(Messages.getString("MainWindow.147"), MainWindow.createResoruceIcon("16x16/remove.png"));   //$NON-NLS-1$ //$NON-NLS-2$
			putValue(SHORT_DESCRIPTION, Messages.getString("MainWindow.149"));  //$NON-NLS-1$
			putValue(MNEMONIC_KEY, Messages.getMnemonic("MainWindow.147.Mnemonic")); //$NON-NLS-1$
		}
		public void actionPerformed(ActionEvent e) {
			int selectedColumn = bbtTable.getSelectedColumn();

			if(selectedColumn < 0) return;

			PCalendar.engine.removeDateRecord(datesList[selectedColumn]);

			//changing the records might impact the colors
			refreshColors();		
			bbtTable.repaint();
		}
	}	

	private void refreshColors() {
		Calendar calendar = new GregorianCalendar();
		calendar.setTime(bbtStartDate);

		for(int i=0; i<dateLabels.length; i++) {
			PCalDayInfo dayInfo = PCalendar.engine.getDayInfo(calendar.getTime());
			Color[] dayColors = PCalendar.mainWindow.getDayColors(dayInfo);
			daysBGColors[i] = dayColors[0];
			daysFGColors[i] = dayColors[1];
			isOvulationFlag[i] = (dayInfo == null)?false:dayInfo.ovulation;
			calendar.add(Calendar.DATE, 1);
		}
	}

	/**
	 * @return the bbtStartDate
	 */
	public Date getBbtStartDate() {
		return bbtStartDate;
	}
}