package org.biomoby.client.rdf.builder;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.biomoby.client.CentralImpl;
import org.biomoby.client.rdf.vocabulary.DC_PROTEGE;
import org.biomoby.client.rdf.vocabulary.FetaVocabulary;
import org.biomoby.registry.meta.Registry;
import org.biomoby.shared.Central;
import org.biomoby.shared.MobyData;
import org.biomoby.shared.MobyException;
import org.biomoby.shared.MobyNamespace;
import org.biomoby.shared.MobyPrimaryDataSet;
import org.biomoby.shared.MobyPrimaryDataSimple;
import org.biomoby.shared.MobyResourceRef;
import org.biomoby.shared.MobySecondaryData;
import org.biomoby.shared.MobyService;
import org.biomoby.shared.MobyUnitTest;
import org.biomoby.shared.data.MobyDataSecondaryInstance;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.RDFWriter;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.vocabulary.RDF;
import com.ibm.lsid.LSID;
import com.ibm.lsid.MalformedLSIDException;

/*
 * Created on Jan 12, 2005 <p>
 * By Eddie <p>
 */

/**
 * @author Eddie Kawas
 *         <p>
 *         This class was created to present a way to retrieve a service
 *         instances' RDF using just a service name and authority.
 *         <p>
 *         For questions, comments, or bugs
 *         <p>
 *         email me at edward.kawas@gmail.com
 */
@SuppressWarnings("unchecked")
public class ServiceInstanceRDF {

    // some URIs
    private static final String empty = "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
	    + System.getProperty("line.separator")
	    + "  xmlns:dc=\"http://purl.org/dc/elements/1.1/\">"
	    + System.getProperty("line.separator")
	    + "  <rdf:Description rdf:about=\"http://www.w3.org/\">"
	    + System.getProperty("line.separator")
	    + "    <dc:title>Service Instance Not Found</dc:title>"
	    + System.getProperty("line.separator")
	    + "  </rdf:Description>"
	    + System.getProperty("line.separator") + "</rdf:RDF>";

    private static Log log = LogFactory.getLog(ServiceInstanceRDF.class);

    private CentralImpl central = null;

    // character is a has
    private String[] trailing_characters = new String[]{"/","#"};
    
    private static String PREFERED_TRAILING_CHARACTER = null;
    
    private String OBJ;

    private String SRV;

    private String NS;

    private String SI;

    public final String getLatestLSID(String uri, String name, String lsid) {
	Central central = null;
	MobyService service = null;
	MobyService[] services = null;
	// initialize central
	central = getCentralImpl();
	// set up the query service
	service = new MobyService(name);
	service.setCategory("");
	service.setAuthority(uri);
	// query for the service
	try {
	    services = central.findService(service);
	} catch (MobyException e1) {
	    return "";
	}
	if (services.length == 1) {
	    service = services[0];
	    return (service.getLSID());
	}
	return "";
    }

    public static void main(String[] args) throws Exception {
	System.out.println(new ServiceInstanceRDF().findService(
		//"bioinfo.icapture.ubc.ca", "getGoTerm", true));
		"atidb.org", "getInsertionsWithOffsetAsGFFByAGICode", true));
    }

    /**
     * 
     * <b>PRE: </b>None
     * <p>
     * <b>POST: </b>If a service instance exists such that it has a name of name
     * and an authoring URI of uri true is returned, otherwise false is
     * returned.
     * <p>
     * 
     * @param uri -
     *                the authors uri
     * @param name -
     *                the name of the service instance
     * @return true if a service instance exists with authority uri, and name
     *         name, otherwise false is returned.
     */
    public final boolean serviceInstanceExists(String uri, String name) {
	Central central = null;
	MobyService service = null;
	MobyService[] services = null;
	// initialize central
	central = getCentralImpl();
	// set up the query service
	service = new MobyService(name);
	service.setCategory("");
	service.setAuthority(uri);
	// query for the service
	try {
	    services = central.findService(service);
	} catch (MobyException e1) {
	    return false;
	}
	return services.length == 1;
    }

    public final boolean serviceInstanceExists(String uri, String name,
	    String lsid) {
	Central central = null;
	MobyService service = null;
	MobyService[] services = null;
	// initialize central
	central = getCentralImpl();
	// set up the query service
	service = new MobyService(name);
	service.setCategory("");
	service.setAuthority(uri);
	// service.setLSID(lsid);
	// query for the service
	try {
	    services = central.findService(service);
	} catch (MobyException e1) {
	    return false;
	}

	if (services.length == 1) {
	    service = services[0];
	    return (service.getLSID().equals(lsid));
	}
	return false;
    }

    /**
     * Default Constructor: Uses the default mobycentral
     * 
     * @throws MobyException
     *                 if there is a problem communicating with the registry
     */
    public ServiceInstanceRDF() throws MobyException {
	this(new Registry(CentralImpl.getDefaultURL(), CentralImpl
		.getDefaultURL(), CentralImpl.getDefaultURI()));
    }

