/*
 * This file is a part of EpnTAPClient.
 * This program aims to provide EPN-TAP support for software clients, like CASSIS spectrum analyzer.
 * See draft specifications: https://voparis-confluence.obspm.fr/pages/viewpage.action?pageId=559861
 * Copyright (C) 2016 Institut de Recherche en Astrophysique et Planétologie.
 *
 * 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 eu.omp.irap.vespa.votable.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import eu.omp.irap.vespa.votable.votabledata.VOTableData;

/**
 * The main class of the View of the application.
 *
 * @author N. Jourdane
 */
public class VOTablePanelView extends JPanel {

	/** The logger for the class VOTableView. */
	private static final Logger LOGGER = Logger.getLogger(VOTablePanelView.class.getName());

	/** The serial version UID. */
	private static final long serialVersionUID = 1L;

	/** The JTable component where the data are displayed. */
	private JTable table;

	/** The DataModel representing the VOTable data in the JTable element. */
	private DefaultTableModel tableData;

	/** Optional listener of VOTable, to handle selection events. */
	private VOTablePanelListener listener;

	/** The indexes of the selected rows, from a model perspective. */
	private List<Integer> selectedRows;

	/** The VOTableData used for the table. */
	private VOTableData data;


	/**
	 * Method constructor
	 *
	 * @param listener The listener of VOTable view, to handle selection events.
	 */
	public VOTablePanelView(VOTablePanelListener listener) {
		this.listener = listener;
		selectedRows = new ArrayList<>();
		tableData = new DefaultTableModel() {

			/** The serial version UID. */
			private static final long serialVersionUID = 1L;

			@Override
			public boolean isCellEditable(int row, int column) {
				return false;
			}
		};

		table = new JTable(tableData);
		table.setRowSelectionAllowed(true);
		table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		buildVOTableView();
	}

	/** Private constructor to hide the implicit public one. */
	@SuppressWarnings("unused")
	private VOTablePanelView() {
	}

	/**
	 * Display an error message to the user.
	 *
	 * @param message A short version of the message.
	 * @param error The exception related to the error.
	 */
	public void displayError(String message, Exception error) {
		JOptionPane.showMessageDialog(this, error.getMessage(), message, JOptionPane.ERROR_MESSAGE);
	}

	/**
	 * Fill the JTable.
	 *
	 * @param data The VoTable data displayed on the JTable
	 */
	public void fillTable(VOTableData data) {
		if (data == null) {
			LOGGER.warning("voTableData is null. can not fill the JTable.");
			return;
		}
		this.data = data;
		String[] columns;
		Object[][] rows;
		LOGGER.info("Filling table: " + data.getNbColumns() + " columns, " + data.getNbRows()
				+ " rows.");
		columns = data.getColumnsName();
		rows = data.getDataArray();
		tableData.setDataVector(rows, columns);
		tableData.fireTableStructureChanged();
		tableData.fireTableDataChanged();

		if (rows.length != 0) {
			table.setRowSelectionInterval(0, 0);
		}
		if (columns.length >= 10) {
			table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		}
		updateColumnsSize();
	}

	/**
	 * @return The indexes of the selected rows, from a <b>MODEL</b> perspective.
	 */
	public List<Integer> getSelectedRows() {
		return selectedRows;
	}

	/**
	 * @return The JTable element.
	 */
	public JTable getTable() {
		return table;
	}

	/**
	 * @param column The column index of the data to get.
	 * @param row The row index of the data to get.
	 * @return The element, can be a String, a integer or something else, according to the voTable
	 *         content.
	 */
	public Object getValueAt(int column, int row) {
		return tableData.getValueAt(row, column);
	}

	/** @return The path of the VOTable related to the view. */
	public String getVOTablePath() {
		return listener.getVOTablePath();
	}

	/**
	 * Build the VOTable panel and add the graphical elements to it.
	 */
	private void buildVOTableView() {
		setLayout(new BorderLayout());

		table.setRowSorter(new TableRowSorter<TableModel>(tableData));

		add(new JScrollPane(table,
				ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER);

		getTable().getSelectionModel().addListSelectionListener(new ListSelectionListener() {

			@Override
			public void valueChanged(ListSelectionEvent e) {
				ListSelectionModel lsm = (ListSelectionModel) e.getSource();
				List<Integer> newSelectedRows = new ArrayList<>();

				if (!lsm.isSelectionEmpty()) {
					int minIndex = lsm.getMinSelectionIndex();
					int maxIndex = lsm.getMaxSelectionIndex();
					for (int i = minIndex; i <= maxIndex; i++) {
						if (lsm.isSelectedIndex(i)) {
							newSelectedRows.add(table.convertRowIndexToModel(i));
						}
					}
				}
				if (!newSelectedRows.equals(selectedRows)) {
					selectedRows = newSelectedRows;
					listener.onRowsSelected();
				}
			}
		});
	}

	/**
	 * Update the columns size.
	 */
	private void updateColumnsSize() {
		int width;
		Component comp;
		for (int column = 0; column < table.getColumnCount(); column++) {
			comp = table.getTableHeader().getDefaultRenderer().getTableCellRendererComponent(table,
					table.getColumnModel().getColumn(column).getHeaderValue(), false, false, 0, 0);
			width = comp.getPreferredSize().width;
			for (int row = 0; row < table.getRowCount(); row++) {
				TableCellRenderer renderer = table.getCellRenderer(row, column);
				comp = table.prepareRenderer(renderer, row, column);
				width = Math.max(comp.getPreferredSize().width, width);
			}
			// Add 12 for margin.
			width += 12;
			table.getColumnModel().getColumn(column).setPreferredWidth(width);
		}
	}

	/**
	 * Return the VOTableData associated with the table.
	 *
	 * @return the VOTableData associated with the table.
	 */
	public VOTableData getVOTableData() {
		return data;
	}

}
