import org.apache.jena.datatypes.xsd.XSDDatatype;
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.query.ResultSetRewindable;
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.sparql.engine.http.QueryEngineHTTP;
import org.apache.jena.vocabulary.RDFS;

public class JenaModel {
	static String filepath = "/home/may/teaching/SemWeb/RDF/";

	public static void printStmtIter(StmtIterator iter) {
		while (iter.hasNext()) {
			Statement stmt      = iter.nextStatement();  // get next statement
			System.out.println(stmt.toString());
		}
	}
	public static void printSPO(RDFNode subject, RDFNode predicate, RDFNode 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 ... note that object.toString does not generate correct output
			System.out.print(" \"" + object.toString() + "\"");
		}
		System.out.println(" .");
	}

	public static void main(String[] args){
		// some definitions

		// create Models: family (empty) and mondial (europe)
		Model europe = RDFDataMgr.loadModel(filepath + "mondial-europe.n3");
		Model family = ModelFactory.createDefaultModel();
		family.setNsPrefix("", "foo://bla/meta#");
		// create the resource
		//   and add the properties cascading style
		Property nameP = family.createProperty("foo://bla/meta#", "name");
		Resource john = family.createResource("foo://bla/persons/john")
			    .addProperty(nameP, "John")
			    .addProperty(nameP, "Johannes", "de")
			    .addProperty(nameP, family.createLiteral("Jean", "fr"))
			    .addProperty(family.createProperty("foo://bla/meta#","birthdate"),
			    		     "1970-12-31", XSDDatatype.XSDdateTime)
			    .addProperty(family.createProperty("foo://bla/meta#","hasChild"),
	                 family.createResource().addProperty(nameP, "Alice")
	                                        .addLiteral(family.createProperty("foo://bla/meta#","age"), 10));
		Resource alice = john.getPropertyResourceValue(family.getProperty("foo://bla/meta#","hasChild"));

		System.out.println("############ write out family as Turtle and RDF/XML #####");

		family.write(System.out, "TURTLE"); //"RDF/XML-ABBREV"
		family.write(System.out, "RDF/XML-ABBREV"); //"RDF/XML-ABBREV"


		System.out.println("############ all statements of family #####");
		// write ALL TRIPLES to standard out
		StmtIterator iter = family.listStatements();
		printStmtIter(iter);

		System.out.println("############ all triples of Andorra #####");
		Resource andorra = europe.getResource("http://www.semwebtech.org/mondial/10/countries/AND/");
		StmtIterator andIter = andorra.listProperties();
		printStmtIter(andIter);

		//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
		*/

		//Querying
		//Simple

		System.out.println("############ french cities #####");
		StmtIterator fcities = europe.listStatements(
				europe.getResource("http://www.semwebtech.org/mondial/10/countries/F/"),
				europe.getProperty("http://www.semwebtech.org/mondial/10/meta#hasCity"),
				(RDFNode) null);
		printStmtIter(fcities);

		System.out.println("############ Things with name 'Monaco' #####");
		StmtIterator monacos = europe.listStatements(
				null,
				europe.getProperty("http://www.semwebtech.org/mondial/10/meta#name"),
				"Monaco");
		printStmtIter(monacos);

		System.out.println("############ things with more than 1000000 inhabitants #####");
		StmtIterator bigthings = europe.listStatements(
				new SimpleSelector(null, europe.getProperty("http://www.semwebtech.org/"
						+ "mondial/10/meta#population"), (RDFNode) null) {
					public boolean selects(Statement s){
						return s.getObject().asLiteral().getFloat() > 1000000;
					}
				}
				);
		printStmtIter(bigthings);

		System.out.println("############ RDFS.label and language tags #####");
		Resource country = europe.getResource("http://www.semwebtech.org/"
				+ "mondial/10/meta#Country");
		country.addProperty(RDFS.label, europe.createLiteral("Country", "en"));
		country.addProperty(RDFS.label, europe.createLiteral("Land", "de"));
		//Get all resources with rdfs:label
		ResIterator tagIter = europe.listResourcesWithProperty(RDFS.label);
		while(tagIter.hasNext()){
			Resource res = tagIter.nextResource();
			StmtIterator prop = res.listProperties(RDFS.label);
			while(prop.hasNext()){
				Statement stmt = prop.nextStatement();
				System.out.print(stmt.getObject().asLiteral().getValue() + " ");
				System.out.println(stmt.getObject().asLiteral().getLanguage());
			}
		}


		// ARQ
		// Command-line "sparql --query cap-hq.sparql --data mondial-europe.n3"

		// as usual
		System.out.println("############ SPARQL country names #####");
		String qs =
				"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 q = QueryFactory.create(qs);
		QueryExecution qe = QueryExecutionFactory.create(qs, europe);
		ResultSet res = qe.execSelect();
		// ResultSetFormatter.out(System.out, res, q);
		// ResultSetFormatter.outputAsXML(System.out, res);
		/* or: */
		ResultSetRewindable persistentRes = ResultSetFactory.copyResults(res); //To keep it available even after iteration and closing of the QueryExecution
		qe.close();

		int numberOfResults = ResultSetFormatter.toList(persistentRes).size();
		System.out.println("Number of Results: " + numberOfResults);
		persistentRes.reset();  // to use it again
		while (persistentRes.hasNext()) {
			QuerySolution sol = persistentRes.next();
			RDFNode n = sol.get("CN");
			// and do something with n
			System.out.println(n);
		}

		//Try-with-resource
		/*try(QueryExecution qe = QueryExecutionFactory.create(query, model2)) {
			ResultSetFormatter.out(qe.execSelect());
		}*/

		//Named Graphs (FROM NAMED <URI>)

		System.out.println("############ Named Graphs - John lives in Andorra #####");
		Dataset ds = DatasetFactory.create();
		ds.setDefaultModel(europe);
        alice.addProperty(family.createProperty("foo://bla/meta#","livesIn"), andorra);
		ds.addNamedModel("http://family", family);

		String qs1 =
				 "prefix mon: <http://www.semwebtech.org/mondial/10/meta#> " +
				 "prefix family: <foo://bla/meta#> " +
				 "SELECT ?C ?CN ?PN ?A " +
				 "WHERE { "
				 + "?C a mon:Country . ?C mon:name ?CN . "
				 + "   OPTIONAL { ?P family:livesIn ?CC }"
				 + "GRAPH <http://family> { ?P family:name ?PN ."
				 + "      OPTIONAL { ?P family:livesIn ?C; family:age ?A }}}";
		Query q1 = QueryFactory.create(qs1);

		QueryExecution qe1 = QueryExecutionFactory.create(q1, ds);
		ResultSet res1 = qe1.execSelect();
	    // do something with res1, e.g.
		ResultSetFormatter.out(System.out, res1, q1);
		qe1.close();


		System.out.println("############ Named Graphs in FROM: non-europ islands in europ seas #####");

		String qs2 =
				" prefix : <http://www.semwebtech.org/mondial/10/meta#> " +
				" CONSTRUCT { ?I ?P ?O . ?O a ?C ; :name ?ON } " +
				" FROM <file:" + filepath + "mondial.n3> " +
				" FROM NAMED <file:" + filepath + "mondial-europe.n3> " +
				" WHERE { GRAPH <file:" + filepath + "mondial-europe.n3>  " +
				"          { ?S a :Sea; :locatedIn ?C0. ?C0 a :Country . " +
				"           FILTER NOT EXISTS { ?C0 :name 'Russia' }  } " +
				"        ?I :locatedInWater ?S . " +
				"        ?I ?P ?O . FILTER (!isBlank(?O)) " +
				"        OPTIONAL { ?O a ?C ; :name ?ON } " +
				"        FILTER NOT EXISTS  " +
				"          { GRAPH <file:" + filepath + "mondial-europe.n3> { ?I a :Island }}} ";

		Query q2 = QueryFactory.create(qs2);
		QueryExecution qe2 = QueryExecutionFactory.create(q2);
		Model islands = qe2.execConstruct();
		islands.write(System.out,"Turtle");
	    // do something with res2, e.g.
		Model europeAndIslands = europe.union(islands);
		qe2.close();

		//Prepared statements
		System.out.println("############ Prepared Statements: USA ... has a non-europ Atlantic island #####");

		// reuse Query "q" (returning country names) from above:
		QuerySolutionMap initialBinding = new QuerySolutionMap();
		initialBinding.add("C", europe.getResource("http://www.semwebtech.org/mondial/10/countries/USA/"));
		QueryExecution qe3 = QueryExecutionFactory.create(q, europeAndIslands, initialBinding);
		ResultSet res3 = qe3.execSelect();
    	ResultSetFormatter.out(System.out, res3, q);
		qe3.close();


		//Remote Queries
		System.out.println("############ Remote SPARQL: all 244 country names #####");
		QueryEngineHTTP remoteQE = (QueryEngineHTTP)
				QueryExecutionFactory.sparqlService("http://www.semwebtech.org/mondial/10/sparql", q);
		//remoteQE.setSelectContentType(WebContent.contentTypeResultsXML);
		ResultSet res4 = remoteQE.execSelect();
		ResultSetFormatter.out(System.out, res4, q);
		remoteQE.close();
	}
}