    /**
     * Constructor:
     * 
     * @param reg
     *                the registry to use
     * @throws MobyException
     *                 if there is a problem communicating with the registry
     */
    public ServiceInstanceRDF(Registry reg) throws MobyException {
	central = new CentralImpl(reg.getEndpoint(), reg.getNamespace());
	MobyResourceRef[] refs = central.getResourceRefs();
	for (MobyResourceRef ref : refs) {
	    if (ref.getResourceName().equals(
		    CentralImpl.DATA_TYPES_RESOURCE_NAME)) {
		OBJ = ref.getResourceLocation().toString();
		OBJ = validateUri(OBJ);
	    } else if (ref.getResourceName().equals(
		    CentralImpl.SERVICE_INSTANCES_RESOURCE_NAME)) {
		SI = ref.getResourceLocation().toString();
		SI = validateUri(SI);
	    } else if (ref.getResourceName().equals(
		    CentralImpl.NAMESPACES_RESOURCE_NAME)) {
		NS = ref.getResourceLocation().toString();
		NS = validateUri(NS);
	    } else if (ref.getResourceName().equals(
		    CentralImpl.SERVICE_TYPES_RESOURCE_NAME)) {
		SRV = ref.getResourceLocation().toString();
		SRV = validateUri(SRV);
	    }
	}
    }

    private String validateUri(String string) {
	boolean isValid = false;
	for (String s : trailing_characters) {
	    if (string.endsWith(s)) {
		isValid = true;
		if (PREFERED_TRAILING_CHARACTER == null)
		    PREFERED_TRAILING_CHARACTER = s;
		break;
	    }
	}
	// our default trailing char
	String trailer = "/";
	if (PREFERED_TRAILING_CHARACTER != null)
	    trailer = PREFERED_TRAILING_CHARACTER;
	return isValid ? string : string + trailer;
    }

    /**
     * 
     * @param model
     *                a jena model
     * @return a string of RDF/XML-ABBREV representing the RDF model
     */
    public final String serializeModel(Model model) {
	FilteredStream stream = new FilteredStream(new ByteArrayOutputStream());
	RDFWriter writer = model.getWriter("RDF/XML-ABBREV");
	writer.setProperty("showXmlDeclaration", "false");
	writer.setProperty("tab", "1");
	writer.write(model, stream, null);
	return stream.getOutput();
    }

    /*
     * method that actually creates the rdf based on one or more services
     * contained in the array services
     */
    private final String createRDF(MobyService[] services, boolean useLSIDs) {
	Model model = createRDFModel(ModelFactory.createDefaultModel(),
		services, useLSIDs);
	return serializeModel(model);
    }

