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

package org.biomoby.service.dashboard.data;

import org.biomoby.shared.MobyDataType;
import org.biomoby.shared.MobyException;
import org.biomoby.shared.MobyService;
import org.biomoby.shared.MobyData;
import org.biomoby.shared.MobyPrimaryData;
import org.biomoby.shared.MobyPrimaryDataSimple;
import org.biomoby.shared.MobyPrimaryDataSet;
import org.biomoby.shared.MobySecondaryData;
import org.biomoby.shared.datatypes.MobyObject;
import org.biomoby.shared.parser.MobyParameter;
import org.biomoby.shared.parser.MobySimple;
import org.biomoby.shared.parser.MobyCollection;
import org.biomoby.shared.parser.MobyJob;
import org.biomoby.shared.parser.MobyPackage;
import org.biomoby.service.dashboard.data.DataTypeTreeTable;
import org.biomoby.service.dashboard.data.ParametersTable;
import org.biomoby.service.dashboard.Dashboard;

import org.tulsoft.shared.UUtils;
import org.tulsoft.tools.gui.SwingUtils;

import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.Icon;

import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;

import org.jdom.Document;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;

import org.jdom.Element;

import java.util.Vector;


/**
 * A swing panel that represents a user interface for entering input
 * data for a Biomoby service. It creates a tree-table for primary
 * inputs and another table for the secondary inputs. It also deals
 * with the collections. When data are entered it can be asked for
 * converting them into Biomoby compiant XML input message (that can
 * be sent to a service). <p>
 *
 * The panel needs for its work all definitions of Biomoby data types
 * (and a definition of a service whose data are being created) - but
 * it does not need Java classes for each individual Biomoby data type
 * (such classes are generated by the MOses project). That's why it is
 * suitable for independent Biomoby client software (I presume that
 * Taverna can use it). <p>
 *
 * @author <A HREF="mailto:martin.senger@gmail.com">Martin Senger</A>
 * @version $Id: ServiceInputPanel.java,v 1.4 2009/01/30 14:44:18 kawas Exp $
 */
