package org.wontovlab.tool;

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

import org.wontovlab.model.Material;
import org.wontovlab.model.Property;
import org.wontovlab.model.Task;

import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntDocumentManager;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.ontology.OntProperty;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.vocabulary.RDFS;

class OntologyMethodsBean {

	//singleton
	private static OntologyMethodsBean instance;
	public static OntologyMethodsBean getInstance() {

		if (instance == null)
			return null;
		
		return instance;
	}
	
	public static OntologyMethodsBean getInstance(boolean isOWLFileSavedLocally, String DomainOntologyOwlPath, String DomainOntologyDocURI, String ApplicationOntologyOwlPath, String ApplicationOntologyDocURI) {
		if (instance == null)
			instance = new OntologyMethodsBean(isOWLFileSavedLocally, DomainOntologyOwlPath, DomainOntologyDocURI, ApplicationOntologyOwlPath, ApplicationOntologyDocURI);
		
		return instance;
	}

	
	private static OntModel doOntModel; //Domain Ontology - Ontology Model
	private static String doNs; //Domain Ontology - Name Space
	private static String doDocURI; //Domain Ontology - Document URI
	
	private static OntModel aoOntModel; //Application Ontology - Ontology Model
	private static String aoNs; //Application Ontology - Name Space
	private static String aoDocURI; //Application Ontology - Document URI


	
	
	private OntologyMethodsBean (boolean isOWLFileSavedLocally, String DomainOntologyOwlPath, String DomainOntologyDocURI) {
		this.doDocURI = DomainOntologyDocURI;
		doNs = DomainOntologyDocURI + "#";
		OntDocumentManager documentManager = new OntDocumentManager();
		if (isOWLFileSavedLocally)
			documentManager.addAltEntry(DomainOntologyDocURI, "file:" + DomainOntologyOwlPath);
		else
			documentManager.addAltEntry(DomainOntologyDocURI, DomainOntologyOwlPath);
		
		//reading ontologies
		OntModelSpec modelSpecification = new OntModelSpec(OntModelSpec.OWL_MEM_MICRO_RULE_INF);
		modelSpecification.setDocumentManager(documentManager);
		doOntModel = ModelFactory.createOntologyModel(modelSpecification);
		doOntModel.read(DomainOntologyDocURI);
	}
	
	private OntologyMethodsBean (boolean isOWLFileSavedLocally, String DomainOntologyOwlPath, String DomainOntologyDocURI, String ApplicationOntologyOwlPath, String ApplicationOntologyDocURI) {
		////////////////////////////Domain Ontology
		this.doDocURI = DomainOntologyDocURI;
		doNs = DomainOntologyDocURI + "#";
		OntDocumentManager DoDocumentManager = new OntDocumentManager();
		if (isOWLFileSavedLocally)
			DoDocumentManager.addAltEntry(DomainOntologyDocURI, "file:" + DomainOntologyOwlPath);
		else
			DoDocumentManager.addAltEntry(DomainOntologyDocURI, DomainOntologyOwlPath);
		
		//reading ontologies
		OntModelSpec DoModelSpecification = new OntModelSpec(OntModelSpec.OWL_MEM_MICRO_RULE_INF);
		DoModelSpecification.setDocumentManager(DoDocumentManager);
		doOntModel = ModelFactory.createOntologyModel(DoModelSpecification);
		doOntModel.read(DomainOntologyDocURI);
		
		////////////////////////////Application Ontology
		this.aoDocURI = ApplicationOntologyDocURI;
		aoNs = ApplicationOntologyDocURI + "#";
		OntDocumentManager AoDocumentManager = new OntDocumentManager();
		
		if (isOWLFileSavedLocally)
			AoDocumentManager.addAltEntry(ApplicationOntologyDocURI, "file:" + ApplicationOntologyOwlPath);
		else
			AoDocumentManager.addAltEntry(ApplicationOntologyDocURI, ApplicationOntologyOwlPath);
		
		//reading ontologies
		OntModelSpec AoModelSpecification = new OntModelSpec(OntModelSpec.OWL_MEM_MICRO_RULE_INF);
		AoModelSpecification.setDocumentManager(AoDocumentManager);
		aoOntModel = ModelFactory.createOntologyModel(AoModelSpecification);
		aoOntModel.read(ApplicationOntologyDocURI);
	}

	
//////////////////////////////////////////////////////////////////////////////////////////////

	
	/*
	 * Return all materials from Domain Ontology 
	 */
	protected List<Material> doGetAllMaterials() {

		List<Material> materialList = new ArrayList<Material>();
		
		OntClass thingClass= doOntModel.getOntClass("http://www.w3.org/2002/07/owl#Thing");
		
		ExtendedIterator instances = thingClass.listInstances();
		
	      while (instances.hasNext()){
	        Individual individual = (Individual) instances.next();
	        
	        //removing classes to be showed. Just instances must be retrieved
	        if (!individual.isClass()) {
	        	//System.out.println(individual.toString());
	        	materialList.add(doGetMaterial(individual.toString()));
	        }
	        	
	      }

		
		return materialList;
	}
	