    /**
     * 
     * @param model
     *                an RDF model to add the services.
     * @param services
     *                an array of MobyService objects.
     * @param useLSIDs
     *                if true then the LSID for the service is added to the RDF
     *                model otherwise it is not.
     * @return the model with service signatures contained within.
     */
    public final Model createRDFModel(Model model, MobyService[] services,
	    boolean useLSIDs) {
	if (model == null) {
	    model = ModelFactory.createDefaultModel();
	}
	// set up the prefixes/namespaces
	Map<String, String> map = model.getNsPrefixMap();
	map.put("mobyService", SRV);
	map.put("mobyNamespace", NS);
	map.put("mobyObject", OBJ);
	map.put("protege-dc", DC_PROTEGE.getURI());
	map.put("mygrid", FetaVocabulary.getURI());
	model.setNsPrefixes(map);

	for (int i = 0; i < services.length; i++) {
	    MobyData[] primaryInputs = services[i].getPrimaryInputs(); // consumes
	    MobyData[] secondaryInputs = services[i].getSecondaryInputs(); // consumes
	    MobyData[] outputs = services[i].getPrimaryOutputs(); // produces

	    // start creating the rdf
	    Resource subject = model.createResource(SI
		    + services[i].getAuthority() + "," + services[i].getName(),
		    FetaVocabulary.serviceDescription);
	    subject.addProperty(FetaVocabulary.hasServiceDescriptionLocation,
		    services[i].getSignatureURL());
	    subject.addProperty(DC_PROTEGE.format, services[i].getCategory());
	    subject.addProperty(FetaVocabulary.locationURI, services[i]
		    .getURL());
	    subject.addProperty(FetaVocabulary.hasServiceDescriptionText,
		    services[i].getDescription());
	    subject.addProperty(FetaVocabulary.hasServiceNameText, services[i]
		    .getName());

	    if (useLSIDs)
		subject.addProperty(DC_PROTEGE.identifier, services[i]
			.getLSID());

	    // create the organization node
	    Resource publishedBy = model.createResource(SI
		    + MD5Checksum.md5(services[i].getAuthority() + "/"
			    + services[i].getName() + "/"
			    + FetaVocabulary.providedBy.getURI()));
	    publishedBy.addProperty(RDF.type, FetaVocabulary.organisation);
	    publishedBy.addProperty(DC_PROTEGE.creator, services[i]
		    .getEmailContact());
	    publishedBy.addProperty(DC_PROTEGE.publisher, services[i]
		    .getAuthority());
	    publishedBy.addProperty(FetaVocabulary.authoritative, ((services[i]
		    .isAuthoritative()) ? "true" : "false"));
	    subject.addProperty(FetaVocabulary.providedBy, publishedBy);

	    // add the inputs/outputs
	    Resource hasOperation = model.createResource(SI
		    + MD5Checksum.md5(services[i].getAuthority() + "/"
			    + services[i].getName() + "/"
			    + FetaVocabulary.hasOperation.getURI()));
	    hasOperation.addProperty(RDF.type, FetaVocabulary.operation);
	    // add the hasOperation to the resource
	    subject.addProperty(FetaVocabulary.hasOperation, hasOperation);
	    hasOperation.addProperty(FetaVocabulary.hasOperationNameText,
		    services[i].getName());

	    // add the performsTask information
	    Resource performs = model.createResource(SI
		    + MD5Checksum.md5(services[i].getAuthority() + "/"
			    + services[i].getName() + "/"
			    + FetaVocabulary.performsTask.getURI()));
	    performs.addProperty(RDF.type, FetaVocabulary.performsTask);
	    // if service type is lsid, dont append to URI
	    try {
		new LSID(services[i].getServiceType().getName());
		performs.addProperty(RDF.type, model.createResource(services[i]
			.getServiceType().getName()));
	    } catch (MalformedLSIDException e) {
		// not an lsid
		performs.addProperty(RDF.type, model.createResource(SRV
			+ services[i].getServiceType().getName()));
	    }

	    hasOperation.addProperty(FetaVocabulary.performsTask, performs);

	    // add any unit test information now
	    if (services[i].getUnitTests() != null) {
		for (MobyUnitTest mobyUnitTest : services[i].getUnitTests()) {
		    Resource unitTest = model.createResource(SI
			    + MD5Checksum.md5(services[i].getAuthority()
				    + "/"
				    + services[i].getName()
				    + "/"
				    + mobyUnitTest.getExampleInput() .trim()
				    + mobyUnitTest.getValidOutputXML() .trim()
				    + mobyUnitTest.getValidREGEX() .trim()
				    + mobyUnitTest.getValidXPath() .trim()
				    + "/"
				    + FetaVocabulary.unitTest.getURI()));
		    unitTest.addProperty(RDF.type, FetaVocabulary.unitTest);
		    // add the example input if defined
		    if (!mobyUnitTest.getExampleInput().trim().equals("")) {
			unitTest.addProperty(FetaVocabulary.exampleInput, mobyUnitTest.getExampleInput());
		    }
		    // add the valid output xml if defined
		    if (!mobyUnitTest.getValidOutputXML().trim().equals("")) {
			unitTest.addProperty(FetaVocabulary.validOutputXML, mobyUnitTest.getValidOutputXML());
		    }
		    // add the valid regex if defined
		    if (!mobyUnitTest.getValidREGEX().trim().equals("")) {
			unitTest.addProperty(FetaVocabulary.validREGEX, mobyUnitTest.getValidREGEX());
		    }
		    // add the xpath expression if defined
		    if (!mobyUnitTest.getValidXPath().trim().equals("")) {
			unitTest.addProperty(FetaVocabulary.validXPath, mobyUnitTest.getValidXPath());
		    }
		    // add node to the graph
		    hasOperation.addProperty(FetaVocabulary.hasUnitTest, unitTest);
		}
	    }
	    
	    
	    
	    if (primaryInputs.length > 0) {
		for (int j = 0; j < primaryInputs.length; j++) {
		    if (primaryInputs[j] instanceof MobyPrimaryDataSimple) {
			MobyPrimaryDataSimple simpleData = (MobyPrimaryDataSimple) primaryInputs[j];
			Resource parameter = model.createResource(SI
				+ MD5Checksum.md5(services[i].getAuthority()
					+ "/"
					+ services[i].getName()
					+ "/isSimple/"
					+ FetaVocabulary.inputParameter
						.getURI() + "/"
					+ simpleData.getName()));
			parameter.addProperty(RDF.type,
				FetaVocabulary.parameter);
			parameter.addProperty(
				FetaVocabulary.hasParameterNameText, simpleData
					.getName());
			Resource pType = model.createResource(SI
				+ MD5Checksum.md5(services[i].getAuthority()
					+ "/"
					+ services[i].getName()
					+ "/isSimple/"
					+ FetaVocabulary.hasParameterType
						.getURI() + "/"
					+ simpleData.getName()));
			pType.addProperty(RDF.type,
				FetaVocabulary.simpleParameter);
			parameter.addProperty(FetaVocabulary.hasParameterType,
				pType);

			boolean isLSID = false;
			try {
			    new LSID(simpleData.getDataType().getName());
			    isLSID = true;
			} catch (MalformedLSIDException e1) {
			    isLSID = false;
			}
			// create the resource
			Resource bnode = model.createResource(SI
				+ MD5Checksum.md5(services[i].getAuthority()
					+ "/"
					+ services[i].getName()
					+ "/isSimple/"
					+ FetaVocabulary.inputParameter
						.getURI() + "/"
					+ simpleData.getName() + "/"
					+ simpleData.getDataType().getName()));
			bnode.addProperty(RDF.type, model
				.createResource(isLSID ? simpleData
					.getDataType().getName() : OBJ
					+ simpleData.getDataType().getName()));
			parameter
				.addProperty(FetaVocabulary.object_type, bnode);

			MobyNamespace[] namespaces = simpleData.getNamespaces();
			if (namespaces.length > 0) {
			    for (int k = 0; k < namespaces.length; k++) {
				// if namespace is lsid, dont append to URI
				Resource parameterNamespace = model
					.createResource(SI
						+ MD5Checksum
							.md5(services[i]
								.getAuthority()
								+ "/"
								+ services[i]
									.getName()
								+ "/isSimple/"
								+ FetaVocabulary.inputParameter
									.getURI()
								+ "/"
								+ simpleData
									.getName()
								+ "/"
								+ namespaces[k]
									.getName()));
				parameterNamespace.addProperty(RDF.type,
					FetaVocabulary.parameterNamespace);
				parameter.addProperty(
					FetaVocabulary.inNamespaces,
					parameterNamespace);
				isLSID = false;
				try {
				    new LSID(namespaces[k].getName());
				    isLSID = true;
				} catch (MalformedLSIDException e) {
				    isLSID = false;
				}
				parameterNamespace.addProperty(RDF.type, model
					.createResource(isLSID ? namespaces[k]
						.getName() : NS
						+ namespaces[k].getName()));
			    }
			}
			hasOperation.addProperty(FetaVocabulary.inputParameter,
				parameter);
		    } else if (primaryInputs[j] instanceof MobyPrimaryDataSet) {
			MobyPrimaryDataSimple[] setOfSimpleData = ((MobyPrimaryDataSet) primaryInputs[j])
				.getElements();
			if (setOfSimpleData.length < 1) {
			    continue;
			}
			String collectionName = ((MobyPrimaryDataSet) primaryInputs[j])
				.getName();
			for (int k = 0; k < setOfSimpleData.length; k++) {
			    if (setOfSimpleData[k] instanceof MobyPrimaryDataSimple) {
				Resource parameter = model
					.createResource(
						SI
							+ MD5Checksum
								.md5(services[i]
									.getAuthority()
									+ "/"
									+ services[i]
										.getName()
									+ "/isCollection/"
									+ FetaVocabulary.inputParameter
										.getURI()
									+ "/"
									+ collectionName))
					.addProperty(RDF.type,
						FetaVocabulary.parameter);
				parameter.addProperty(
					FetaVocabulary.hasParameterNameText,
					collectionName);
				parameter
					.addProperty(
						FetaVocabulary.hasParameterType,
						model
							.createResource(
								SI
									+ MD5Checksum
										.md5(services[i]
											.getAuthority()
											+ "/"
											+ services[i]
												.getName()
											+ "/isCollection/"
											+ FetaVocabulary.hasParameterType
												.getURI()
											+ "/"
											+ collectionName))
							.addProperty(
								RDF.type,
								FetaVocabulary.collectionParameter));
				// if object is lsid, dont append to URI
				try {
				    Resource bnode = model
					    .createResource(SI
						    + MD5Checksum
							    .md5(services[i]
								    .getAuthority()
								    + "/"
								    + services[i]
									    .getName()
								    + "/isCollection/"
								    + FetaVocabulary.inputParameter
									    .getURI()
								    + "/"
								    + collectionName
								    + "/"
								    + setOfSimpleData[k]
									    .getDataType()
									    .getName()));
				    new LSID(setOfSimpleData[k].getDataType()
					    .getName());
				    bnode.addProperty(RDF.type, model
					    .createResource(setOfSimpleData[k]
						    .getDataType().getName()));
				    parameter.addProperty(
					    FetaVocabulary.object_type, bnode);

				} catch (MalformedLSIDException e) {
				    Resource bnode = model
					    .createResource(SI
						    + MD5Checksum
							    .md5(services[i]
								    .getAuthority()
								    + "/"
								    + services[i]
									    .getName()
								    + "/isCollection/"
								    + FetaVocabulary.inputParameter
									    .getURI()
								    + "/"
								    + collectionName
								    +"/"
								    + setOfSimpleData[k]
									    .getDataType()
									    .getName()));
				    bnode.addProperty(RDF.type, model
					    .createResource(OBJ
						    + setOfSimpleData[k]
							    .getDataType()
							    .getName()));
				    parameter.addProperty(
					    FetaVocabulary.object_type, bnode);
				}

				MobyNamespace[] namespaces = setOfSimpleData[k]
					.getNamespaces();
				if (namespaces.length > 0) {
				    for (int index = 0; index < namespaces.length; index++) {
					// if namespace is lsid, dont
					// append to URI
					Resource parameterNamespace = model
						.createResource(SI
							+ MD5Checksum
								.md5(services[i]
									.getAuthority()
									+ "/"
									+ services[i]
										.getName()
									+ "/isCollection/"
									+ FetaVocabulary.inputParameter
										.getURI()
									+ "/"
									+ collectionName
									+ "/"
									+ namespaces[k]
										.getName()));
					parameterNamespace
						.addProperty(
							RDF.type,
							FetaVocabulary.parameterNamespace);
					try {
					    new LSID(namespaces[index]
						    .getName());
					    parameterNamespace
						    .addProperty(
							    RDF.type,
							    model
								    .createResource(namespaces[index]
									    .getName()));
					} catch (MalformedLSIDException e) {
					    parameterNamespace
						    .addProperty(
							    RDF.type,
							    model
								    .createResource(NS
									    + namespaces[index]
										    .getName()));
					}
					parameter.addProperty(
						FetaVocabulary.inNamespaces,
						parameterNamespace);

				    }
				    /*
				     * parameter.addProperty(
				     * FetaVocabulary.inNamespaces,
				     * parameterNamespace);
				     */
				}
				hasOperation.addProperty(
					FetaVocabulary.inputParameter,
					parameter);
			    } else {
				System.err
					.print("A collection of a collection in getServiceInstances()!");
				return null;
			    }
			}
		    } else {
			// ERROR
			System.err
				.println("Primary input was not of known subtype (simple or collection)");
			return null;
		    }

		}
	    }
	    if (secondaryInputs.length > 0) {
		for (int j = 0; j < secondaryInputs.length; j++) {
		    if (secondaryInputs[j] instanceof MobySecondaryData) {
			MobySecondaryData data = (MobySecondaryData) secondaryInputs[j];
			Resource _li = model.createResource(
				SI
					+ MD5Checksum.md5(services[i]
						.getAuthority()
						+ "/"
						+ services[i].getName()
						+ "/isSecondaryInputParameter/"
						+ FetaVocabulary.inputParameter
							.getURI()
						+ "/"
						+ data.getName())).addProperty(
				RDF.type, FetaVocabulary.parameter);
			_li
				.addProperty(
					FetaVocabulary.hasParameterType,
					model
						.createResource(
							SI
								+ MD5Checksum
									.md5(services[i]
										.getAuthority()
										+ "/"
										+ services[i]
											.getName()
										+ "/isSecondary/"
										+ FetaVocabulary.hasParameterType
											.getURI()
										+ "/"
										+ data
											.getName()))
						.addProperty(
							RDF.type,
							FetaVocabulary.secondaryParameter));

			if (!data.getName().equals(""))
			    _li
				    .addProperty(
					    FetaVocabulary.hasParameterNameText,
					    model.createTypedLiteral(data
						    .getName()));
			if (!data.getDefaultValue().equals(""))
			    _li.addProperty(FetaVocabulary.hasDefaultValue,
				    model.createTypedLiteral(data
					    .getDefaultValue()));
			_li.addProperty(FetaVocabulary.datatype, model
				.createTypedLiteral(data.getDataType()));

			// add the secondary description if necessary
			if (!data.getDescription().equals("")) // FIXME should
			    // be defined in
			    // FetaVocabulary
			    _li.addProperty(
				    FetaVocabulary.hasParameterDescriptionText,
				    model.createTypedLiteral(data
					    .getDescription()));

			/*
			 * _li.addProperty(RDF.type, model
			 * .getProperty(FetaModelRDF.getURI() +
			 * "secondaryParameter"));
			 */
			if (data.getDataType().equals("Integer")) {
			    _li.addProperty(FetaVocabulary.min, model
				    .createTypedLiteral(data.getMinValue()));
			    _li.addProperty(FetaVocabulary.max, model
				    .createTypedLiteral(data.getMaxValue()));
			}
			String[] vals = data.getAllowedValues();
			for (int k = 0; k < vals.length; k++) {
			    _li.addProperty(FetaVocabulary.enumeration, model
				    .createTypedLiteral(vals[k]));
			}
			hasOperation.addProperty(FetaVocabulary.inputParameter,
				_li);
		    } else if (secondaryInputs[j] instanceof MobyDataSecondaryInstance) {
			// should not be here, but ...
		    } else {
			// ERROR
			log
				.error("Secondary input was not of known subtype (Secondary)");
			return null;
		    }
		}
	    }
	    if (outputs.length > 0) {
		for (int j = 0; j < outputs.length; j++) {
		    if (outputs[j] instanceof MobyPrimaryDataSimple) {
			MobyPrimaryDataSimple simpleData = (MobyPrimaryDataSimple) outputs[j];
			Resource parameter = model.createResource(SI
				+ MD5Checksum.md5(services[i].getAuthority()
					+ "/"
					+ services[i].getName()
					+ "/isSimple/"
					+ FetaVocabulary.outputParameter
						.getURI() + "/"
					+ simpleData.getName()));
			parameter.addProperty(
				FetaVocabulary.hasParameterNameText, simpleData
					.getName());
			Resource pType = model.createResource(SI
				+ MD5Checksum.md5(services[i].getAuthority()
					+ "/"
					+ services[i].getName()
					+ "/isSimple/"
					+ FetaVocabulary.hasParameterType
						.getURI() + "/"
					+ simpleData.getName()));
			pType.addProperty(RDF.type,
				FetaVocabulary.simpleParameter);
			parameter.addProperty(FetaVocabulary.hasParameterType,
				pType);

			boolean isLSID = false;
			try {
			    new LSID(simpleData.getDataType().getName());
			    isLSID = true;
			} catch (MalformedLSIDException e1) {
			    isLSID = false;
			}
			// create the resource
			Resource bnode = model.createResource(SI
				+ MD5Checksum.md5(services[i].getAuthority()
					+ "/"
					+ services[i].getName()
					+ "/isSimple/"
					+ FetaVocabulary.outputParameter
						.getURI() + "/"
					+ simpleData.getName() + "/"
					+ simpleData.getDataType().getName()));
			bnode.addProperty(RDF.type, model
				.createResource(isLSID ? simpleData
					.getDataType().getName() : OBJ
					+ simpleData.getDataType().getName()));
			parameter
				.addProperty(FetaVocabulary.object_type, bnode);

			MobyNamespace[] namespaces = simpleData.getNamespaces();
			if (namespaces.length > 0) {
			    for (int k = 0; k < namespaces.length; k++) {
				// if namespace is lsid, dont append to URI
				Resource parameterNamespace = model
					.createResource(SI
						+ MD5Checksum
							.md5(services[i]
								.getAuthority()
								+ "/"
								+ services[i]
									.getName()
								+ "/isSimple/"
								+ FetaVocabulary.outputParameter
									.getURI()
								+ "/"
								+ simpleData
									.getName()
								+ "/"
								+ namespaces[k]
									.getName()));
				parameterNamespace.addProperty(RDF.type,
					FetaVocabulary.parameterNamespace);
				parameter.addProperty(
					FetaVocabulary.inNamespaces,
					parameterNamespace);
				try {
				    new LSID(namespaces[k].getName());
				    parameterNamespace.addProperty(RDF.type,
					    model.createResource(namespaces[k]
						    .getName()));
				} catch (MalformedLSIDException e) {
				    parameterNamespace.addProperty(RDF.type,
					    model.createResource(NS
						    + namespaces[k].getName()));
				}
			    }
			}
			hasOperation.addProperty(
				FetaVocabulary.outputParameter, parameter);
		    } else if (outputs[j] instanceof MobyPrimaryDataSet) {
			MobyPrimaryDataSimple[] setOfSimpleData = ((MobyPrimaryDataSet) outputs[j])
				.getElements();
			if (setOfSimpleData.length < 1) {
			    continue;
			}
			String collectionName = ((MobyPrimaryDataSet) outputs[j])
				.getName();
			for (int k = 0; k < setOfSimpleData.length; k++) {
			    if (setOfSimpleData[k] instanceof MobyPrimaryDataSimple) {
				Resource parameter = model
					.createResource(
						SI
							+ MD5Checksum
								.md5(services[i]
									.getAuthority()
									+ "/"
									+ services[i]
										.getName()
									+ "/isCollection/"
									+ FetaVocabulary.outputParameter
										.getURI()
									+ "/"
									+ collectionName))
					.addProperty(RDF.type,
						FetaVocabulary.parameter);
				parameter.addProperty(
					FetaVocabulary.hasParameterNameText,
					collectionName);
				Resource pType = model.createResource(SI
					+ MD5Checksum.md5(services[i].getAuthority()
						+ "/"
						+ services[i].getName()
						+ "/isCollection/"
						+ FetaVocabulary.hasParameterType
							.getURI() + "/"
						+ collectionName));
				pType.addProperty(RDF.type,
					FetaVocabulary.collectionParameter);
				parameter.addProperty(FetaVocabulary.hasParameterType,
					pType);
				// if object is lsid, dont append to URI
				try {
				    Resource bnode = model
					    .createResource(SI
						    + MD5Checksum
							    .md5(services[i]
								    .getAuthority()
								    + "/"
								    + services[i]
									    .getName()
								    + "/isCollection/"
								    + FetaVocabulary.outputParameter
									    .getURI()
								    + "/"
								    + collectionName
								    + "/"
								    + setOfSimpleData[k]
									    .getDataType()
									    .getName()));
				    new LSID(setOfSimpleData[k].getDataType()
					    .getName());
				    bnode.addProperty(RDF.type, model
					    .createResource(setOfSimpleData[k]
						    .getDataType().getName()));
				    parameter.addProperty(
					    FetaVocabulary.object_type, bnode);
				} catch (MalformedLSIDException e) {
				    Resource bnode = model
					    .createResource(SI
						    + MD5Checksum
							    .md5(services[i]
								    .getAuthority()
								    + "/"
								    + services[i]
									    .getName()
								    + "/isCollection/"
								    + FetaVocabulary.outputParameter
									    .getURI()
								    + "/"
								    + collectionName
								    + "/"
								    + setOfSimpleData[k]
									    .getDataType()
									    .getName()));
				    bnode.addProperty(RDF.type, model
					    .createResource(OBJ
						    + setOfSimpleData[k]
							    .getDataType()
							    .getName()));
				    parameter.addProperty(
					    FetaVocabulary.object_type, bnode);
				}

				MobyNamespace[] namespaces = setOfSimpleData[k]
					.getNamespaces();
				if (namespaces.length > 0) {
				    /*
				     * Resource parameterNamespace = model
				     * .createResource(FetaVocabulary.parameterNamespace);
				     */
				    for (int index = 0; index < namespaces.length; index++) {
					Resource parameterNamespace = model
						.createResource(SI
							+ MD5Checksum
								.md5(services[i]
									.getAuthority()
									+ "/"
									+ services[i]
										.getName()
									+ "/isCollection/"
									+ FetaVocabulary.outputParameter
										.getURI()
									+ "/"
									+ collectionName
									+ "/"
									+ namespaces[k]
										.getName()));
					parameterNamespace
						.addProperty(
							RDF.type,
							FetaVocabulary.parameterNamespace);
					try {
					    new LSID(namespaces[index]
						    .getName());
					    parameterNamespace
						    .addProperty(
							    RDF.type,
							    model
								    .createResource(namespaces[index]
									    .getName()));
					} catch (MalformedLSIDException e) {
					    parameterNamespace
						    .addProperty(
							    RDF.type,
							    model
								    .createResource(NS
									    + namespaces[index]
										    .getName()));
					}
					parameter.addProperty(
						FetaVocabulary.inNamespaces,
						parameterNamespace);
				    }
				    /*
				     * parameter.addProperty(
				     * FetaVocabulary.inNamespaces,
				     * parameterNamespace);
				     */
				}
				hasOperation.addProperty(
					FetaVocabulary.outputParameter,
					parameter);
			    } else {
				System.err
					.print("A collection of a collection (output) in getServiceInstances()!");
				return null;
			    }
			}
		    } else {
			// ERROR
			System.err
				.println("Output was not of known subtype (simple or collection)");
			return null;
		    }
		}
	    }
	}

	return model;
    }

