// RDF.java
//
//    senger@ebi.ac.uk
//    October 2003
//

package org.biomoby.client;

import java.util.Properties;

import org.biomoby.shared.MobyService;

import edu.mit.lcs.haystack.rdf.Literal;
import edu.mit.lcs.haystack.rdf.LocalRDFContainer;
import edu.mit.lcs.haystack.rdf.RDFException;
import edu.mit.lcs.haystack.rdf.Resource;
import edu.mit.lcs.haystack.rdf.Statement;
import edu.mit.lcs.haystack.rdf.Utilities;

/**
 * A utility class that understands how to create
 * <a href="http://www.research.att.com/sw/tools/graphviz/">graphviz</a> graphs from a set of
 * {@link org.biomoby.client.ServicesEdge ServiceEdges}, or from other
 * data structures.
 *
 * @author <A HREF="mailto:senger@ebi.ac.uk">Martin Senger</A>
 * @version $Id: RDF.java,v 1.4 2005/07/19 12:39:59 senger Exp $
 */

public abstract class RDF {

    public static final String PREDICATE_HAS_OUTPUT = "hasOutput";
    public static final String PREDICATE_HAS_INPUT = "hasInput";

    /*************************************************************************
     * Creates a graph connecting Moby services as defined in a set of
     * the graph 'edges.  <p>
     *
     * @param edges represent services and their connectors in the
     * created graph
     * @param props are some properties that can influence how the
     * graph will look like; see the property names elswhere in this
     * API what properties are understood
     *
     * @return a string with all definitions as understood by 'dot'
     * program (from the graphviz package); this string can be saved
     * in a '.dot' file that can be passed to a dot program to produce
     * graphs in many available formats
     *
     *************************************************************************/
    static public String createServicesGraph (ServicesEdge[] edges,
					      Properties props) {

	try {
	    LocalRDFContainer rdfc = new LocalRDFContainer();
	    for (int i = 0; i < edges.length; i++) {
		ServicesEdge edge = edges[i];
		if (edge.targetService == null) {
		    rdfc.add (new Statement ( new Resource (lsidizeService (edge.sourceService)),
					      new Resource ("urn:lsid:biomoby.org:connect:" + PREDICATE_HAS_OUTPUT),
					      new Resource (lsidizeDataType (edge.getConnector())) ));
		} else {
// 		StringBuffer edgeAttrs = new StringBuffer();
// 		if (edge.isWeakConnection()) {
// 		    appendAfterComma (edgeAttrs, "style=dotted");
// 		}
// 		switch (edge.getConnectionType()) {
// 		case ServicesEdge.SIMPLE_CONNECTION:
// 		    appendAfterComma (edgeAttrs, "arrowtail=none");
// 		    appendAfterComma (edgeAttrs, "arrowhead=open");
// 		    break;
// 		case ServicesEdge.HEAD_COLLECTION_CONNECTION:
// 		    appendAfterComma (edgeAttrs, "arrowtail=inv");
// 		    appendAfterComma (edgeAttrs, "arrowhead=open");
// 		    break;
// 		case ServicesEdge.TAIL_COLLECTION_CONNECTION:
// 		    appendAfterComma (edgeAttrs, "arrowtail=none");
// 		    appendAfterComma (edgeAttrs, "arrowhead=normal");
// 		    break;
// 		case ServicesEdge.BOTH_COLLECTIONS_CONNECTION:
// 		    appendAfterComma (edgeAttrs, "arrowtail=inv");
// 		    appendAfterComma (edgeAttrs, "arrowhead=normal");
// 		    break;
// 		}
		    String connector = edge.getConnector();
// 		if (! connector.equals (""))
// 		    appendAfterComma (edgeAttrs, "label=\"" + connector + "\"");
// 		if (edgeAttrs.length() > 0) {
// 		    edgeAttrs.insert (0, " [");
// 		    edgeAttrs.append ("]");
// 		}
		    String lsidSourceService = lsidizeService (edge.sourceService);
		    String lsidTargetService = lsidizeService (edge.targetService);
		    rdfc.add (new Statement ( new Resource (lsidSourceService),
					      new Resource ("urn:lsid:biomoby.org:connect:" + PREDICATE_HAS_OUTPUT),
					      new Resource (lsidizeDataType (connector)) ));
		    rdfc.add (new Statement ( new Resource (lsidSourceService),
					      new Resource ("http://purl.org/dc/elements/1.1/title"),
					      new Literal (edge.sourceService.getName()) ));
		    rdfc.add (new Statement ( new Resource (lsidSourceService),
					      new Resource ("http://purl.org/dc/elements/1.1/description"),
					      new Literal (edge.sourceService.getDescription().trim()) ));
		    rdfc.add (new Statement ( new Resource (lsidTargetService),
					      new Resource ("urn:lsid:biomoby.org:connect:" + PREDICATE_HAS_INPUT),
					      new Resource (lsidizeDataType (connector)) ));
		    rdfc.add (new Statement ( new Resource (lsidTargetService),
					      new Resource ("http://purl.org/dc/elements/1.1/title"),
					      new Literal (edge.targetService.getName()) ));
		    rdfc.add (new Statement ( new Resource (lsidTargetService),
					      new Resource ("http://purl.org/dc/elements/1.1/description"),
					      new Literal (edge.targetService.getDescription().trim()) ));
		}
	    }

	    return Utilities.generateRDF (rdfc);
	} catch (RDFException e) {
	    System.err.println ("RDF Error: " + e.toString());
	    return "";
	}
    }