	/*
	 * This method returns a Material object from a domain ontology URI
	 */
	protected Material doGetMaterial(String domainOntologyMaterialReference) {
		Individual instance = doOntModel.getIndividual(domainOntologyMaterialReference);
		Material material = null;
		
		if (instance != null) {
			material = new Material();
			material.setOntologyReference(domainOntologyMaterialReference);
			
			material.setName(doGetMaterialLabel(domainOntologyMaterialReference));
			
			
/*			if (instance.getProperty(RDFS.label) != null)
        		material.setName(instance.getProperty(RDFS.label).getString());
        	else
        		material.setName(null);*/
			
			material.setProperties(doGetMaterialProperties(material.getOntologyReference()));
		}
		
		return material;
	}
	
	
	
	
	protected String getApplicationOntologyPropertyLabel(String applicationOntologyPropertyURI) {
		Individual instance = aoOntModel.getIndividual(applicationOntologyPropertyURI);
		
		if (instance != null) {
			
			if (instance.getProperty(RDFS.label) != null)
        		return instance.getProperty(RDFS.label).getString();
		}
		
		return null;
	}
	
	protected String getDomainOntologyPropertyLabel(String domainOntologyPropertyURI) {
		Individual instance = doOntModel.getIndividual(domainOntologyPropertyURI);
		
		if (instance != null) {
			
			if (instance.getProperty(RDFS.label) != null)
        		return instance.getProperty(RDFS.label).getString();
		}
		
		return null;
	}
	
	
	
	
	/*
	 * This method returns a Material label from a domain ontology URI
	 */
	protected String doGetMaterialLabel(String domainOntologyMaterialReference) {
		Individual instance = doOntModel.getIndividual(domainOntologyMaterialReference);
		
		if (instance != null) {
			
			if (instance.getProperty(RDFS.label) != null)
        		return instance.getProperty(RDFS.label).getString();

		}
		
		return null;
			
	}
	
	/*
	 * @param ontoInstanceReference The material's domain ontology URI
	 * This method return a Property object list (if there is) for the material passed as parameter 
	 */
	private List<Property> doGetMaterialProperties(String materialDomainOntologyReference) {
		
		//Firstly it needs to transform an instance reference in an individual to get its class
		Individual individual = doOntModel.getIndividual(materialDomainOntologyReference);
		OntClass individualClass = doOntModel.getOntClass(individual.getOntClass().toString());
		
		List<Property> dataTypePropertylistToBeReturned = null;
		
		for (ExtendedIterator iter = individualClass.listDeclaredProperties(); iter.hasNext();) {
			
			
			OntProperty ontProperty = (OntProperty)  iter.next();
			
			if (ontProperty.isDatatypeProperty()) {
				

				
				//System.out.println("Atributo: " + ontProperty.toString());
				
				//dataTypePropertylistToBeReturned.add(new PropertyRange(ontProperty.toString(), listDataTypePropertyRange(ontProperty)));

				if (ontProperty.getProperty(RDFS.label) != null) {
					
					if (dataTypePropertylistToBeReturned == null)
						dataTypePropertylistToBeReturned = new ArrayList<Property>();
					
					dataTypePropertylistToBeReturned.add(new Property(ontProperty.getProperty(RDFS.label).getString(), ontProperty.toString()));
				}
				
			}
		}
		
		return dataTypePropertylistToBeReturned;
		
	}
	