    private static class MD5Checksum {

	private static byte[] createChecksum(String msg) {
	    try {
		InputStream fis = new ByteArrayInputStream(msg.getBytes());
		byte[] buffer = new byte[1024];
		MessageDigest complete = MessageDigest.getInstance("MD5");
		int numRead;
		do {
		    numRead = fis.read(buffer);
		    if (numRead > 0) {
			complete.update(buffer, 0, numRead);
		    }
		} while (numRead != -1);
		fis.close();
		return complete.digest();
	    } catch (Exception e) {

	    }
	    return "".getBytes();
	}

	public static String md5(String msg) {
	    byte[] b = createChecksum(msg);
	    String result = "";
	    for (int i = 0; i < b.length; i++) {
		result += Integer.toString((b[i] & 0xff) + 0x100, 16)
			.substring(1);
	    }
	    return result;
	}
    }

    /**
     * 
     * <b>PRE: </b>name is either a valid name or null
     * <p>
     * <b>POST: </b>If authorURI is a valid authority, then a RDF containing all
     * of the service instances will be returned. If authorURI and name are
     * valid then a single RDF containing just the service instance identified
     * by name is returned.
     * <p>
     * 
     * @param authorURI -
     *                the authority to query
     * @param name -
     *                the name of the service instance if applicable or null.
     * @param useLSIDs
     *                whether or not we should include lsids
     * @return a string representing either all of services defined by
     *         authorURI, (if name is null) or a single service instances' rdf
     *         if name and authorURI are valid. If name and/or authorURI are
     *         invalid an empty rdf document is returned.
     */
    public final String findService(String authorURI, String name,
	    boolean useLSIDs) {
	if (name == null || name.trim().equals("")) {
	    return (useLSIDs ? getAllServices(authorURI) : getAllServices(
		    authorURI, false));
	} else {
	    return (useLSIDs ? getService(authorURI, name) : getService(
		    authorURI, name, false));
	}
    }

