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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import ua.be.wee.model.nodes.Activity;
import ua.be.wee.model.nodes.Artifact;
import ua.be.wee.model.nodes.AutomatedActivity;
import ua.be.wee.model.nodes.FinalNode;
import ua.be.wee.model.nodes.ForkJoinNode;
import ua.be.wee.model.nodes.InitialNode;
import ua.be.wee.model.nodes.InvokingActivity;
import ua.be.wee.model.nodes.Node;
import ua.be.wee.model.nodes.ports.ControlInputPort;
import ua.be.wee.model.nodes.ports.ControlOutputPort;
import ua.be.wee.model.nodes.ports.DataInputPort;
import ua.be.wee.model.nodes.ports.DataOutputPort;
import ua.be.wee.model.pm.PM;

@Repository
public class NodeRespository {

	@Autowired
	private PMRepository pmRepo;
	
	//TODO Create getPMbyActivityIRI 

	public List<Node> getNodes(String pmIri) throws Exception {
		List<Node> nodes = new ArrayList<Node>();

		PM pm = pmRepo.getPM(pmIri);

		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
				+ "PREFIX ob: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>\n"
				+ "SELECT ?node ?nodetype WHERE {\n" 
				+ "  ?pm a pm:Model ;\n" 
				+ "  	ob:hasName ?pmName ;\n"
				+ "  	owl:sameAs <" + pm.getIri() + "> ;\n" 
				+ "  	ob:hasObject ?node .\n" 
				+ "  ?node a ?nodetype .\n"
				+ "  FILTER (?nodetype in (pm:Initial, pm:Activity, pm:AutomatedActivity, pm:InvokingActivity, pm:Final, pm:ForkJoin, pm:Artifact)) .      \n"
				+ "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();

			RDFNode iri = soln.get("?node");
			RDFNode type = soln.get("?nodetype");
			Node node = createNode(iri, type);
			nodes.add(node);
		}
		pm.setNodes(nodes);
		updateArtifactOfActivities(pm);
		updateControlNodesReferences(nodes);

