package ua.be.wee.model.repository;

import java.util.ArrayList;
import java.util.List;

import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.RDFNode;
import org.springframework.stereotype.Repository;

import ua.be.wee.model.nodes.Node;
import ua.be.wee.model.pm.PM;
import ua.be.wee.model.pt.PT;
import ua.be.wee.model.util.Pair;

@Repository
public class PMRepository {

	public List<PM> getAllPMs() {
		String query = "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
				+ "PREFIX base: <http://ua.be/sdo2l/vocabulary/base#>\n"
				+ "PREFIX obj: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>\n"
				+ "SELECT ?pm ?pmName WHERE {\n"
				+ "  ?pm a pm:Model ;\n"
				+ "  	obj:hasName ?pmName .\n"
				+ "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		List<PM> list = new ArrayList<PM>();
		while (results.hasNext()) {
			PM pm = new PM();
			QuerySolution soln = results.nextSolution();
			RDFNode iri = soln.get("?pm");
			RDFNode name = soln.get("?pmName");
			pm.setIri(iri.toString());
			pm.setName(name.toString());
			list.add(pm);
		}
		return list;
	}

	public PM getPM(String pmiri) throws Exception {
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
				+ "PREFIX obj: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>\n"
				+ "SELECT ?pm ?pmName WHERE {\n"
				+ "  ?pm a pm:Model ;\n"
				+ "  	obj:hasName ?pmName ;\n"
				+ "  	owl:sameAs <" + pmiri + "> .\n"
				+ "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		
		if (results.hasNext()) {
			PM pm = new PM();
			QuerySolution soln = results.nextSolution();
			RDFNode iri = soln.get("?pm");
			RDFNode name = soln.get("?pmName");
			pm.setIri(iri.toString());
			pm.setName(name.toString());
			return pm;
		} else {
			throw new Exception("No data returned by query: \n" + query );
		}
		
	}

