// SecondaryDataTable.java
//
// Created: November 2005
//
// This file is a component of the BioMoby project.
// Copyright Martin Senger (martin.senger@gmail.com).
//

package org.biomoby.service.dashboard;

import org.biomoby.shared.MobySecondaryData;

import org.tulsoft.shared.StringUtils;

import javax.swing.JComboBox;
import javax.swing.DefaultCellEditor;
import javax.swing.table.TableColumn;
import javax.swing.table.DefaultTableCellRenderer;

import java.util.Vector;

import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableCellEditor;

import javax.swing.JTable;
import javax.swing.JComponent;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.AbstractCellEditor;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;



/**
 * A swing JTable that collects definitions of secondary data
 * (parameters) for a service. Used in the service registration. <p>
 *
 * @author <A HREF="mailto:martin.senger@gmail.com">Martin Senger</A>
 * @version $Id: SecondaryDataTable.java,v 1.6 2006/05/05 20:18:08 senger Exp $
 */

public class SecondaryDataTable
    extends CommonDataTable {

    // indeces of the used columns
    public final static int COL_ARTICLE  = SecondaryDataTableModel.COL_ARTICLE;
    public final static int COL_DATATYPE = SecondaryDataTableModel.COL_DATATYPE;
    public final static int COL_DEFAULT  = SecondaryDataTableModel.COL_DEFAULT;
    public final static int COL_MIN      = SecondaryDataTableModel.COL_MIN;
    public final static int COL_MAX      = SecondaryDataTableModel.COL_MAX;
    public final static int COL_ENUM     = SecondaryDataTableModel.COL_ENUM;
    public final static int COL_DESC     = SecondaryDataTableModel.COL_DESC;

    /*********************************************************************
     * Default constructor.
     ********************************************************************/
    public SecondaryDataTable() {
	super();
	tableModel = new SecondaryDataTableModel();
	setModel (tableModel);
	createItself();
    }

    /*********************************************************************
     *
     ********************************************************************/
    public MobySecondaryData[] getData() {
	Vector v = tableModel.getData();
	MobySecondaryData[] result = new MobySecondaryData [v.size()];
	v.copyInto (result);
	return result;
    }

    /*********************************************************************
     *
     ********************************************************************/
    protected void createItself() {
	super.createItself();

	// set renderer/editor for data type column
	TableColumn dtColumn =
	    getColumnModel().getColumn (COL_DATATYPE);
        JComboBox comboBox = new JComboBox();
        comboBox.addItem ("String");
        comboBox.addItem ("Integer");
        comboBox.addItem ("Float");
        comboBox.addItem ("Boolean");
        comboBox.addItem ("DateTime");
	dtColumn.setCellRenderer (new DefaultTableCellRenderer());
 	dtColumn.setCellEditor (new DefaultCellEditor(comboBox));

	// set renderer/editor for button-like description column
	TableColumn descColumn =
	    getColumnModel().getColumn (COL_DESC);
	if (tAreaIcon != null)
	    descColumn.setMaxWidth (tAreaIcon.getIconWidth() + 2);
	descColumn.setCellRenderer (new MoreLinesButtonRenderer());
	descColumn.setCellEditor (new MoreLinesButtonEditor());
 	descColumn.setHeaderRenderer (new TableCellRenderer() {
		public Component getTableCellRendererComponent
		(JTable table, Object value, boolean isSelected,
		 boolean hasFocus, int row, int column) {
		    return (JComponent)value;
		}
	    });
	descColumn.setHeaderValue (new JButton ("", tAreaIcon));

	// min-max value
// 	getColumnModel().getColumn (COL_MIN).setCellEditor (new MinMaxValueEditor());
// 	getColumnModel().getColumn (COL_MAX).setCellEditor (new MinMaxValueEditor());

    }

    /**************************************************************************
     *
     **************************************************************************/
    protected class SecondaryDataTableModel
	extends CommonDataTableModel {

	public final static int COL_ARTICLE  = 1;
	public final static int COL_DATATYPE = 2;
	public final static int COL_DEFAULT  = 3;
	public final static int COL_MIN      = 4;
	public final static int COL_MAX      = 5;
	public final static int COL_ENUM     = 6;
	public final static int COL_DESC     = 7;

	public SecondaryDataTableModel() {
	    columnToolTips = new String[] {
		"Click in this column to remove data from this service",
		"Click to edit article name",
		"Select a primitive data type that will represent this parameter",
		"Click to edit parameter's default value",
		"Click to edit parameter's minimum value",
		"Click to edit parameter's maximum value",
		"Click to edit parameter's allowed values - separate them by commas",
		"Click to edit description - multi-lines are possible",
	    };
	    columnNames = new String[] {
		"Remove",
		"Article name",
		"Data Type",
		"Default",
		"Min",
		"Max",
		"Allowed",
		"",
	    };
	    columnClasses = new Class[] {
		Integer.class,
		String.class,
		String.class,
		String.class,
		String.class,
		String.class,
		String.class,
		String.class,
	    };
	}

	public void addEmptyData() {
	    data.addElement (new MobySecondaryData (""));
	    int rowCount = getRowCount();
	    fireTableRowsInserted (rowCount-1, rowCount-1);
	}

	public Object getValueAt (int row, int col) {
	    try {
		MobySecondaryData d = (MobySecondaryData)data.elementAt (row);
		switch (col) {
		case COL_BUTTON:    return new Integer (row);
		case COL_ARTICLE:   return d.getName();
		case COL_DATATYPE:  return d.getDataType();
		case COL_DEFAULT:   return d.getDefaultValue();
		case COL_MIN:       return d.getMinValue();
// 		case COL_MIN:       return getValueOfProperType (d.getMinValue());
// 		    int minValue = d.getMinimumValue();
// 		    if (minValue == Integer.MIN_VALUE) return "";
// 		    else return new Integer (minValue);
		case COL_MAX:       return d.getMaxValue();
// 		    int maxValue = d.getMaximumValue();
// 		    if (maxValue == Integer.MAX_VALUE) return "";
// 		    else return new Integer (maxValue);
		case COL_ENUM:      return StringUtils.join (d.getAllowedValues(), ",");
		case COL_DESC:      return d.getDescription();
		}
	    } catch (Exception e) { }
	    return "";
	}

// 	protected Object getValueOfProperType (String value) {
// 	    String currType = (String)getValueAt (0, COL_DATATYPE);
// 	    if ("String".equals (currType)) return value;
// 	    if ("Integer".equals (currType)) return Integer.class;
// 	    if ("Float".equals (currType)) return Float.class;
// 	    if ("Boolean".equals (currType)) return Boolean.class;
// 	    if ("DateTime".equals (currType)) return String.class;
// 	    return Integer.class;

// 		    int minValue = d.getMinimumValue();
// 		    if (minValue == Integer.MIN_VALUE) return "";
// 		    else return new Integer (minValue);



	public void setValueAt (Object value, int row, int col) {
	    if (value == null) return;
	    try {
		MobySecondaryData d = (MobySecondaryData)data.elementAt (row);
		switch (col) {
		case COL_ARTICLE:
		    d.setName (value.toString());
		    break;
		case COL_DATATYPE:
		    d.setDataType (value.toString());
		    break;
		case COL_DEFAULT:
		    d.setDefaultValue (value.toString());
		    break;
		case COL_MIN:
// 		    d.setMinimumValue ( ((Integer)value).intValue() );
		    d.setMinValue (value.toString());
		    break;
		case COL_MAX:
// 		    d.setMaximumValue ( ((Integer)value).intValue() );
		    d.setMaxValue (value.toString());
		    break;
		case COL_ENUM:
		    d.setAllowedValues (value.toString().split ("\\s*,\\s*"));
		    break;
		case COL_DESC:
		    d.setDescription (value.toString());
		    break;
		}
		fireTableCellUpdated (row, col);
	    } catch (Exception e) { }
	}

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

    /**************************************************************************
     *
     **************************************************************************/
    class MoreLinesButton extends JButton {
	JTextArea editArea;
	JPanel editPanel;
	String[] buttons;

	// which row we are now editing
	int currentRow = -1;

	// if this button is used as a cell editor it need access to
	// this editor in order to call stopCellEditing() in its
	// actionePerformed
	protected AbstractCellEditor editor;

	public MoreLinesButton() {
	    this (null);
	}

	// 'editor' can be null if the button does not represent a
	// cell editor (but only a cell renderer)
	public MoreLinesButton (AbstractCellEditor editor) {
	    super();
	    setText ("");
 	    setFocusPainted (false);
	    this.editor = editor;
	    setToolTipText ("Click here to enter a value - possibly containing more lines");
	    setIcon (tAreaIcon);
	    if (editor != null) {

		editArea = new JTextArea (10, 40);
		editArea.setCaretPosition (0);
		editArea.setLineWrap (true);
		editArea.setWrapStyleWord (true);

		editPanel = new JPanel();
		editPanel.add (new JScrollPane (editArea));	

		buttons = new String[] { "Done", "Clear", "Cancel"};

		addActionListener (new ActionListener() {
			public void actionPerformed (ActionEvent e) {
			    Object currentValue =
				(currentRow >= 0 ? getValueAt (currentRow, COL_DESC) : "");
			    if (currentValue != null)
				editArea.setText (currentValue.toString());

			    int selected =
				JOptionPane.showOptionDialog (MoreLinesButton.this,
							      editPanel,
							      "Enter/Edit multi-line data...",
							      JOptionPane.YES_NO_OPTION,
							      JOptionPane.PLAIN_MESSAGE,
							      null,
							      buttons,
							      null);
			    String newValue = null;
			    if (selected == 0)
				newValue = editArea.getText();   // 'done' selected
			    else if (selected == 1)
				newValue = "";                   // 'clear' selected
			    if (newValue != null && currentRow >= 0)
				setValueAt (newValue, currentRow, COL_DESC);
			    MoreLinesButton.this.editor.stopCellEditing();
			}
		    });

	    }
	}

	public void rememberRow (int row) {
	    currentRow = row;
	}

    }

    /**************************************************************************
     *
     **************************************************************************/
    class MoreLinesButtonRenderer extends MoreLinesButton
	implements TableCellRenderer {

	public MoreLinesButtonRenderer() {
	    setOpaque (true);
	}	

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

    /**************************************************************************
     *
     **************************************************************************/
    class MoreLinesButtonEditor
	extends AbstractCellEditor
	implements TableCellEditor {

	public Object getCellEditorValue() {
	    return null;
	}

	public Component getTableCellEditorComponent (JTable table,
						      Object value,
						      boolean isSelected,
						      int row, int column) {
	    MoreLinesButton button = new MoreLinesButton (this);
	    button.rememberRow (row);
	    return button;
	}
    }

//     /**************************************************************************
//      *
//      **************************************************************************/
//     class MinMaxValueEditor
// 	extends AbstractCellEditor
// 	implements TableCellEditor {

// 	protected TableCellEditor editor;

// 	public Object getCellEditorValue() {
// 	    return editor.getCellEditorValue();
// 	}

// 	public Component getTableCellEditorComponent (JTable table,
// 						      Object value,
// 						      boolean isSelected,
// 						      int row, int column) {

// 	    editor = table.getDefaultEditor (currentClass());
// 	    return editor.getTableCellEditorComponent (table, value, isSelected,
// 						       row, column);
// 	}

// 	public boolean isCellEditable (java.util.EventObject anEvent) {
// 	    return ! "Boolean".equals ((String)getValueAt (0, COL_DATATYPE));
// 	}

// 	protected Class currentClass() {
// 	    String currType = (String)getValueAt (0, COL_DATATYPE);
// 	    if ("String".equals (currType)) return String.class;
// 	    if ("Integer".equals (currType)) return Integer.class;
// 	    if ("Float".equals (currType)) return Float.class;
// 	    if ("Boolean".equals (currType)) return Boolean.class;
// 	    if ("DateTime".equals (currType)) return String.class;
// 	    return Integer.class;
// 	}


//     }

}