		return nodes;

	}
	
	public ControlInputPort getControlInputPort(String iri) throws Exception {
	    String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
	                 + "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
	                 + "SELECT ?name ?activity WHERE {\n"
	                 + "  ?port owl:sameAs <" + iri + "> ;\n"
	                 + "        a pm:CtrlInputPort ;\n"
	                 + "        pm:hasName ?name ;\n"
	                 + "        pm:ofActivity ?activity .\n"
	                 + "}";
	    ResultSet rs = FusekiWrapper.getInstance().execQuery(query);
	    if (rs.hasNext()) {
	        QuerySolution sol = rs.next();
	        ControlInputPort port = new ControlInputPort();
	        port.setIri(iri);
	        port.setName(sol.get("?name").toString());

	        // Set activity
	        String activityIri = sol.get("?activity").toString();
	        Activity act = getGenericActivityByIri(activityIri);

	        port.setAct(act);

	        return port;
	    } else {
	        throw new RuntimeException("ControlInputPort not found for IRI: " + iri);
	    }
	}
	
	public Activity getGenericActivityByIri(String activityIri) throws Exception {
	    String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
	            + "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
	            + "SELECT ?type WHERE {\n"
	            + "  <" + activityIri + "> a ?type .\n"
	            + "}";

	    ResultSet rs = FusekiWrapper.getInstance().execQuery(query);

	    List<String> types = new ArrayList<>();
	    while (rs.hasNext()) {
	        QuerySolution sol = rs.next();
	        types.add(sol.get("?type").toString());
	    }

	    // Verificar do mais específico para o mais genérico
	    if (types.stream().anyMatch(t -> t.endsWith("InvokingActivity"))) {
	        return (Activity) createInvokingActivity(activityIri);
	    } else if (types.stream().anyMatch(t -> t.endsWith("AutomatedActivity"))) {
	        return (Activity) createAutomatedActivity(activityIri);
	    } else if (types.stream().anyMatch(t -> t.endsWith("Activity"))) {
	        return getActivityByIri(activityIri);
	    } else {
	        throw new RuntimeException("Unsupported node type for activity: " + types);
	    }
	}



	public ControlOutputPort getControlOutputPort(String iri) throws Exception {
	    String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
	                 + "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
	                 + "SELECT ?name ?activity WHERE {\n"
	                 + "  ?port owl:sameAs <" + iri + "> ;\n"
	                 + "        a pm:CtrlOutputPort ;\n"
	                 + "        pm:hasName ?name ;\n"
	                 + "        pm:ofActivity ?activity .\n"
	                 + "}";
	    ResultSet rs = FusekiWrapper.getInstance().execQuery(query);
	    if (rs.hasNext()) {
	        QuerySolution sol = rs.next();
	        ControlOutputPort port = new ControlOutputPort();
	        port.setIri(iri);
	        port.setName(sol.get("?name").toString());

	        // Set activity
	        String activityIri = sol.get("?activity").toString();
	        Activity act = getGenericActivityByIri(activityIri);

	        port.setActivity(act);

	        return port;
	    } else {
	        throw new RuntimeException("ControlOutputPort not found for IRI: " + iri);
	    }
	}


	private void updateControlNodesReferences(List<Node> nodes) {

		for (Node node : nodes) {
			if (node instanceof InitialNode) {
				updateInitialNode(nodes, (InitialNode) node);
			} else if (node instanceof FinalNode) {
				updateFinalNode(nodes, (FinalNode) node);
			} else if (node instanceof ForkJoinNode) {
				updateForkJoinNode(nodes, (ForkJoinNode) node);
			}
		}

	}

	private void updateForkJoinNode(List<Node> nodes, ForkJoinNode node) {
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" 
				+ "SELECT ?next ?nodetype ?act\n"
				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" 
				+ "  ?node pm:ctrlFrom ?next.\n"
				+ "  ?next a ?nodetype ;\n"
				+ "	FILTER (?nodetype in (pm:CtrlOutputPort,pm:ForkJoin, pm:Initial)) \n" 
				+ "	OPTIONAL {\n"
				+ "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		List<Node> list = new ArrayList<Node>();
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode type = soln.get("?nodetype");
			String nextIri;
			if (type.toString().equals(Node.CTRL_OUTPUT_PORT_IRI)) {
				nextIri = soln.get("?act").toString();
			} else {
				nextIri = soln.get("?next").toString();
			}
			for (Node node2 : nodes) {
				if (node2.getIri().equals(nextIri)) {
					list.add(node2);
				}
			}
		}
		node.setPrevious(list);

		query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?next ?nodetype ?act\n"
				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" + "  ?node pm:ctrlTo ?next.\n"
				+ "  ?next a ?nodetype ;\n"
				+ "	FILTER (?nodetype in (pm:CtrlInputPort, pm:Initial, pm:Final,pm:ForkJoin)) \n"
				+ "	OPTIONAL {\n" + "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
		list.clear();
		results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode type = soln.get("?nodetype");
			String nextIri;
			if (type.toString().equals(Node.CTRL_INPUT_PORT_IRI)) {
				nextIri = soln.get("?act").toString();
			} else {
				nextIri = soln.get("?next").toString();
			}
			for (Node node2 : nodes) {
				if (node2.getIri().equals(nextIri)) {
					list.add(node2);
				}
			}
		}
		node.setNextNodes(list);

	}

	private void updateFinalNode(List<Node> nodes, FinalNode node) {
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?next ?nodetype ?act\n"
				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" + "  ?node pm:ctrlFrom ?next.\n"
				+ "  ?next a ?nodetype ;\n"
				+ "	FILTER (?nodetype in (pm:CtrlOutputPort, pm:Initial, pm:Final,pm:ForkJoin)) \n"
				+ "	OPTIONAL {\n" + "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
		List<Node> list = new ArrayList<Node>();
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode type = soln.get("?nodetype");
			String nextIri;
			if (type.toString().equals(Node.CTRL_OUTPUT_PORT_IRI)) {
				nextIri = soln.get("?act").toString();
			} else {
				nextIri = soln.get("?next").toString();
			}
			for (Node node2 : nodes) {
				if (node2.getIri().equals(nextIri)) {
					list.add(node2);
				}
			}
		}
		node.setPrevious(list);
	}

	private void updateInitialNode(List<Node> nodes, InitialNode node) {
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?next ?nodetype ?act\n"
				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" + "  ?node pm:ctrlTo ?next.\n"
				+ "  ?next a ?nodetype ;\n" + "	FILTER (?nodetype in (pm:CtrlInputPort, pm:Final,pm:ForkJoin)) \n"
				+ "	OPTIONAL {\n" + "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode type = soln.get("?nodetype");
			String nextIri;
			if (type.toString().equals(Node.CTRL_INPUT_PORT_IRI)) {
				nextIri = soln.get("?act").toString();
			} else {
				nextIri = soln.get("?next").toString();
			}
			for (Node node2 : nodes) {
				if (node2.getIri().equals(nextIri)) {
					node.setNext(node2);
					break;
				}
			}
		}
	}

	private void updateArtifactOfActivities(PM pm) throws Exception {
		for (Node node : pm.getNodes()) {
			if (node instanceof Activity) {
				Activity act = (Activity) node;
				for (DataInputPort inport : act.getDatalInPorts()) {
					String query = "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
							+ "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n" + "SELECT  ?inport ?art\n" + "WHERE { \n"
							+ "  ?inport owl:sameAs <" + inport.getIri() + "> .\n" + "  ?inport pm:dataFrom ?art .\n"
							+ "}";
					ResultSet results = FusekiWrapper.getInstance().execQuery(query);
					if (results.hasNext()) {
						QuerySolution soln = results.nextSolution();

						RDFNode art = soln.get("?art");
						if (art != null && !art.toString().isEmpty()) {
							Artifact artifact = (Artifact) pm.getNode(art.toString());
							act.addInputArtifact(artifact);
							inport.setArtifact(artifact);
						}
					} else {
						throw new Exception("Query did not return any data: \n" + query );
					}
					
				}

				for (DataOutputPort outport : act.getDataOutPorts()) {
					String query = "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
							+ "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n" + "SELECT  ?outport ?art\n"
							+ "WHERE { \n" + "  ?outport owl:sameAs <" + outport.getIri() + "> .\n"
							+ "  ?outport pm:dataTo ?art .\n" + "}";
					ResultSet results = FusekiWrapper.getInstance().execQuery(query);
					if (results.hasNext()) {
						QuerySolution soln = results.nextSolution();
	
						RDFNode art = soln.get("?art");
						if (art != null && !art.toString().isEmpty()) {
							Artifact artifact = (Artifact) pm.getNode(art.toString());
							act.addOutputArtifact(artifact);
							outport.setArtifact(artifact);
						}
					} else {
						throw new Exception("Query did not return any data: \n" + query );
					}
				}
			}
		}
	}

	private Node createNode(RDFNode iri, RDFNode type) throws Exception {
		Node node = null;
		switch (type.toString()) {
		case Node.ACTIVITY_IRI:
			node = createActivity(iri.toString());
			break;
		case Node.AUTOMATED_ACTIVITY_IRI:
			node = createAutomatedActivity(iri.toString());
			break;
		case Node.INVOKING_ACTIVITY_IRI:
			node = createInvokingActivity(iri.toString());
			break;
		case Node.INITIAL_IRI:
			node = new InitialNode();
			break;
		case Node.FINAL_IRI:
			node = new FinalNode();
			break;
		case Node.FORKJOIN_IRI:
			// addlogic for fork join
			node = new ForkJoinNode();
			break;
		case Node.ARTIFACT_IRI:
			// addlogic for fork join
			node = createArtifact(iri.toString());
			break;
		}
		node.setIri(iri.toString());
		return node;
	}

	

	private Node createArtifact(String iri) throws Exception {
		Artifact art = new Artifact();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
				+ "PREFIX base: <http://ua.be/sdo2l/vocabulary/base/base#>\n" + "SELECT ?art ?name ?typename WHERE {\n"
				+ "	?art owl:sameAs <" + iri + "> .\n" + " ?art	pm:hasName ?name .\n"
				+ " ?art  	pm:hasType ?type .\n" + " ?type base:hasGUID ?typename .\n" + "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		if (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode name = soln.get("?name");
			RDFNode type = soln.get("?typename");
			art.setIri(iri);
			art.setName(name.toString());
			art.setType(type.toString());
			return art;
		} else {
			throw new Exception("Query did not return any data: \n" + query );
		}
		
	}

	private Node createActivity(String iri) throws Exception {
		Activity act = new Activity();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX ftg: <http://ua.be/sdo2l/vocabulary/formalisms/ftg#>\n"
				+ "PREFIX base: <http://ua.be/sdo2l/vocabulary/base/base#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?act ?name ?type WHERE {\n"
				+ "	?act owl:sameAs <" + iri + ">;\n" + "   		pm:isTransformation ?t ;  \n"
				+ "   		pm:hasName ?name .  \n" + " ?t base:hasGUID ?type .  \n" + "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		if (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode name = soln.get("?name");
			RDFNode type = soln.get("?type");
			act.setIri(iri);
			act.setName(name.toString());
			act.setTransformationName(type.toString());

			act.setCtrlInPorts(getCtrlInPorts(act));
			act.setCtrlOutPorts(getCtrlOutPorts(act));
			act.setDatalInPorts(getDataInPorts(act));
			act.setDataOutPorts(getDataOutPorts(act));

			return act;

		} else {
			throw new Exception("Query did not return any data: \n" + query );
		}
	}
	
	private Node createAutomatedActivity(String iri) throws Exception {
		AutomatedActivity act = new AutomatedActivity();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX ftg: <http://ua.be/sdo2l/vocabulary/formalisms/ftg#>\n"
				+ "PREFIX base: <http://ua.be/sdo2l/vocabulary/base/base#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" 
				+ "SELECT ?act ?name ?type ?timeout ?endpoint WHERE {\n"
				+ "	?act owl:sameAs <" + iri + ">;\n" 
				+ "   		pm:isTransformation ?t ;  \n"
				+ "   		pm:hasName ?name ;  \n" 
				+ "   		pm:hasTimeout ?timeout .  \n"
				+ " ?t base:hasGUID ?type ;  \n" 
				+ "   		ftg:hasEndpoint ?endpoint .  \n"
				+ "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		if (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode name = soln.get("?name");
			RDFNode type = soln.get("?type");
			RDFNode timeout = soln.get("?timeout");
			RDFNode endpoint = soln.get("?endpoint");
			
			act.setIri(iri);
			act.setName(name.toString());
			act.setTransformationName(type.toString());
			act.setTimeout(timeout.asLiteral().getLong());
			act.setEndpoint(endpoint.toString());

			act.setCtrlInPorts(getCtrlInPorts(act));
			act.setCtrlOutPorts(getCtrlOutPorts(act));
			act.setDatalInPorts(getDataInPorts(act));
			act.setDataOutPorts(getDataOutPorts(act));

			return act;

		} else {
			throw new Exception("Query did not return any data: \n" + query );
		}
	}
	
	private Node createInvokingActivity(String iri) throws Exception {
	    InvokingActivity act = new InvokingActivity();
	    
	    String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
	            + "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
	            + "PREFIX base: <http://ua.be/sdo2l/vocabulary/base/base#>\n"
	            + "SELECT ?act ?name ?invokedModel WHERE {\n"
	            + "  ?act owl:sameAs <" + iri + ">;\n"
	            + "       pm:invokesPM ?invokedModel ;\n"  
	            + "       pm:hasName ?name .\n"
	            + "}"
	            ;

	    ResultSet results = FusekiWrapper.getInstance().execQuery(query);
	    if (results.hasNext()) {
	        QuerySolution soln = results.nextSolution();
	        
	        RDFNode name = soln.get("?name");
			RDFNode invokedModel = soln.get("?invokedModel");
			
	        
	        act.setIri(iri);
	        act.setName(name.toString());
	        act.setInvokePM(invokedModel.toString());

	        act.setCtrlInPorts(getCtrlInPorts(act));
	        
	        act.setCtrlOutPorts(getCtrlOutPorts(act));
	        act.setDatalInPorts(getDataInPorts(act));
	        act.setDataOutPorts(getDataOutPorts(act));

	        return act;
	    } else {
	        throw new Exception("Query did not return any data: \n" + query);
	    }
	}


	private List<DataOutputPort> getDataOutPorts(Activity act) {
		List<DataOutputPort> list = new ArrayList<DataOutputPort>();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?port ?pname WHERE {\n"
				+ "	?act owl:sameAs <" + act.getIri() + ">;\n" + "   		pm:hasPort ?port .\n"
				+ "  	?port a pm:DataOutputPort .\n" + "  	?port pm:hasName ?pname .\n" + "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode iri = soln.get("?port");
			RDFNode name = soln.get("?pname");
			DataOutputPort cout = new DataOutputPort();
			cout.setIri(iri.toString());
			cout.setName(name.toString());
			cout.setActivity(act);
			list.add(cout);
		}
		return list;
	}

	private List<DataInputPort> getDataInPorts(Activity act) {
		List<DataInputPort> list = new ArrayList<DataInputPort>();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?port ?pname WHERE {\n"
				+ "	?act owl:sameAs <" + act.getIri() + ">;\n" + "   		pm:hasPort ?port .\n"
				+ "  	?port a pm:DataInputPort .\n" + "  	?port pm:hasName ?pname .\n" + "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode iri = soln.get("?port");
			RDFNode name = soln.get("?pname");
			DataInputPort cout = new DataInputPort();
			cout.setIri(iri.toString());
			cout.setName(name.toString());
			cout.setActivity(act);
			list.add(cout);
		}
		return list;
	}

	private List<ControlOutputPort> getCtrlOutPorts(Activity act) {
		List<ControlOutputPort> list = new ArrayList<ControlOutputPort>();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?port ?pname WHERE {\n"
				+ "	?act owl:sameAs <" + act.getIri() + ">;\n" + "   		pm:hasPort ?port .\n"
				+ "  	?port a pm:CtrlOutputPort .\n" + "  	?port pm:hasName ?pname .\n" + "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode iri = soln.get("?port");
			RDFNode name = soln.get("?pname");
			ControlOutputPort cout = new ControlOutputPort();
			cout.setIri(iri.toString());
			cout.setName(name.toString());
			cout.setActivity(act);
			list.add(cout);
		}
		return list;
	}

	private List<ControlInputPort> getCtrlInPorts(Activity act) {
		List<ControlInputPort> list = new ArrayList<ControlInputPort>();
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?port ?pname WHERE {\n"
				+ "	?act owl:sameAs <" + act.getIri() + ">;\n" + "   		pm:hasPort ?port .\n"
				+ "  	?port a pm:CtrlInputPort .\n" + "  	?port pm:hasName ?pname .\n" + "}";
		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
		while (results.hasNext()) {
			QuerySolution soln = results.nextSolution();
			RDFNode iri = soln.get("?port");
			RDFNode name = soln.get("?pname");
			ControlInputPort cout = new ControlInputPort();
			cout.setIri(iri.toString());
			cout.setName(name.toString());
			cout.setAct(act);
			list.add(cout);
		}
		return list;
	}
	

	public Activity getActivityByIri(String activityIri) {
		// return PM IRI of InvokedAct
	    String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
	            + "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
	            + "SELECT ?act ?name ?t WHERE {\n"
	            + "  ?act owl:sameAs <" + activityIri + ">;\n"
	            + "       pm:hasName ?name ;\n"
	            + "       pm:isTransformation ?t .\n"
	           // temporary + "  ?t pm:hasType ?type .\n"
	            + "}";

	    ResultSet results = FusekiWrapper.getInstance().execQuery(query);

	    if (results.hasNext()) {
	        QuerySolution soln = results.nextSolution();
	        RDFNode name = soln.get("?name");
	       // RDFNode type = soln.get("?type");

	        Activity act = new Activity();
	        act.setIri(activityIri);
	        act.setName(name.toString());
	       // act.setTransformationName(type.toString());

	        // Define as portas para a atividade
	        act.setCtrlInPorts(getCtrlInPorts(act));
	        act.setCtrlOutPorts(getCtrlOutPorts(act));
	        act.setDatalInPorts(getDataInPorts(act));
	        act.setDataOutPorts(getDataOutPorts(act));

	        return act;
	    } else {
	        throw new RuntimeException("No activity found with IRI:" + activityIri);
	    }
	}

	public String getPMIRIByInvokedActivity(String activityIri) {
	    // query SPARQL to get PM IRI associated to InvokedActivity
	    String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
	            + "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
	            + "SELECT ?pmIri WHERE {\n"
	            + "  ?act owl:sameAs <" + activityIri + ">;\n"
	            + "       a pm:InvokingActivity  ;\n"
	            + "       pm:invokesPM ?pmIri .\n"
	            + "}";

	    ResultSet results = FusekiWrapper.getInstance().execQuery(query);

	    if (results.hasNext()) {
	        QuerySolution soln = results.nextSolution();
	        RDFNode pmIriNode = soln.get("?pmIri");

	        if (pmIriNode != null) {
	            return pmIriNode.toString(); 
	        }
	    }

	    // if we don't find the PM, throw a exception
	    throw new RuntimeException("No PM IRI found for InvokedActivity with IRI: " + activityIri);
	}
	
	public String getActivityIRIByInvokedActivity(String invokedActivityIri) {
	    String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
	            + "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
	            + "SELECT ?activityIri WHERE {\n"
	            + "  <" + invokedActivityIri + "> pm:hasPort ?inputPort .\n" // Get the input port of the InvokingActivity
	            + "  ?inputPort a pm:CtrlInputPort ;\n"                    // Ensure it’s a control input port
	            + "            pm:ctrlFrom ?outputPort .\n"              // Trace back to the output port
	            + "  ?outputPort a pm:CtrlOutputPort ;\n"                 // Ensure it’s a control output port
	            + "             pm:ofActivity ?activityIri .\n"          // Get the activity owning the output port
	            + "  ?activityIri a pm:Activity .\n"                     // Confirm it’s an Activity
	            + "} LIMIT 1";

	    ResultSet results = FusekiWrapper.getInstance().execQuery(query);
	    if (results.hasNext()) {
	        QuerySolution soln = results.nextSolution();
	        RDFNode activityIriNode = soln.get("?activityIri");
	        if (activityIriNode != null) {
	            System.out.println("Found previous activity: " + activityIriNode.toString());
	            return activityIriNode.toString();
	        }
	    }
	    System.out.println("WARNING: No previous activity found for InvokingActivity: " + invokedActivityIri);
	    return null;
	}
	
	public String getPMbyActivityIRI(String activityIri) {
		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
                + "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
                + "SELECT ?pmIri WHERE {\n"
                + "  ?act owl:sameAs <" + activityIri + ">;\n"
                + "       a pm:InvokingActivity ;\n"
                + "       pm:invokesPM ?pmIri .\n"
                + "}";

        ResultSet results = FusekiWrapper.getInstance().execQuery(query);

        if (results.hasNext()) {
            QuerySolution soln = results.nextSolution();
            RDFNode pmIriNode = soln.get("?pmIri");

            if (pmIriNode != null) {
                return pmIriNode.toString();
            }
        }

        throw new RuntimeException("No PM IRI found for Activity with IRI: " + activityIri);
    }


	public String getParentTraceIri(String traceIri) {
		String query = "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n" +
					   "SELECT ?parentTrace WHERE {\n" +
					   "  <" + traceIri + "> tr:invokedByTrace ?parentTrace .\n" +
					   "}";
		ResultSet rs = FusekiWrapper.getInstance().execQuery(query);
		if (rs.hasNext()) {
			return rs.nextSolution().getResource("?parentTrace").getURI();
		}
		return null;
	}
	

}