	public List<Pair<String,String>> findNextNodes(String iri, String trace) throws Exception {
		List<Pair<String,String>> list = new ArrayList<Pair<String,String>>();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
				+ "SELECT ?e ?nodetype ?act WHERE {\n"
				+ "  ?node owl:sameAs <" + iri + "> .\n"
				+ "  ?node pm:ctrlTo ?e .\n"
				+ "  ?e a ?nodetype ;\n"
				+ "  FILTER (?nodetype in (pm:CtrlInputPort, pm:Final, pm:ForkJoin)) \n"
				+ "	 OPTIONAL {\n"
				+ "		?e pm:ofActivity ?act .\n"
				+ "	 }\n"
				+ "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode type = soln.get("?nodetype");
			Pair<String,String> pair = null;
			if (type.toString().equals(Node.CTRL_INPUT_PORT_IRI)) {
				pair = new Pair<String,String>(soln.get("?e").toString(),soln.get("?act").toString());
			} else if (type.toString().equals(Node.FORKJOIN_IRI)) {
				if (isJoin(soln.get("?e").toString())) {
					if (checkJoin(soln.get("?e").toString(), trace, iri)) {
						list.addAll(findNextNodes(soln.get("?e").toString(), trace));
					}
				} else {
					list.addAll(findNextNodes(soln.get("?e").toString(), trace));
				}
			} else {
				pair = new Pair<String,String>(soln.get("?e").toString(), null);
			}
			if (pair != null) {
				list.add(pair);
			}
		}
		return list;
	}

	private boolean checkJoin(String node, String trace, String input) throws Exception {
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
				+ "SELECT ?sync WHERE {\n"
				+ "  ?sync a tr:JoinSync .\n"
				+ "  ?sync tr:nodeIRI <" + node + "> .\n"
				+ "  ?sync tr:trace <" + trace + "> .\n"
				+ "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		if (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			String sync = soln.get("?sync").toString();
			return checkInputs(sync,node,trace,input);
		} else {
			createJoinSync(node,trace,input);
			return false;
		}
	}

	private boolean checkInputs(String sync, String node, String trace, String input) throws Exception {
		List<String> list = new ArrayList<String>();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
				+ "SELECT ?inCtrl WHERE {\n"
				+ "  ?sync owl:sameAs  <" + sync + "> .\n"
				+ "  ?sync tr:nodeIRI ?node .\n"
				+ "  ?inCtrl pm:ctrlTo ?node .\n"
				+ "  FILTER NOT EXISTS { ?sync tr:input ?inCtrl } \n"
				+ "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			String inCtrl = soln.get("?inCtrl").toString();
			list.add(inCtrl);
		}
		if (list.size() == 1 && list.get(0).equals(input)) {
			insertRealizedInputInJoinSync(sync,input);
			return true; 
		} else if (list.size() == 1 && !list.get(0).equals(input)) {
			return false;
		} else if (list.size() > 1) {
			for (String inCtrl : list) {
				if (inCtrl.equals(input)) {
					insertRealizedInputInJoinSync(sync,input);
				}
			}
		} else if (list.isEmpty()) {
			return true;
		}
		return false;
		
	}

	private void insertRealizedInputInJoinSync(String sync, String input) throws Exception {
		String query = "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
				+ "INSERT DATA {\n"
				+ "GRAPH <"+ PT.TRACE_GRAPH_IRI + "> {"
				+ "	<"+ sync + "> tr:input <" + input + "> .\n"
				+ "	}"
				+ "} ";
		if (!FusekiWrapper.getInstance().updateQuery(query) ) {
			throw new Exception("Error inserting data.");
		}
	}

	private void deleteJoinSync(String sync) throws Exception {
		String query = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
				+ "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX base: <http://ua.be/sdo2l/vocabulary/base/acyclic#>\n"
				+ "DELETE { "
				+ "GRAPH <"+ PT.TRACE_GRAPH_IRI + "> {"
				+ "	?sync ?p ?v . }\n"
				+ "}"
				+ "WHERE { \n"
				+ "  ?sync a tr:JoinSync .\n"
				+ "  ?sync owl:sameAs <" + sync + "> .\n"
				+ "  ?sync ?p ?v .\n"
				+ "};\n";	
		if (!FusekiWrapper.getInstance().updateQuery(query) ) {
			throw new Exception("Error inserting data.");
		}
	}

	private void createJoinSync(String node, String trace, String input) throws Exception {
		String sync = trace + "_" + node.split("#")[1];
		String query = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
				+ "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "INSERT DATA {\n"
				+ "GRAPH <"+ PT.TRACE_GRAPH_IRI + "> {"
				+ "	<"+ sync + "> rdf:type <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#JoinSync> , owl:Thing ;\n"
				+ "    	tr:nodeIRI <" + node + "> ;\n"
				+ " 	tr:trace <" + trace + "> ;\n"
				+ " 	tr:input <" + input + "> ;\n"
				+ "     owl:sameAs  <" + sync + "> .\n"
				+ "	} \n"
				+ "}; ";
		if (!FusekiWrapper.getInstance().updateQuery(query) ) {
			throw new Exception("Error inserting data.");
		}
	}

	private boolean isJoin(String iri) {
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
				+ "SELECT ?input WHERE {\n"
				+ "  ?node owl:sameAs <" + iri + "> .\n"
				+ "  ?input pm:ctrlTo ?node .\n"
				+ "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		results.next();
		return results.hasNext();
	}

	public void checkJoinSync(String portiri, String traceiri) throws Exception {
		List<String> list = new ArrayList<String>();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
				+ "SELECT DISTINCT ?sync WHERE {\n"
				+ "  ?inCtrl owl:sameAs <" + portiri + "> .\n"
				+ "  {?node pm:ctrlTo ?inCtrl .}\n"
				+ "  UNION\n"
				+ "  {?inCtrl pm:ctrlFrom+ ?node . }\n"
				+ "  ?node a pm:ForkJoin .\n"
				+ "  ?sync tr:nodeIRI ?node .\n"
				+ "  ?sync tr:trace <" + traceiri +"> .\n"
				+ "  FILTER NOT EXISTS { \n"
				+ "  	?in pm:ctrlTo ?node . \n"
				+ "    FILTER NOT EXISTS {\n"
				+ "   		?sync tr:input ?in .\n"
				+ "    }\n"
				+ "  }\n"
				+ "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			String sync = soln.get("?sync").toString();
			list.add(sync);
		}
		
		for (String sync : list) {
			deleteJoinSync(sync);
		}
		
	}
	
}