    /**
     * 
     * method that retrieves the RDF describing a specific service instance
     * based on the parameters domain, serviceName.
     * <p>
     * <b>PRE: </b>None.
     * <p>
     * <b>POST: </b>The RDF describing the service with parameters domain,
     * serviceName is created.
     * 
     * @param domain -
     *                the authoring domain of the service instance in question.
     * @param serviceName -
     *                the name of the service instance in question.
     * @return - the string representation of the RDF describing the service
     *         instance based on the parameters given.
     */
    public final String findService(String domain, String serviceName) {
	if (serviceName != null && !serviceName.trim().equals("")) {
	    // variables needed
	    MobyService service = null;
	    MobyService[] services = null;
	    Central central = null;
	    // initialize central
	    central = getCentralImpl();
	    // set up the query service
	    service = new MobyService(serviceName);
	    service.setAuthority(domain);
	    service.setCategory("");
	    // query for the service
	    try {
		services = central.findService(service);
	    } catch (MobyException e1) {
		log.error("Could not find service:\n" + e1.getMessage());
		return empty;
	    }
	    // only one service should be returned!
	    if (services.length != 1)
		return empty;
	    // get the rdf
	    String str = createRDF(services, true);
	    if (str == null)
		return empty;
	    return str;
	} else {
	    // servicename is null
	    MobyService service = null;
	    MobyService[] services = null;
	    Central central = getCentralImpl();
	    // set up the query service
	    service = new MobyService();
	    service.setAuthority(domain);
	    service.setCategory("");
	    // query for the service
	    try {
		services = central.findService(service);
	    } catch (MobyException e1) {
		log.error("Could not find service:\n" + e1.getMessage());
		return empty;
	    }
	    // get the rdf
	    String str = createRDF(services, true);
	    if (str == null)
		return empty;
	    return str;
	}
    }