	/*
	 * @param materialApplicationOntologyReference The material's application ontology URI
	 * This method return a Property object list for the material passed as parameter 
	 */
	private List<Property> aoGetPropertiesFromMaterial(String materialApplicationOntologyReference) {
		
		Individual individual = aoOntModel.getIndividual(materialApplicationOntologyReference);
		
		List<Property> propertyList = null;
		
		for (StmtIterator iter=individual.listProperties(); iter.hasNext();) {
			Object ob = iter.nextStatement();
			
			if (((Statement) ob).getObject().isLiteral()) {
				
				if (propertyList == null)
					propertyList = new ArrayList<Property>();
				
				Literal lit = (Literal) ((Statement) ob).getObject();
				//System.out.print("Atributo instanciado: " + ((Statement) ob).getPredicate() + " = ");
				//System.out.println(lit.getValue());	
				
				Property property = new Property();
				
				property.setName(getApplicationOntologyPropertyLabel(((Statement) ob).getPredicate().toString()));
				property.setValue(lit.getValue().toString());
				property.setPropertyOntologyReference(((Statement) ob).getPredicate().toString());
				
				
				
				
				
				
				propertyList.add(property);
				
			}
			
		}
		
		return propertyList;
		
		
		
		
	}
	
	
	
	/*
	 * @ param material Material object which the user wants to get similar materials
	 * This method returns a Material object list which have materials similar to the material passed as parameter.    
	 */
	protected List<Material> doGetInstancesSimilarTo(Material material) {
		Individual instance = doOntModel.getIndividual(material.getOntologyReference());
		OntProperty ontProperty = doOntModel.getOntProperty(doNs + "similarTo");
		
		List<Material> materials = null;
		
		for (StmtIterator si = doOntModel.listStatements(instance, ontProperty, (RDFNode) null); si.hasNext();) {
			if (materials == null)
				materials = new ArrayList<Material>();
			
			Individual ind = (Individual) ((RDFNode)si.nextStatement().getObject()).as(Individual.class);

			materials.add(doGetMaterial(ind.toString()));
			
			//System.out.println(ind.toString());
		}
		
		//Remove the own instance from the list. The instance passed as parameter is always returned as similiar to 
		//itself, but it is not useful in my business rule, so it is removed from the list.
		if (materials != null)
			for (int i=0; i < materials.size(); i++) {
				if (materials.get(i).getOntologyReference().equals(material.getOntologyReference())) {
					materials.remove(i);
					break;
				}
			}
		
		return materials;
	}
	
//////////////////////////////////////////////////////////////////////////////////////////////	
	
	/*
	 * Return all tasks from Application Ontology 
	 */
	protected List<Task> aoGetAllTasks() {

		List<Task> stepList = new ArrayList<Task>();
		
		OntClass taskClass= aoOntModel.getOntClass("http://www.owl-ontologies.com/Ontology1258201595.owl#Tarefa");
		
		ExtendedIterator tasks = taskClass.listInstances();
		
	      while (tasks.hasNext()){
	        Individual individual = (Individual) tasks.next();
	        
	        //removing classes to be showed. Just instances must be retrieved
	        if (!individual.isClass()) {
	        	
	        	Task task = new Task();
	        	
	        	task.setApplicationOntologyReference(individual.toString());
	        	//System.out.print(individual.toString());
	        	
	        	if (individual.getProperty(RDFS.label) != null) {
	        		//System.out.println(" " + individual.getProperty(RDFS.label).getString());
	        		task.setDescription(individual.getProperty(RDFS.label).getString());
	        	} else {
	        		task.setDescription(null);
	        		//System.out.println("  - Descrição: NULL");
	        	}
	        
	        	//System.out.println("    --> Verificando materiais utilizados neste passo: " + task.getApplicationOntologyReference());
	        	task.setMaterials(aoGetTaskMaterials(task.getApplicationOntologyReference()));
	        	
	        	stepList.add(task);
	        }
	        	
	      }
	      return stepList;
	}
	
