/*
 * TaxonSetsPanel.java
 *
 * Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST 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
 * of the License, or (at your option) any later version.
 *
 *  BEAST 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 BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.app.treestat;

import jam.framework.Exportable;
import jam.table.TableRenderer;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;


public class TaxonSetsPanel extends JPanel implements Exportable {


	/**
	 *
	 */
	private static final long serialVersionUID = -9013475414423166476L;
	TreeStatFrame frame = null;
	TreeStatData treeStatData = null;
	TreeStatData.TaxonSet selectedTaxonSet = null;

	JScrollPane scrollPane1 = null;
	JTable taxonSetsTable = null;
	TaxonSetsTableModel taxonSetsTableModel = null;

	JScrollPane scrollPane2 = null;
	JTable excludedTaxaTable = null;
	TaxaTableModel excludedTaxaTableModel = null;

	JScrollPane scrollPane3 = null;
	JTable includedTaxaTable = null;
	TaxaTableModel includedTaxaTableModel = null;

	public TaxonSetsPanel(TreeStatFrame frame, TreeStatData treeStatData) {

		this.frame = frame;
		setOpaque(false);

		this.treeStatData = treeStatData;

		Icon addIcon = null, removeIcon = null, includeIcon = null, excludeIcon = null;
 		try {
			addIcon = new ImageIcon(dr.app.util.Utils.getImage(this, "images/add.png"));
			removeIcon = new ImageIcon(dr.app.util.Utils.getImage(this, "images/minus.png"));
			includeIcon = new ImageIcon(dr.app.util.Utils.getImage(this, "images/include.png"));
			excludeIcon = new ImageIcon(dr.app.util.Utils.getImage(this, "images/exclude.png"));
		} catch (Exception e) {
			// do nothing
		}

        JPanel buttonPanel = new JPanel(new BorderLayout());
        buttonPanel.setOpaque(false);
        JButton importButton = new JButton(frame.getImportAction());
        importButton.setFocusable(false);
        importButton.putClientProperty("JButton.buttonType", "textured");
        importButton.setMargin(new Insets(4,4,4,4));
        buttonPanel.add(importButton, BorderLayout.WEST);
        buttonPanel.add(new JLabel("    To define taxon sets, first import a list of taxa (i.e., from the trees to be analysed)"), BorderLayout.SOUTH);

        // Taxon Sets
		taxonSetsTableModel = new TaxonSetsTableModel();
        TableSorter sorter = new TableSorter(taxonSetsTableModel);
		taxonSetsTable = new JTable(sorter);
        sorter.addTableModelListener(taxonSetsTable);

		taxonSetsTable.getColumnModel().getColumn(0).setCellRenderer(
			new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));

		taxonSetsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent evt) { taxonSetsTableSelectionChanged(); }
		});

   		scrollPane1 = new JScrollPane(taxonSetsTable,
										JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
										JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

 		JPanel buttonPanel1 = createAddRemoveButtonPanel(addTaxonSetAction, addIcon, "Create a new taxon set",
 															removeTaxonSetAction, removeIcon, "Remove a taxon set",
 															javax.swing.BoxLayout.X_AXIS);

		// Excluded Taxon List
		excludedTaxaTableModel = new TaxaTableModel(false);
        sorter = new TableSorter(excludedTaxaTableModel);
		excludedTaxaTable = new JTable(sorter);
        sorter.addTableModelListener(excludedTaxaTable);

		excludedTaxaTable.getColumnModel().getColumn(0).setCellRenderer(
			new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));

		excludedTaxaTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent evt) { excludedTaxaTableSelectionChanged(); }
		});

   		scrollPane2 = new JScrollPane(excludedTaxaTable,
										JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
										JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

 		JPanel buttonPanel2 = createAddRemoveButtonPanel(includeTaxonAction, includeIcon, "Include selected taxa in the taxon set",
 															excludeTaxonAction, excludeIcon, "Exclude selected taxa from the taxon set",
 															javax.swing.BoxLayout.Y_AXIS);

 		// Included Taxon List
		includedTaxaTableModel = new TaxaTableModel(true);
        sorter = new TableSorter(includedTaxaTableModel);
		includedTaxaTable = new JTable(sorter);
        sorter.addTableModelListener(includedTaxaTable);

		includedTaxaTable.getColumnModel().getColumn(0).setCellRenderer(
			new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));

		includedTaxaTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent evt) { includedTaxaTableSelectionChanged(); }
		});

   		scrollPane3 = new JScrollPane(includedTaxaTable,
										JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
										JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

        JPanel panel = new JPanel();
        panel.setOpaque(false);
        panel.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();

        c.weightx = 0.333333;
		c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(6,6,6,6);
		c.gridx = 0;
		c.gridy = 0;
		panel.add(scrollPane1, c);

		c.weightx = 0.333333;
		c.weighty = 0;
		c.fill = GridBagConstraints.NONE;
		c.anchor = GridBagConstraints.LINE_START;
		c.insets = new Insets(0,6,6,6);
		c.gridx = 0;
		c.gridy = 1;
		panel.add(buttonPanel1, c);

		c.weightx = 0.333333;
		c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(6,6,6,6);
		c.gridx = 1;
		c.gridy = 0;
		panel.add(scrollPane2, c);

		c.weightx = 0.0;
		c.fill = GridBagConstraints.NONE;
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(0,0,0,0);
		c.gridx = 2;
		c.gridy = 0;
		panel.add(buttonPanel2, c);

		c.weightx = 0.333333;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(6,6,6,6);
		c.gridx = 3;
		c.gridy = 0;
		panel.add(scrollPane3, c);

        setLayout(new BorderLayout());
        setBorder(BorderFactory.createEmptyBorder(6,6,6,6));
        add(buttonPanel, BorderLayout.NORTH);
        add(panel, BorderLayout.CENTER);

	}

	JPanel createAddRemoveButtonPanel(Action addAction, Icon addIcon, String addToolTip,
										Action removeAction, Icon removeIcon, String removeToolTip, int axis) {

 		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel, axis));
		buttonPanel.setOpaque(false);
		JButton addButton = new JButton(addAction);
		if (addIcon != null) {
			addButton.setIcon(addIcon);
			addButton.setText(null);
		}
   	  	addButton.setToolTipText(addToolTip);
		addButton.putClientProperty("JButton.buttonType", "textured");
		addButton.setOpaque(false);
		addAction.setEnabled(false);

		JButton removeButton = new JButton(removeAction);
		if (removeIcon != null) {
			removeButton.setIcon(removeIcon);
			removeButton.setText(null);
		}
   	  	removeButton.setToolTipText(removeToolTip);
		removeButton.putClientProperty("JButton.buttonType", "textured");
		removeButton.setOpaque(false);
		removeAction.setEnabled(false);

		buttonPanel.add(addButton);
		buttonPanel.add(new JToolBar.Separator(new Dimension(6,6)));
		buttonPanel.add(removeButton);

		return buttonPanel;
	}

	public void dataChanged() {

		addTaxonSetAction.setEnabled(treeStatData.allTaxa.size() > 0);

		taxonSetsTableModel.fireTableDataChanged();
		includedTaxaTableModel.fireTableDataChanged();
		excludedTaxaTableModel.fireTableDataChanged();
	}

	private void taxonSetsTableSelectionChanged() {
		if (taxonSetsTable.getSelectedRowCount() == 0) {
			selectedTaxonSet = null;
			removeTaxonSetAction.setEnabled(false);
		} else {
			selectedTaxonSet = treeStatData.taxonSets.get(taxonSetsTable.getSelectedRow());
			removeTaxonSetAction.setEnabled(true);
		}
		includedTaxaTableModel.fireTableDataChanged();
		excludedTaxaTableModel.fireTableDataChanged();
	}

	private void excludedTaxaTableSelectionChanged() {
		if (excludedTaxaTable.getSelectedRowCount() == 0) {
			includeTaxonAction.setEnabled(false);
		} else {
			includeTaxonAction.setEnabled(true);
		}
	}

	private void includedTaxaTableSelectionChanged() {
		if (includedTaxaTable.getSelectedRowCount() == 0) {
			excludeTaxonAction.setEnabled(false);
		} else {
			excludeTaxonAction.setEnabled(true);
		}
	}

    public JComponent getExportableComponent() {
		return this;
	}

  	Action addTaxonSetAction = new AbstractAction("+") {

  		/**
		 *
		 */
		private static final long serialVersionUID = 1831933175582860833L;

		public void actionPerformed(ActionEvent ae) {
			TreeStatData.TaxonSet taxonSet = new TreeStatData.TaxonSet();
			taxonSet.name = "untitled";
			taxonSet.taxa = new ArrayList();
			treeStatData.taxonSets.add(taxonSet);
			dataChanged();

			int sel = treeStatData.taxonSets.size() - 1;
			taxonSetsTable.setRowSelectionInterval(sel, sel);
  		}
  	};

  	Action removeTaxonSetAction = new AbstractAction("-") {

  		/**
		 *
		 */
		private static final long serialVersionUID = -8662527333546044639L;

		public void actionPerformed(ActionEvent ae) {
			int saved = taxonSetsTable.getSelectedRow();
			int row = taxonSetsTable.getSelectedRow();
			if (row != -1) {
				treeStatData.taxonSets.remove(row);
			}
			dataChanged();
			if (saved >= treeStatData.taxonSets.size()) saved = treeStatData.taxonSets.size() - 1;
			taxonSetsTable.setRowSelectionInterval(saved, saved);
  		}
  	};

  	Action includeTaxonAction = new AbstractAction("->") {
  		/**
		 *
		 */
		private static final long serialVersionUID = -1875904513948242608L;

		public void actionPerformed(ActionEvent ae) {
			int saved = taxonSetsTable.getSelectedRow();
			int[] rows = excludedTaxaTable.getSelectedRows();
			ArrayList<String> exclList = new ArrayList<String>(treeStatData.allTaxa);
			exclList.removeAll(selectedTaxonSet.taxa);
            for (int row : rows) {
                selectedTaxonSet.taxa.add(exclList.get(row));
            }
			dataChanged();
			taxonSetsTable.setRowSelectionInterval(saved, saved);
  		}
  	};

  	Action excludeTaxonAction = new AbstractAction("<-") {

  		/**
		 *
		 */
		private static final long serialVersionUID = 4523480086490780822L;

		public void actionPerformed(ActionEvent ae) {
			int saved = taxonSetsTable.getSelectedRow();
			int[] rows = includedTaxaTable.getSelectedRows();
			for (int i = rows.length - 1; i >= 0 ; i--) {
				selectedTaxonSet.taxa.remove(rows[i]);
			}
			dataChanged();
			taxonSetsTable.setRowSelectionInterval(saved, saved);
  		}
  	};

	class TaxonSetsTableModel extends AbstractTableModel {

		/**
		 *
		 */
		private static final long serialVersionUID = 219223813257870207L;

		public TaxonSetsTableModel() {
		}

		public int getColumnCount() {
			return 1;
		}

		public int getRowCount() {
			return treeStatData.taxonSets.size();
		}

		public Object getValueAt(int row, int col) {
			return (treeStatData.taxonSets.get(row)).name;
		}

		public void setValueAt(Object value, int row, int col) {
			(treeStatData.taxonSets.get(row)).name = (String)value;
		}

        public boolean isCellEditable(int row, int col) {
 			return true;
        }

		public String getColumnName(int column) {
			return "Taxon Sets";
		}

		public Class getColumnClass(int c) {return getValueAt(0, c).getClass();}
	}

    class TaxaTableModel extends AbstractTableModel {

		/**
		 *
		 */
		private static final long serialVersionUID = 1559408662356843275L;
		boolean included;

		public TaxaTableModel(boolean included) {
			this.included = included;
		}

		public int getColumnCount() {
			return 1;
		}

		public int getRowCount() {
			if (selectedTaxonSet == null) return 0;

			if (included) {
				return selectedTaxonSet.taxa.size();
			} else {
				return treeStatData.allTaxa.size() - selectedTaxonSet.taxa.size();
			}
		}

		public Object getValueAt(int row, int col) {

			if (included) {
				return selectedTaxonSet.taxa.get(row);
			} else {
				ArrayList<String> exclList = new ArrayList<String>(treeStatData.allTaxa);
				exclList.removeAll(selectedTaxonSet.taxa);
				return exclList.get(row);
			}
		}

        public boolean isCellEditable(int row, int col) {
 			return false;
        }

		public String getColumnName(int column) {
			if (included) return "Included Taxa";
			else return "Excluded Taxa";
		}

		public Class getColumnClass(int c) {return getValueAt(0, c).getClass();}
	}
}