    /**
     * 
     * method that retrieves the RDF describing a specific service instance
     * based on the parameters domain, serviceName, url and uri.
     * <p>
     * <b>PRE: </b>None.
     * <p>
     * <b>POST: </b>The RDF describing the service with parameters domain,
     * serviceName, url and uri is created.
     * 
     * @param domain -
     *                the authoring domain of the service instance in question.
     * @param serviceName -
     *                the name of the service instance in question.
     * @param url -
     *                the URL of the endpoint of the BioMoby registry that you
     *                would like to query. If null, the mobycentral registry is
     *                queried.
     * @param uri -
     *                the URI of the BioMoby registry that you would like to
     *                query. If null, the mobycentral registry is queried.
     * @param useLSIDs -
     *                whether or not the returned RDF document should contain an
     *                LSID. Set this to false to retrieve RDF that service
     *                providers should have.
     * @return - the string representation of the RDF describing the service
     *         instance based on the parameters given.
     */
    public final String findService(String domain, String serviceName,
	    String url, String uri, boolean useLSIDs) {
	if (serviceName != null) {
	    // variables needed
	    MobyService service = null;
	    MobyService[] services = null;
	    Central central = null;
	    // initialize central
	    try {
		central = new CentralImpl(url, uri);
	    } catch (MobyException e) {
		log.error("Could not connect to " + url + " in findService.");
		return empty;
	    }
	    // set up the query service
	    service = new MobyService(serviceName);
	    service.setCategory("");
	    service.setAuthority(domain);
	    // query for the service
	    try {
		services = central.findService(service);
	    } catch (MobyException e1) {
		log.error("Could not find service:\n" + e1.getMessage());
		return empty;
	    }
	    // only one service should be returned!
	    if (services.length != 1)
		return empty;
	    // get the rdf
	    String str = createRDF(services, useLSIDs);
	    if (str == null)
		return empty;
	    return str;
	} else {
	    // servicename is null
	    MobyService service = null;
	    MobyService[] services = null;
	    Central central = null;
	    // initialize central
	    try {
		central = new CentralImpl(url, uri);
	    } catch (MobyException e) {
		log.error("Could not connect to " + url + " in findService.");
		return empty;
	    }
	    // set up the query service
	    service = new MobyService();
	    service.setAuthority(domain);
	    service.setCategory("");
	    // query for the service
	    try {
		services = central.findService(service);
	    } catch (MobyException e1) {
		log.error("Could not find service:\n" + e1.getMessage());
		return empty;
	    }
	    // get the rdf
	    String str = createRDF(services, useLSIDs);
	    if (str == null)
		return empty;
	    return str;
	}
    }