public class ServiceInputPanel
    extends JPanel {

    protected static Icon addDataIcon, addParamIcon, addCollIcon;

    // a vector storing input data: for each input data there are two
    // elements
    //   MobyData | DataTypeTreeTable
    Vector inputTables;

    // table for secondary parameters
    ParametersTable paramsTable;

    /*************************************************************************
     * Create a GUI for all inputs for the given service. <p>
     *
     * @param service whose input data should be created
     * @param dataTypes all definitions of the Biomoby data types
     * (well, not all are needed, just those who are used by the
     * 'service')
     *************************************************************************/
    public ServiceInputPanel (MobyService service,
			      MobyDataType[] dataTypes) {
	super (new GridBagLayout());
	loadIcons();

	MobyData[] primInputs = service.getPrimaryInputs();
	MobyData[] sis = service.getSecondaryInputs();
	MobySecondaryData[] secInputs = new MobySecondaryData [sis.length];
	for (int i = 0; i < sis.length; i++)
	    secInputs[i] = (MobySecondaryData)sis[i];

	inputTables = new Vector();
	paramsTable =
	    ( (secInputs == null || secInputs.length == 0) ?
	      null :
	      new ParametersTable (secInputs)
	      );

	// create GUI
	JTabbedPane tabbedPane = new JTabbedPane();
	SwingUtils.addComponent
	    (this, tabbedPane,
	     0, 0, 1, 1, GridBagConstraints.BOTH, GridBagConstraints.NORTHWEST, 1.0, 1.0);

	// add primary inputs to the tabbed pane
	for (int i = 0; i < primInputs.length; i++) {

	    MobyPrimaryData pData = (MobyPrimaryData)primInputs[i];
	    MobyDataType rootType = findDataType (pData.getDataType().getName(),
						  dataTypes);
	    if (rootType == null)
		continue;
	    DataTypeTreeTable treeTable =
		new DataTypeTreeTable (rootType, dataTypes);
	    JPanel p = new JPanel (new GridBagLayout());
	    SwingUtils.addComponent
		(p, treeTable.scrollable(),
		 0, 0, 1, 1, GridBagConstraints.BOTH, GridBagConstraints.NORTHWEST, 1.0, 1.0);
	    inputTables.addElement (pData);
	    inputTables.addElement (treeTable);

	    String toolTip = rootType.getDescription();
	    tabbedPane.addTab (pData.getName(), addDataIcon, p,
			       "<html>" + toolTip.replaceAll ("\n", "<br>\n"));
	}

	// add secondary inputs to the tabbed pane (all as one tab)
	if (secInputs != null && secInputs.length > 0) {
	    JPanel p = new JPanel (new GridBagLayout());
	    SwingUtils.addComponent
		(p, paramsTable.scrollable(),
		 0, 0, 1, 1, GridBagConstraints.BOTH, GridBagConstraints.NORTHWEST, 1.0, 1.0);
	    tabbedPane.addTab ("Parameters", addDataIcon, p,
			       "Enter secondary inputs/parameters");
	}
    }

    /**************************************************************************
     * Find 'dataTypeToBeFound in 'dataTypes' and return it.
     **************************************************************************/
    protected MobyDataType findDataType (String dataTypeToBeFound,
					 MobyDataType[] dataTypes) {
	for (int i = 0; i < dataTypes.length; i++) {
	    if (dataTypeToBeFound.equals (dataTypes[i].getName()))
		return dataTypes[i];
	}
	System.err.println ("Strange, data type '" + dataTypeToBeFound +
			    "' was not found in " + dataTypes.length +
			    " data types.");
	return null;
    }

    /*********************************************************************
     * Load shared icon.
     ********************************************************************/
    protected static void loadIcons() {
	if (addDataIcon == null)
	    addDataIcon = SwingUtils.createIcon ("images/smallAnnotate.gif",
						 Dashboard.class);
    }

    /*************************************************************************
     * Return an XML document representing complete input for a
     * service given in the constructor of this class.
     *************************************************************************/
    public Document getXMLDocument() {
	Vector v = new Vector();
	for (int i = 0; i < inputTables.size(); i = i+2) {
	    MobyData data = (MobyData)inputTables.elementAt (i);
	    DataTypeTreeTable table = (DataTypeTreeTable)inputTables.elementAt (i+1);
	    String articleName = data.getName();
	    Element dataElem = table.toXML();
	    Element wrapperElem = null;
	    if (data instanceof MobyPrimaryDataSimple)
		wrapperElem = MobySimple.toXML (articleName, dataElem);
	    else if (data instanceof MobyPrimaryDataSet)
		wrapperElem = MobyCollection.toXML (articleName,
						    new Element[] {
							MobySimple.toXML ("",
									  dataElem) });
	    if (wrapperElem != null)
		v.addElement (wrapperElem);
	}

	if (paramsTable != null) {
	    Element[] params = paramsTable.toXML();
	    for (int i = 0; i < params.length; i++) {
		v.addElement (params[i]);
	    }
	}

	Element[] allData = new Element [v.size()];
	v.copyInto (allData);
 	Document doc = MobyPackage.toXMLDocument
	    (new Element[] { MobyJob.toXML ("sip_1_", allData) });
	return doc;
    }
    
    /**
     * 
     * @param xml
     *                an XML file containing a full biomoby message that we
     *                would like to parse and display as input to our service
     * @throws MobyException if there is a problem parsing the XML or populating our table
     */
    public void setValues(String xml) throws MobyException {
	// create the MobyPackage
	MobyPackage pack = MobyPackage.createFromXML(xml);
	// get our job
	MobyJob job = null;
	if (pack != null && pack.getJobs().length > 0)
	    job = pack.getJob(0);
	if (job == null)
	    return;
	for (int i = 0; i < inputTables.size(); i = i+2) {
	    MobyData data = (MobyData)inputTables.elementAt (i);
	    DataTypeTreeTable table = (DataTypeTreeTable)inputTables.elementAt (i+1);
	    String articleName = data.getName();
	    Object object = job.getData(articleName);
	    // datatype not existent in this message
	    if (object == null)
		continue;
	    table.populateData((MobyObject)object);
	}
	
	// update secondary params
	if (paramsTable != null) {
	    Object[] values = paramsTable.tableModel.getData();
	    for (int i = 0; i < values.length; i++) {
		if (!"".equals(values[i])) {
		    String name = paramsTable.paramDefs[i].getName();
		    String secondary = job.getParameter(name);
		    // set the secondary param
		    if (secondary != null)
			values[i] = (UUtils.isEmpty (secondary) ? "" : secondary);
		}
	    }
	}
	// update graphics
	updateUI();
    }

    /*************************************************************************
     * Return an XML string representing complete input for a service
     * given in the constructor of this class.
     *************************************************************************/
    public String toXML() {
	XMLOutputter xo = new XMLOutputter();
	xo.setFormat (Format.getPrettyFormat());
	return xo.outputString (getXMLDocument());
    }

}