	/*
	 * @param task The application ontology task. It will be used to get the task's application ontology URI 
	 * This method returns a Material object list of all materials used in the task passed as parameter 
	 */
	protected List<Material> aoGetTaskMaterials(String applicationOntologyTaskURI) {
		
		//First of all, getting the materials that have no property
		Individual instance = aoOntModel.getIndividual(applicationOntologyTaskURI);
		OntProperty ontProperty = aoOntModel.getOntProperty(aoNs + "hasMaterial");
		
		List<Material> materialList = null;
		
		//As ontProperty is pointing to "hasMaterial" relationShip, it will be returned just materials that have no property
		for (StmtIterator si = aoOntModel.listStatements(instance, ontProperty, (RDFNode) null); si.hasNext();) {
			if (materialList == null)
				materialList = new ArrayList<Material>();
			
			Individual ind = (Individual) ((RDFNode)si.nextStatement().getObject()).as(Individual.class);

			materialList.add(doGetMaterial(ind.toString()));
			
			//System.out.println("=-=-=-=-=-=> Material sem propriedade:" + ind.toString());
			
		}
		//First of all, getting the materials that have no property

		
		
		
		
		OntProperty hasMaterialWithPropertyObjectProperty = aoOntModel.getOntProperty(aoNs + "hasMaterialWithProperty");
		for (StmtIterator si = aoOntModel.listStatements(instance, hasMaterialWithPropertyObjectProperty, (RDFNode) null); si.hasNext();) {
			Individual ind = (Individual) ((RDFNode)si.nextStatement().getObject()).as(Individual.class);
			//System.out.println("=-=-=-=-=-=-=-=-=> Material com propriedade: " + ind.toString());
			
			Material material = new Material();
			
			Individual materialWithPropertyInstance = aoOntModel.getIndividual(ind.toString());
			for (StmtIterator si2 = aoOntModel.listStatements(materialWithPropertyInstance, ontProperty, (RDFNode) null); si2.hasNext();) {
				Individual materialWithProperty = (Individual) ((RDFNode)si2.nextStatement().getObject()).as(Individual.class);
				//System.out.println("-> Material with property: " + materialWithProperty.toString());
				
				material.setOntologyReference(materialWithProperty.toString());
				
				material.setName(doGetMaterialLabel(materialWithProperty.toString()));
				
				material.setProperties(aoGetPropertiesFromMaterial(ind.toString()));
				
				materialList.add(material);
			}
			
			//Pegar as propriedades
/*			Individual individual = aoOntModel.getIndividual(ind.toString());
			
			for (StmtIterator iter=individual.listProperties(); iter.hasNext();) {
				Object ob = iter.nextStatement();
				
				if (((Statement) ob).getObject().isLiteral()) {
					Literal lit = (Literal) ((Statement) ob).getObject();
					System.out.print("Atributo instanciado: " + ((Statement) ob).getPredicate() + " = ");
					System.out.println(lit.getValue());	
					
					material.setProperties(doGetMaterialProperties(material.getOntologyReference()));
					
				}
				
			}*/

			
	

			
		}
		
		
		return materialList;
		
	}
	
	
	