    /*
     * a method that retrieves all of the service instances based on an
     * authoriy.
     */
    private final String getAllServices(String URI) {
	// variables needed
	MobyService service = null;
	MobyService[] services = null;
	Central central = getCentralImpl();
	// set up the query service
	service = new MobyService("");
	service.setCategory("");
	service.setAuthority(URI);
	// query for the service
	try {
	    services = central.findService(service);
	} catch (MobyException e1) {
	    log.error("error in getAllServices 2:\n" + e1.getMessage());
	    return empty;
	}
	// should have >= 1 service returned
	if (services.length <= 0)
	    return empty;
	// get the rdf
	String str = createRDF(services, true);
	if (str == null)
	    return empty;
	return str;
    }

    /*
     * a method that retrieves all of the service instances based on an
     * authoriy.
     */
    private final String getAllServices(String URI, boolean useLSIDs) {
	// variables needed
	MobyService service = null;
	MobyService[] services = null;
	Central central = getCentralImpl();
	// set up the query service
	service = new MobyService("");
	service.setCategory("");
	service.setAuthority(URI);
	// query for the service
	try {
	    services = central.findService(service);
	} catch (MobyException e1) {
	    log.error("error in getAllServices 2:\n" + e1.getMessage());
	    return empty;
	}
	// should have >= 1 service returned
	if (services.length <= 0)
	    return empty;
	// get the rdf
	String str = createRDF(services, useLSIDs);
	if (str == null)
	    return empty;
	return str;
    }

