package tim.prune.function.deletebydate;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Date;
import java.util.TimeZone;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.border.EtchedBorder;
import javax.swing.table.AbstractTableModel;

import tim.prune.App;
import tim.prune.DataSubscriber;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.TimezoneHelper;
import tim.prune.data.DataPoint;
import tim.prune.function.compress.MarkAndDeleteFunction;

/**
 * Function to select a date or dates,
 * and mark the corresponding points for deletion
 */
public class DeleteByDateFunction extends MarkAndDeleteFunction
{
	/** dialog for selecting dates */
	private JDialog _dialog = null;
	/** table */
	private JTable _infoTable = null;
	// panel for selection buttons
	private JPanel _selButtonPanel = null;
	private JButton _keepSelectedButton = null;
	private JButton _delSelectedButton = null;
	/** date info list */
	private DateInfoList _infoList = new DateInfoList();


	/**
	 * Constructor
	 * @param inApp App object
	 */
	public DeleteByDateFunction(App inApp)
	{
		super(inApp);
	}

	@Override
	public String getNameKey() {
		return "function.deletebydate";
	}

	@Override
	public void begin()
	{
		// Select the current timezone
		TimeZone selectedTimezone = TimezoneHelper.getSelectedTimezone(getConfig());
		DateInfo.setTimezone(selectedTimezone);

		// Make a list of which dates are present in the track
		_infoList.clearAll();
		final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
		for (int i=0; i<numPoints; i++)
		{
			DataPoint point = _app.getTrackInfo().getTrack().getPoint(i);
			if (point != null)
			{
				if (point.hasTimestamp()) {
					_infoList.addPoint(point.getTimestamp().getCalendar(selectedTimezone).getTime());
				}
				else {
					_infoList.addPoint(null); // no timestamp available
				}
			}
		}

		// Complain if there is only one entry in the list - this means all points are on the same day
		if (_infoList.getNumEntries() < 2)
		{
			_app.showErrorMessage(getNameKey(), "dialog.deletebydate.onlyonedate");
		}
		else
		{
			// Create and build dialog if necessary
			if (_dialog == null)
			{
				_dialog = new JDialog(_parentFrame, getName(), true);
				_dialog.setLocationRelativeTo(_parentFrame);
				_dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
				_dialog.getContentPane().add(makeDialogComponents());
				_dialog.pack();
			}
			_infoTable.clearSelection();
			_selButtonPanel.setVisible(_infoList.getNumEntries() > 4);
			enableButtons();
			// Show dialog
			_dialog.setVisible(true);
		}
	}

	/**
	 * Create dialog components
	 * @return Panel containing all gui elements in dialog
	 */
	private Component makeDialogComponents()
	{
		JPanel dialogPanel = new JPanel();
		dialogPanel.setLayout(new BorderLayout(5, 5));
		// Label at top
		JLabel topLabel = new JLabel(I18nManager.getText("dialog.deletebydate.intro"));
		topLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		dialogPanel.add(topLabel, BorderLayout.NORTH);

		// close window if escape pressed
		KeyAdapter escListener = new KeyAdapter() {
			public void keyReleased(KeyEvent inE) {
				if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
					_dialog.dispose();
				}
			}
		};