    /*************************************************************************
     * Creates a graph connecting 'dataTypes' using their ISA
     * relationship and showing also their HASA children.
     * <p>
     *
     * @param dataTypes represent nodes in the created graph
     * @param props are some properties that can influence how the
     * graph will look like; see the property names elswhere in this
     * API what properties are understood
     *
     * @return a string with all definitions as understood by 'dot'
     * program (from the graphviz package); this string can be saved
     * in a '.dot' file that can be passed to a dot program to produce
     * graphs in many available formats
     *
     *************************************************************************/
//     static public String createDataTypesGraph (MobyDataType[] dataTypes,
// 					       Properties props) {

// 	StringBuffer buf = new StringBuffer();
// 	buf.append ("digraph MobyDataTypes {\n");
// 	buf.append ("\trankdir=" + props.getProperty (PROP_RANKDIR, "LR") + ";\n");
// 	buf.append ("\tedge [dir=back,arrowtail=empty];\n");
// 	for (int d = 0; d < dataTypes.length; d++) {
// 	    MobyDataType type = dataTypes[d];
// 	    String name = Utils.pureName (type.getName());
// 	    String[] parents = type.getParentNames();
// 	    for (int i = 0; i < parents.length; i++) {
// 		buf.append ("\t");
// 		buf.append (trName (Utils.pureName (parents[i])));
// 		buf.append (" -> ");
// 		buf.append (trName (name));
// 		buf.append (";\n");
// 	    }
// 	    Hashtable children = type.getChildren();
// 	    if (children.size() > 0) {
// 		String dummyName = trName (name) + "_HASA";
// 		buf.append ("\t" + trName (name) + " -> " + dummyName);
// 		buf.append (" [style=dotted,dir=forward,arrowhead=none,arrowtail=ediamond];\n");
// 		StringBuffer hasaBuf = new StringBuffer();
// 		for (Enumeration en = children.keys(); en.hasMoreElements(); ) {
// 		    String childName = (String)en.nextElement();
// 		    String childType = Utils.pureName ((String)children.get (childName));
// 		    if (childType == null)
// 			childType = "n/a";  // should not happen I guess
// 		    hasaBuf.append (" | {" + childType + "|" + childName + "}");
// 		}
// 		buf.append ("\t" + dummyName + " [shape=record, label=\"HAS[A]");
// 		buf.append (hasaBuf);
// 		buf.append ("\"];\n");
// 	    }
// 	}
// 	buf.append ("}\n");
// 	return new String (buf);
//     }

    static String lsidizeService (MobyService service) {
	StringBuffer buf = new StringBuffer();
	buf.append ("urn:lsid:biomoby.org:serviceinstance:");
	buf.append (service.getAuthority());
	buf.append (",");
	buf.append (service.getName());
	return new String (buf);
    }

    static String lsidizeDataType (String connector) {
	connector.replace ('/', ',');
	StringBuffer buf = new StringBuffer();
	buf.append ("urn:lsid:biomoby.org:data:");
	buf.append (connector);
	return new String (buf);
    }

//     /*************************************************************************
//      * Append 'value' to 'buf'. If it is not the first value there, it
//      * prefix it by a comma.
//      *************************************************************************/
//     static void appendAfterComma (StringBuffer buf, String value) {
// 	if (buf.length() > 0)
// 	    buf.append (",");
// 	buf.append (value);
//     }

//     /*************************************************************************
//      * Replaces dashes by underscores in 'name'. Because I found that
//      * sometimes the dashes in the node names in Graphviz caused me
//      * some problems (or was I mistaken?).
//      *
//      * @param name to be changed
//      * @return changed 'name'
//      *************************************************************************/
//     public static String trName (String name) {
// 	return name.replace ('-', '_');
//     }

}