    /**
     * @return
     * @throws MobyException
     */
    private Central getCentralImpl() {
	return this.central;
    }

    /*
     * a method that retrieves a single service instance rdf document based on
     * the URI and name of that service.
     */
    private final String getService(String URI, String name) {
	// variables needed
	MobyService service = null;
	MobyService[] services = null;
	Central central = getCentralImpl();

	// set up the query service
	service = new MobyService(name);
	service.setCategory("");
	service.setAuthority(URI);
	// query for the service
	try {
	    services = central.findService(service);
	} catch (MobyException e1) {
	    log.error("error in getService 2:\n" + e1.getMessage());
	    return empty;
	}
	// only one service should be returned!
	if (services.length != 1)
	    return empty;
	// get the rdf
	String str = createRDF(services, true);
	if (str == null)
	    return empty;
	return str;
    }

    /*
     * a method that retrieves a single service instance rdf document based on
     * the URI and name of that service.
     */
    private final String getService(String URI, String name, boolean useLSIDs) {
	// variables needed
	MobyService service = null;
	MobyService[] services = null;
	Central central = getCentralImpl();

	// set up the query service
	service = new MobyService(name);
	service.setCategory("");
	service.setAuthority(URI);
	// query for the service
	try {
	    services = central.findService(service);
	} catch (MobyException e1) {
	    log.error("error in getService 2:\n" + e1.getMessage());
	    return empty;
	}
	// only one service should be returned!
	if (services.length != 1)
	    return empty;
	// get the rdf
	String str = createRDF(services, useLSIDs);
	if (str == null)
	    return empty;
	return str;
    }
}