		_infoTable = new JTable(new DeletionTableModel(_infoList));
		_infoTable.getTableHeader().addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent mouseEvent)
			{
				int index = _infoTable.convertColumnIndexToModel(_infoTable.columnAtPoint(mouseEvent.getPoint()));
				if (index >= 2) {
					modifyAllCheckboxes(index == 3); // parameter is true for delete, false for keep
				}
			}
		});
		_infoTable.getSelectionModel().addListSelectionListener(e -> enableButtons());

		JScrollPane pane = new JScrollPane(_infoTable);
		pane.setPreferredSize(new Dimension(300, 180));
		pane.setBorder(BorderFactory.createEmptyBorder(2, 50, 2, 50));
		JPanel middlePanel = new JPanel();
		middlePanel.setLayout(new BorderLayout());
		middlePanel.add(pane, BorderLayout.CENTER);
		_selButtonPanel = new JPanel();
		_selButtonPanel.setBorder(BorderFactory.createCompoundBorder(
			BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
		);
		_selButtonPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
		_keepSelectedButton = new JButton(I18nManager.getText("button.keepselected"));
		_keepSelectedButton.addActionListener(e -> changeSelectedRowsToKeep());
		_selButtonPanel.add(_keepSelectedButton);
		_delSelectedButton = new JButton(I18nManager.getText("button.deleteselected"));
		_delSelectedButton.addActionListener(e -> changeSelectedRowsToDelete());
		_selButtonPanel.add(_delSelectedButton);
		middlePanel.add(_selButtonPanel, BorderLayout.SOUTH);
		dialogPanel.add(middlePanel, BorderLayout.CENTER);

		// button panel at bottom
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
		// OK button
		JButton okButton = new JButton(I18nManager.getText("button.ok"));
		okButton.addActionListener(e -> performDelete());
		buttonPanel.add(okButton);
		okButton.addKeyListener(escListener);
		// Cancel button
		JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
		cancelButton.addActionListener(e-> _dialog.dispose());
		cancelButton.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent inE) {
				if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
			}
		});
		buttonPanel.add(cancelButton);
		dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
		return dialogPanel;
	}

	/**
	 * Select a column of checkboxes
	 * @param isDelete true for delete all, false for keep all
	 */
	private void modifyAllCheckboxes(boolean isDelete)
	{
		for (int rowIndex=0; rowIndex<_infoList.getNumEntries(); rowIndex++) {
			_infoList.getDateInfo(rowIndex).setDeleteFlag(isDelete);
		}
		enableButtons();
	}

	private void changeSelectedRowsToKeep() {
		changeSelectedRows(false);
	}

	private void changeSelectedRowsToDelete() {
		changeSelectedRows(true);
	}

	/**
	 * Change the selected rows to either all keep or all delete
	 * @param isDelete true for delete, false for keep
	 */
	private void changeSelectedRows(boolean isDelete)
	{
		int firstRow = -1, lastRow = -1;
		for (int rowIndex : _infoTable.getSelectedRows())
		{
			if (firstRow == -1) {firstRow = rowIndex;}
			_infoList.getDateInfo(rowIndex).setDeleteFlag(isDelete);
			lastRow = rowIndex;
		}
		// Make sure all rows between first and last updated ones are updated in the table
		((AbstractTableModel) _infoTable.getModel()).fireTableRowsUpdated(firstRow, lastRow);
		enableButtons();
	}

	/**
	 * Enable or disable the keepSelected and deleteSelected buttons
	 * according to the current selection
	 */
	private void enableButtons()
	{
		boolean hasKeep = false, hasDelete = false;
		for (int rowIndex : _infoTable.getSelectedRows())
		{
			if (_infoList.getDateInfo(rowIndex).getDeleteFlag()) {
				hasDelete = true;
			}
			else {
				hasKeep = true;
			}
		}
		_delSelectedButton.setEnabled(hasKeep);
		_keepSelectedButton.setEnabled(hasDelete);
	}

	/**
	 * Do the actual point deletion according to the
	 * selected rows in the table
	 */
	private void performDelete()
	{
		int numMarked = 0;
		final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
		final int numDates = _infoList.getNumEntries();
		_app.getTrackInfo().clearAllMarkers();
		// Loop over all points to mark each one for deletion or not
		for (int p=0; p<numPoints; p++)
		{
			DataPoint point = _app.getTrackInfo().getTrack().getPoint(p);
			if (point != null)
			{
				final Date date = (point.hasTimestamp() ?
					point.getTimestamp().getCalendar(TimezoneHelper.getSelectedTimezone(getConfig())).getTime()
					: null);
				boolean pointMarked = false;
				// Try to match each of the date info objects in the list
				for (int d=0; d<numDates; d++)
				{
					DateInfo info = _infoList.getDateInfo(d);
					if ( (info.isDateless() && date == null) // matches dateless
					  || (!info.isDateless() && date != null && info.isSameDate(date)))
					{
						pointMarked = info.getDeleteFlag();
						break;
					}
				}
				if (pointMarked) {
					_app.getTrackInfo().markPointForDeletion(p);
					numMarked++;
				}
			}
		}
		// Now points have been marked, we can ask user to delete them (or delete automatically)
		if (numMarked > 0) {
			optionallyDeleteMarkedPoints(numMarked);
		}
		else
		{
			// Nothing to be deleted
			// Delete flags might have been reset, so refresh display
			UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
		}
		_dialog.dispose();
	}
}
