import java.io.InputStream;

import org.apache.jena.ontology.AllValuesFromRestriction;
import org.apache.jena.ontology.EnumeratedClass;
import org.apache.jena.ontology.HasValueRestriction;
import org.apache.jena.ontology.Individual;
import org.apache.jena.ontology.ObjectProperty;
import org.apache.jena.ontology.OntClass;
import org.apache.jena.ontology.OntModel;
import org.apache.jena.ontology.OntModelSpec;
import org.apache.jena.ontology.OntResource;
import org.apache.jena.ontology.Restriction;
import org.apache.jena.ontology.SomeValuesFromRestriction;
import org.apache.jena.ontology.UnionClass;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.DatasetFactory;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.QuerySolutionMap;
import org.apache.jena.query.ResultSet;
import org.apache.jena.query.ResultSetFactory;
import org.apache.jena.query.ResultSetFormatter;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.ResIterator;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.SimpleSelector;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.riot.WebContent;
import org.apache.jena.sparql.engine.http.QueryEngineHTTP;
import org.apache.jena.util.FileManager;
import org.apache.jena.util.iterator.ExtendedIterator;
import org.apache.jena.vocabulary.RDFS;
import org.apache.jena.vocabulary.VCARD;
import org.apache.jena.vocabulary.XSD;

public class JenaBasic {
	