	private String aoGetTaskLabel(String applicationOntologyTaskURI) {
		Individual instance = aoOntModel.getIndividual(applicationOntologyTaskURI);
		
		if (instance != null && instance.getProperty(RDFS.label) != null)
        	return instance.getProperty(RDFS.label).getString();

		
		return null;
	}

	protected Task aoGetTask(String applicationOntologyTaskURI) {
		Individual instance = aoOntModel.getIndividual(applicationOntologyTaskURI);
		
		Task task = null;
		
		if (instance != null) {
			task = new Task();
			task.setApplicationOntologyReference(applicationOntologyTaskURI);
			task.setDescription(aoGetTaskLabel(applicationOntologyTaskURI));
			task.setMaterials(aoGetTaskMaterials(applicationOntologyTaskURI));
		}
		
		return task;
		
	}

	/*
	 * @param applicationOntologyTaskURI The application ontology step URI
	 * This method get the param applicationOntologyTaskURI and verify over the application ontology if there are alternative
	 * steps for this step. If there is, it will return a list of Step.
	 */
	protected List<Task> aoGetTasksSimilarTo(String applicationOntologyTaskReference) {
		

		Individual instance = aoOntModel.getIndividual(applicationOntologyTaskReference);
		OntClass individualClass = instance.getOntClass();
		
		List<Task> tasks = new ArrayList<Task>();
		for (ExtendedIterator iter = individualClass.listInstances(); iter.hasNext();) {
			Individual individualTaskURI = (Individual) iter.next(); 
			
			tasks.add(aoGetTask(individualTaskURI.getURI()));
		}
		
		
		return tasks;
	}

	
//////////////////////////////////////////////////////////////////////////////////////////////	

	
	
	/*
	
	 * Return all allowed values (Range) to a specific dataType property.
	 
	protected List<String> listDataTypePropertyRange(OntProperty dataTypeProperty) {
	
		List<String> RangeListToReturn = null;
		
		for (ExtendedIterator iterator= dataTypeProperty.listRange(); iterator.hasNext();) {

			
			OntResource ontoResource = (OntResource) iterator.next();

			if (ontoResource.isDataRange()) {
				
				if (RangeListToReturn == null)
					RangeListToReturn = new ArrayList<String>();
				
				DataRange dataRange = ontoResource.asDataRange();
				//System.out.println("    Valores Permitidos: ");
				for (ExtendedIterator rangeIterator = dataRange.listOneOf(); rangeIterator.hasNext();) {
					Literal rangeLiteral = (Literal) rangeIterator.next();
					RangeListToReturn.add(rangeLiteral.getValue().toString());
					//System.out.println("    --> " + rangeLiteral.getValue());
					
				}
			}
		}
		
		//if (RangeListToReturn == null)
			//System.out.println("    Não há restrição de valores para esta propriedade");
		
		return RangeListToReturn;
	}*/

	/*
	 * If an instance has one or more dataType object instanced, so, return them with its respective values
	 */
/*	protected List<Property> listIndividualInstancedAttributes(String ontoInstanceReference) {
		
		//Firstly it needs to transform an instance reference in an individual to get its class
		Individual individual = doOntModel.getIndividual(ontoInstanceReference);
		OntClass individualClass = doOntModel.getOntClass(individual.getOntClass().toString());
		
		List<Property> instancedAttributelistToBeReturned = null;
		
		for (StmtIterator iter=individualClass.listProperties(); iter.hasNext();) {
			Object ob = iter.nextStatement();
			if (((Statement) ob).getObject().isLiteral()) {
				
				instancedAttributelistToBeReturned = new ArrayList<Property>();
				
				Literal lit = (Literal) ((Statement) ob).getObject();
				System.out.print("Atributo instanciado: " + ((Statement) ob).getPredicate() + " = ");
				System.out.println(lit.getValue());
				instancedAttributelistToBeReturned.add(new Property(( (Statement) ob).getPredicate().toString(), lit.getValue().toString()));
			}
		}
		return instancedAttributelistToBeReturned;
	}*/
	
	

		
	
	 

	



	

}
