/*
 * Created on Jun 17, 2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package org.biomoby.shared.schema;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedList;

import org.biomoby.client.rdf.vocabulary.Predicates;
import org.biomoby.shared.MobyException;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.RDFReader;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;

/**
 * @author Eddie
 * 
 * TODO To change the template for this generated type comment go to Window -
 * Preferences - Java - Code Style - Code Templates
 */
public class RdfParser {

	public static String OBJECT_URI = "http://biomoby.org/RESOURCES/MOBY-S/Objects#";

	public static MElement buildMElement(String name) {
		String rdf = null;
		try {
			rdf = getObjectRdf();
		} catch (MobyException e) {
			e.printStackTrace();
		}
		// return null if i cant build the element
		if (rdf == null)
			return null;

		// create the model
		Model model = ModelFactory.createDefaultModel(); // the RESOURCE model
		RDFReader reader = model.getReader();
		reader.read(model, new StringReader(rdf), null);

		// check if a certain property exists to ensure that you a valid object
		if (model.getResource(OBJECT_URI + name).getProperty(RDFS.comment) == null) {
		    return null;
		}
		// start querying model for what we need and create the MElement
		String rootDescription = getDescription(model, name);
		MElement root = new MElement(name, "", rootDescription);
		String rootsParent = getParentDataType(model, name);
		if (rootsParent == null)
			rootsParent = "";
		if (MElement.isPrimitive(rootsParent)) {
			root.setType(rootsParent);
		} else {
			root.setType(null);
		}
		// go through the isa relationships and extract the has/hasa
		processRelationships(root, model, name);

		return root;
	}

	/**
	 * @param root
	 * @param model
	 * @param name
	 */
	private static void processRelationships(MElement root, Model model, String name) {
		if (name.indexOf("#") > 0) {
			name = name.substring(name.indexOf("#")+1);
		}
		Resource resource = model.getResource(OBJECT_URI + name);
		if (resource.hasProperty(RDFS.subClassOf)) {
            String parent = resource.getProperty(RDFS.subClassOf)
                    .getObject().asNode().getURI();
            processRelationships(root, model, parent);
        }
		// process the has children
		processChildrenTypes(root, model, Predicates.has, name);
		// process the hasa children
		processChildrenTypes(root, model, Predicates.hasa, name);
	}

	/**
	 * @param root
	 * @param model
	 * @param has
	 * @param name
	 */
	private static void processChildrenTypes(MElement root, Model model,
			Property property, String name) {
		Resource resource = model.getResource(OBJECT_URI + name);
		StmtIterator iterator = resource.listProperties(property);
		if (iterator != null)
			while (iterator.hasNext()) {
				Statement next = (Statement) iterator.next();
				RDFNode object = next.getObject();
				if (object instanceof Resource) {
					Resource child = (Resource) object;
					String qualifiedName = null;
					String childName = "";
					String childArticleName = "";
					String childDescription = "";
					if (child.hasProperty(RDF.type)) {
						qualifiedName = child.getProperty(RDF.type).getObject()
								.toString();
						childName = stripOutURI(child.getProperty(RDF.type)
								.getObject().toString());
					}
					if (child.hasProperty(Predicates.articleName)) {
						childArticleName = stripOutLiteral(child.getProperty(
								Predicates.articleName).getObject().toString());
					}
					childDescription = stripOutLiteral(model.getResource(
							qualifiedName).getProperty(RDFS.comment)
							.getObject().toString());

					MElement element = new MElement(childName,
							childArticleName, childDescription);
					String rootsParent = getParentDataType(model, childName);
					if (MElement.isPrimitive(rootsParent)) {
						element.setType(rootsParent);
					} else {
						element.setType(null);
					}
					processChildrenTypes(element, model, Predicates.has,
							childName);
					processChildrenTypes(element, model, Predicates.hasa,
							childName);
					if (property.getLocalName().equalsIgnoreCase("hasa")) {
						root.addHasaMElement(element);
					} else {
						root.addHasMElement(element);
					}
				}
			}
	}

	/**
	 * @param model
	 * @param name
	 * @return
	 */
	private static String getParentDataType(Model model, String name) {

		// the statement should return null iff the object is the Moby root
		// object
		if (name.equals("Object"))
			return null;
		LinkedList list = new LinkedList();
		list.addFirst(name);
		name = OBJECT_URI + name;
		String parent = "";
		String previousParent = "";
		Resource resource = model.getResource(name);
		Statement statement = resource.getProperty(RDFS.subClassOf);
		do {
			parent = statement.getObject().toString();
			if (!((String) list.getFirst()).equals(parent)) {
				list.addFirst(parent);
			}
			resource = model.getResource(parent);
			statement = resource.getProperty(RDFS.subClassOf);

		} while (statement != null);
		// check to see if we have a primitive
		parent = (String) list.removeFirst();
		if (list.size() > 0) {
			previousParent = (String) list.removeFirst();
			previousParent = stripOutURI(previousParent);
			if (MElement.isPrimitive(previousParent)) {
				return previousParent;
			}
		}
		// no primitive parent, so every object inherits from object
		parent = stripOutURI(parent);
		return parent;
	}

	/**
	 * @param model
	 * @param name
	 * @return
	 */
	private static String getDescription(Model model, String name) {
		Resource resource = model.getResource(OBJECT_URI + name);
		Statement comment = resource.getProperty(RDFS.comment);
		String description = comment.getObject().toString();
		description = stripOutLiteral(description);
		return description;
	}

	private static String getObjectRdf() throws MobyException {
		String s = null;
		StringBuffer sb = new StringBuffer();
		URL url = null;
		try {
			url = new URL(OBJECT_URI);
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
			return null;
		}
		try {
			BufferedReader in = null;
	        in = new BufferedReader(new InputStreamReader(url.openStream()));
	        while ((s = in.readLine()) != null) {
	        	sb.append(s);
	        }
			
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		return sb.toString();
	}

	private static String stripOutURI(String object) {
		if (object.indexOf("#") > 0)
			object = object.substring(object.indexOf("#") + 1);
		return object;
	}

	private static String stripOutLiteral(String object) {
		if (object.indexOf("~") > 0)
			object = object.substring(0, object.indexOf("~"));
		return object;
	}

	public static void main(String[] args) {
		System.out.println("Is it me?");
		System.out.println("\nOutput Imag Object\n");
		System.out.println(RdfParser.buildMElement("DNASequence"));
/*		System.out.println("###########################################");
		System.out.println("Outputting a String object");
		System.out.println(RdfParser.buildMElement("String"));

		System.out.println("###########################################");
		System.out.println("Outputting the base object");
		System.out.println(RdfParser.buildMElement("Object"));

		System.out.println("###########################################");
		System.out.println("Outputting a DNA sequence object");
		System.out.println(RdfParser.buildMElement("DNASequence"));

		System.out.println("###########################################");
		System.out.println("Outputting a GFF object");
		System.out.println(RdfParser.buildMElement("GFF"));

		System.out.println("###########################################");
		System.out.println("Outputting a BasicGFFSequenceFeature object");
		System.out.println(RdfParser.buildMElement("BasicGFFSequenceFeature"));
*/
	}
}