	public static void main(String[] args){ 
		// some definitions
		String personURI    = "http://somewhere/JohnSmith";
		String givenName    = "John";
		String familyName   = "Smith";
		String fullName     = givenName + " " + familyName;

		// create an empty Model
		Model model = ModelFactory.createDefaultModel();

		// create the resource
		//   and add the properties cascading style
		Resource johnSmith = model.createResource(personURI)
	         	.addProperty(VCARD.FN, fullName)
	         	.addProperty(VCARD.N, model.createResource()
	                           .addProperty(VCARD.Given, givenName)
	                           .addProperty(VCARD.Family, familyName));
		model.write(System.out, "N-TRIPLES"); //"RDF/XML-ABBREV"
	

		 // use the RDFDataMgr to load the model from a file
		String inputFileName = "./resource/mondial-europe.n3";
		Model model2 = RDFDataMgr.loadModel( inputFileName );
		
		// write it to standard out
		/*StmtIterator iter = model2.listStatements();
		
		// print out the predicate, subject and object of each statement
		while (iter.hasNext()) {
		    Statement stmt      = iter.nextStatement();  // get next statement
		    Resource  subject   = stmt.getSubject();     // get the subject
		    Property  predicate = stmt.getPredicate();   // get the predicate
		    RDFNode   object    = stmt.getObject();      // get the object

		    System.out.print(subject.toString());
		    System.out.print(" " + predicate.toString() + " ");
		    if (object instanceof Resource) {
		       System.out.print(object.toString());
		    } else {
		        // object is a literal
		        System.out.print(" \"" + object.toString() + "\"");
		    }

		    System.out.println(" .");
		} */
		
		// define prefixes
		//model2.setNsPrefix("meta", "http://www.semwebtech.org/mondial/10/meta#");
		
		Resource germany = model2.getResource("http://www.semwebtech.org/mondial/10/countries/D/");
		StmtIterator geriter = germany.listProperties();
		
		// print out the predicate, subject and object of each statement
		while (geriter.hasNext()) {
			Statement stmt      = geriter.nextStatement();  // get next statement
			Resource  subject   = stmt.getSubject();     // get the subject
			Property  predicate = stmt.getPredicate();   // get the predicate
			RDFNode   object    = stmt.getObject();      // get the object

			System.out.print(subject.toString());
			System.out.print(" " + predicate.toString() + " ");
			if (object instanceof Resource) {
				System.out.print(object.toString());
			} else {
				// object is a literal
				System.out.print(" \"" + object.toString() + "\"");
			}

			System.out.println(" .");
		}
		
		//Model Union, Intersection, Difference
		/*
		Model mEurope = RDFDataMgr.loadModel("./resource/mondial-europe.n3");
		Model mAll = RDFDataMgr.loadModel("./resource/mondial.n3");
		
		Model mUnion = mAll.union(mEurope); //should do nothing
		Model mIntersection = mAll.intersection(mEurope); //should return mEurope
		Model mDifference = mAll.difference(mEurope); //should return only non-europe facts
		*/
		
		
		//Language tags
		Resource r = model.createResource();
		r.addProperty(RDFS.label, model.createLiteral("hello","en"));
		r.addProperty(RDFS.label, model.createLiteral("hallo","de"));
		ResIterator tagIter = model.listResourcesWithProperty(RDFS.label);
		while(tagIter.hasNext()){
			Resource res = tagIter.nextResource();
			StmtIterator prop = res.listProperties(RDFS.label);
			while(prop.hasNext()){
				System.out.println(prop.nextStatement().getObject().asLiteral().getLanguage());
			}	
		}
		
		
		//Querying
		
		//Simple
		StmtIterator qIter = model2.listStatements(
				new SimpleSelector(null, model2.getProperty("http://www.semwebtech.org/mondial/10/meta#hasCity"), (RDFNode) null) {
					public boolean selects(Statement s){
						return s.getSubject().toString().endsWith("countries/F/");
					}
				});
		while(qIter.hasNext()){
			Statement stmt = qIter.nextStatement();
			Resource  subject   = stmt.getSubject();     // get the subject
			Property  predicate = stmt.getPredicate();   // get the predicate
			RDFNode   object    = stmt.getObject();      // get the object

			System.out.print(subject.toString());
			System.out.print(" " + predicate.toString() + " ");
			if (object instanceof Resource) {
				System.out.print(object.toString());
			} else {
				// object is a literal
				System.out.print(" \"" + object.toString() + "\"");
			}

			System.out.println(" .");
		}
		
		//ARQ
		//Command-line "sparql --query cap-hq.sparql --data mondial-europe.n3"
		
		//Usual
		String queryString = 
				"prefix mon: <http://www.semwebtech.org/mondial/10/meta#> " +
				"prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
				"SELECT ?CN " +
				"WHERE {?C a mon:Country . ?C mon:name ?CN } " +
				"ORDER BY ?CN ";	
		
		Query query = QueryFactory.create(queryString);
		QueryExecution qe = QueryExecutionFactory.create(query, model2);
		ResultSet results = qe.execSelect();
		
		//ResultSet persistentResult = ResultSetFactory.copyResults(results); //To keep it available even after iteration and closing of the QueryExecution
		
		/*while(persistentResult.hasNext()){
			QuerySolution sol = persistentResult.next();
			RDFNode n = sol.get("CN");
		}*/
		ResultSetFormatter.out(System.out, results, query);
		//ResultSetFormatter.outputAsXML(System.out, results);
		qe.close();
		
		//Try-with-resource
		/*try(QueryExecution qe = QueryExecutionFactory.create(query, model2)) {
			ResultSetFormatter.out(qe.execSelect());
		}*/
		
		
		//Names Graphs (FROM NAMED <URI>), instead of/with background data 

		//Dataset dataset = DatasetFactory.create();
		//dataset.setDefaultModel(model2);
		//dataset.addNamedModel("http://foo/bla", mAll);
		//Model namedModel = ModelFactory.createDefaultModel();
		
		String namedQueryString = 
				 "prefix mon: <http://www.semwebtech.org/mondial/10/meta#> " +
				 "prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n " +
				 //"prefix p: <http://foo/>\n " +
				 "prefix p: <resource/> " +
				 "SELECT ?g ?C ?CC ?PG " +
				 "FROM <https://www.dbis.informatik.uni-goettingen.de/Mondial/Mondial-RDF/mondial-europe.n3> " +
				 //"FROM NAMED <file:resource/mondial-europe.n3> " +
				 "FROM NAMED <file:resource/mondial.n3> " +
				 "WHERE { "
				 + "?C a mon:Country . ?C mon:name ?CN . "
				 + "OPTIONAL { "
				 + "GRAPH p:mondial.n3 { "
				 	+ "?C mon:hasCity ?CC . "
				 	+ "} } " 
				 + "GRAPH <resource/mondial.n3> { "
				 	+ "?C mon:populationGrowth ?PG . "
				 	+ "} "
				 + "} "
				 + "ORDER BY ?C ";
		Query namedQuery = QueryFactory.create(namedQueryString);
		
		QueryExecution qe2 = QueryExecutionFactory.create(namedQuery); //, dataset);
		ResultSet results2 = qe2.execSelect();

		ResultSetFormatter.out(System.out, results2, namedQuery);
		
		qe2.close();
		
		//Prepared statements
		QuerySolutionMap initialBinding = new QuerySolutionMap();
		initialBinding.add("C", model2.getResource("http://www.semwebtech.org/mondial/10/countries/GR/"));
		QueryExecution qe3 = QueryExecutionFactory.create(query, model2, initialBinding);
		ResultSet results3 = qe3.execSelect();
		
		ResultSetFormatter.out(System.out, results3, query);
		qe3.close();
		
		
		//Remote Queries
		
		QueryEngineHTTP remoteQE = (QueryEngineHTTP) QueryExecutionFactory.sparqlService("http://www.semwebtech.org/mondial/10/sparql", query);
		//remoteQE.setSelectContentType(WebContent.contentTypeResultsXML);
		ResultSet remoteResults = remoteQE.execSelect();
		ResultSetFormatter.out(System.out, remoteResults, query);
		remoteQE.close();
		
		
		//Jena Ontology API
		//Jena reasoner create a new RDF model, which appears to contain the triples that are derived from reasoning 
		// as well as the triples that were asserted in the base model
		//Ontology model <-> (Jena Graph interface) Reasoner <-> (Jena Graph interface) Base RDF graph + n * (Jena Graph Interface) Imported ontology n
		
		Model ontology = RDFDataMgr.loadModel("./resource/mondial-meta.n3");
		OntModel ontM = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM, ontology); //Muss gewrappt werden, da es sonst nicht geht
		//Mit OntModelSpec.OWL_DL_MEM_TRANS_INF wird automatisch transitive class-hierarchy inference betrieben -> bei Country wird so Area als superclass herausgefunden
		
