package OntologyEntityFinder;


import java.io.File;
import java.util.*;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.util.SimpleIRIMapper;

/**
 * @author Simeon Polfliet
 * (simeon.polfliet@ensimag.imag.fr)
 */
public class OntologyEntityFinder {

	/** List of used ontologies */
	private LinkedList< OntologyDescription > listOnto;
	/** Ontology currently being loaded */
	private OntologyDescription curOnto;
	/** Dictionary of the obvious associations between 
	 * a word and an entity, to avoid useless computations */
	private Hashtable<String, String> obviousRelations;
	/** Stores the relations to not recalculate them */
	private Hashtable<String, String> alreadyCalc;
	// Variable used for tests
	private double lastResult;

	/**
	 * Standard constructor
	 * @param useWN will use WordNet Similarity if true and only String similarity if false
	 */
	public OntologyEntityFinder(boolean useWN) {
		listOnto = new LinkedList< OntologyDescription >();
		obviousRelations = new Hashtable<String, String>();
		alreadyCalc = new Hashtable<String, String>();
		new StringOperation(useWN);
	}
	
	// Function used for tests
	public double getLastResult(){
		return lastResult;
	}
	
	/** 
	 * Update the set of calculated elements
	 * Can be used to take into account user input
	 * @param table The table of relations
	 */
	public void updateAlreadyCalc(Hashtable<String, String> table){
		alreadyCalc.putAll(table);
	}

	/**
	 * Add all the entities of the set to the current ontology
	 * @param setEntity
	 * @param ontology 
	 */
	private void loadEntity(Set<OWLEntity> setEntity,OWLOntology ontology ){
		OWLEntity entity;
		String entityName, url;
		Iterator<OWLEntity> it = setEntity.iterator();
		while(it.hasNext()){
			entity = it.next();
			url = curOnto.getUrl().replaceAll("#", "");
			if (entity.toStringID().startsWith(url)){
				entityName = entity.toStringID().replaceAll(url,"").replaceAll("#", "");
				if (!entityName.equals("")){
					curOnto.addEntities(entityName);
					if (entityName.contains("/")){
						obviousRelations.put(StringOperation.removeSpe(entityName), "<" + curOnto.getUrl() + entityName + ">");
					} else {
						obviousRelations.put(StringOperation.removeSpe(entityName), curOnto.getPrefix() + ":" + entityName);
					}
				}
			} 
		}
	}


	/**
	 * Add an ontology the the list of ontology by parsing an ontology description
	 * given in a RDF/XML file
	 * @param fileName path of the file with the ontology description
	 * @param url base url of the ontology
	 * @param prefix prefix tu use for this ontology
	 */
	public void loadOntology(String fileName, String url, String prefix){
		OWLOntologyManager manager = OWLManager.createOWLOntologyManager(); 
		File file = new File(fileName);
		IRI iri = IRI.create(url);
		OWLAxiom ax;
		manager.addIRIMapper(new SimpleIRIMapper(iri, IRI.create(file))); 
		OWLOntology ontology = null;
		try {
			ontology = manager.loadOntology(iri);
		} catch (OWLOntologyCreationException e) {
			e.printStackTrace();
		}

		curOnto = new OntologyDescription(url, prefix);
		listOnto.add(curOnto);

		Set<OWLAxiom> axioms = ontology.getAxioms();
		Iterator<OWLAxiom> it = axioms.iterator();

		while (it.hasNext()){
			ax = it.next();
			loadEntity(ax.getSignature(), ontology);
		}

		/* Hack to load skos annotations 
		 * because those informations are skipped by the OWL API */
		it = axioms.iterator();
		while (it.hasNext()){
			ax = it.next();
			String label = "", propName = "";
			
			if (!curOnto.getUrl().contains("skos")){
				label = StringOperation.getSubStringWithCondition(ax.toString(), "skos:prefLabel", "\"", true);
				propName = StringOperation.getSubStringWithCondition(ax.toString(), url, ">", false);
			}

			if (!label.equals("")){				
				Entity p = curOnto.getEntity(propName);
				if (p != null){					
					p.delWord(StringOperation.split(propName));
					p.addWord(StringOperation.split(label));
					obviousRelations.remove(StringOperation.removeSpe(propName));
					obviousRelations.put(StringOperation.removeSpe(label), curOnto.getPrefix() + ":" + propName);
				}
			} 
		}
		//System.out.println(curOnto);
		//System.out.println(obviousRelations);
	}

	/**
	 * Associate a string (= Table name or table attribute) with an ontology entity
	 * @param str the input string
	 * @param context the context string (= TableName + Attribute)
	 * @return the "best" corresponding entity name
	 */
	public String getEntity (String str, String context) {
		String res = "";
		double evalRes = 0.0;
		double tmp;
		LinkedList<String> splitStr = StringOperation.split(str);
		OntologyDescription onto;

		// Several tests to return association without doing long computation
		if (alreadyCalc.containsKey(str+context)){
			lastResult = 2;
			res = alreadyCalc.get(str+context);
			return res;
		}
		if (obviousRelations.containsKey(StringOperation.removeSpe(context))){
			lastResult = 1.000000000000001;
			res = obviousRelations.get(StringOperation.removeSpe(context));
			return res;
		}

		// Heuristic : usually the last word is more important than the others
		// Except in a few cases
		Iterator<String> itStr;
		String word = "";
		if (splitStr.contains("per") || splitStr.contains("of")){
			itStr = splitStr.iterator();
			while (itStr.hasNext()){
				word = word + itStr.next();			
				if (obviousRelations.containsKey(word)){
					lastResult = 1.000000000000002;
					res = obviousRelations.get(word);
				}
			}
			if (!res.equals("")){
				return res;
			}
		} 
		
		word = "";
		itStr = splitStr.descendingIterator();
		while (itStr.hasNext()){
			word = itStr.next() + word;
			if (obviousRelations.containsKey(word)){
				lastResult = 1.000000000000003;
				res = obviousRelations.get(word);
			}
		}
		if (!res.equals("")){
			return res;
		}
		
		word = "";
		itStr = splitStr.iterator();
		while (itStr.hasNext()){
			word = itStr.next() + word;
			if (obviousRelations.containsKey(word)){
				lastResult = 1.000000000000004;
				res = obviousRelations.get(word);
			}
		}
		if (!res.equals("")){
			return res;
		}

		// If no easy association was found do the similarity calculation
		Iterator<OntologyDescription> it = listOnto.descendingIterator();
		while (it.hasNext()){
			onto = it.next();
			tmp = onto.compareWord(splitStr);
			if (evalRes < tmp){
				evalRes = tmp;
				if (onto.getLastResult().contains("/")){
					res = "<" + onto.getUrl() + onto.getLastResult() + ">";
				} else {
					res = onto.getPrefix() + ":" + onto.getLastResult();
				}
			}
		}

		lastResult = evalRes;
		alreadyCalc.put(str + context, res);
		return res;
	}
	
	/**
	 * Associate a string (= Table name or table attribute) with an ontology entity
	 * @param str the input string
	 * @return the "best" corresponding entity name
	 */
	public String getEntity(String str){
		return getEntity(str,"");
	}

}