		//ontM.read(arg0) To read other ontologies into the model
		//ontM.getBaseModel() To filter out the base model
		String resName = "Country";
		OntResource res = ontM.getOntResource("http://www.semwebtech.org/mondial/10/meta#" + resName);
		for(StmtIterator props = res.listProperties(); props.hasNext(); ){
			Statement stmnt = props.next();
			System.out.println(resName + " property: " + stmnt.getPredicate() + " value: " + stmnt.getObject().toString());
		}
		OntClass cls = res.as(OntClass.class);
		//listSubClasses/SuperClasses/etc also available with boolean argument for only direct inferred relationships
		for(ExtendedIterator<OntClass> subs = cls.listSubClasses(); subs.hasNext(); ){
			OntClass subClass = subs.next();
			System.out.println(subClass + " is a subclass of " + resName);
		}
		for(ExtendedIterator<OntClass> supers = cls.listSuperClasses(); supers.hasNext(); ){
			OntClass superClass = supers.next();
			System.out.println(superClass + " is a superclass of " + resName);
		}
		
		//Augmenting existing and creating new Classes,Properties,Relationships
		
		//res.addSameAs(resource);
		//res.listSameAs();
		//res.getCardinality(property);
		
		//ObjectProperty newProp = ontM.createDatatypeProperty(URI);
		//newProp.addDomain(OntClass);
		//newProp.addRange(XSD.dateTime);
		
		//More complex class expression
		
		OntModel animalModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM);
		String catPrefix = "http://www.semwebtech.org/cats/";
		
		OntClass catClass = animalModel.createClass(catPrefix + "meta#" + "Cat");
		OntClass activityClass = animalModel.createClass(catPrefix + "meta#" + "Activity");
		ObjectProperty lovesProp = animalModel.createObjectProperty(catPrefix + "meta#" + "loves");
		
		Individual run = activityClass.createIndividual(catPrefix + "running");
		
		
		SomeValuesFromRestriction lovesACat = animalModel.createSomeValuesFromRestriction(null, lovesProp, catClass);
		HasValueRestriction lovesRunning = animalModel.createHasValueRestriction(null, lovesProp, run);
		
		UnionClass runningCatLoverClass = animalModel.createUnionClass(catPrefix + "meta#runningCatLover", animalModel.createList(new RDFNode[] {lovesACat, lovesRunning}));
		
		//animalModel.write(System.out,"Turtle");
		
		//System.out.println("AllValuesFrom class: " + avf.getAllValuesFrom().getURI() + " on property: " + avf.getOnProperty().getURI());
		
		//Iterator<Restriction> isniceProp.listReferringRestrictions();
		
		/*ExtendedIterator<Restriction> i = ontM.listRestrictions();
		while (i.hasNext()) {
		    Restriction restriction = i.next();
		    if(restriction.isAllValuesFromRestriction()){
		    	
		    }
		    if(restriction.isCardinalityRestriction()){
		    	
		    }
		    etc..
		}*/
		
		//Enumerated classes
		//Defining a class by stating the individuals the class contains
		String dogPrefix = "http://www.semwebtech.org/dogs/";
		String humanPrefix = "http://www.semwebtech.org/humans/";
		OntClass humanClass = animalModel.createClass(humanPrefix + "meta#Human");
		EnumeratedClass dogLoverClass = animalModel.createEnumeratedClass(dogPrefix + "meta#dogLover", null);
		dogLoverClass.addOneOf(humanClass.createIndividual(humanPrefix + "Basti"));
		dogLoverClass.addOneOf(humanClass.createIndividual(humanPrefix + "Franzi"));
		dogLoverClass.addOneOf(humanClass.createIndividual(humanPrefix + "Martin"));
		
		animalModel.write(System.out,"Turtle");
		
	}
}
