/*
 * Copyright (C) 1999-2006 <a href="mailto:paschke@in.tum.de">Adrian Paschke</a>
 * 
 * The OWL2Prova library is part of the Rule Based Service Level Agreement (RBSLA)
 * framework, available at 
 * <a href=" http://ibis.in.tum.de/staff/paschke/rbsla/index.htm">RBSLA</a>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package rbsla.owl.converter.dlp;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.rdf.model.impl.ResourceImpl;
import rbsla.owl.ProvaModel;

/**
 * Map the OWL instances to Individual keys according thier given relations
 * in the owl file (sameAs, differentForm, etc.). Also build up the subClass graph
 * and map it to Horn Syntax. This class can continue previous mappings.
 * It extends the IdentifyMapper and adds the constructs which are needed to continue previous mappings.
 * @version 0.1 <1 Feb 2005>
 * @since 0.1
 */
public class UpdateIdentifyMapper extends IdentifyMapper {
	
	private boolean anonMapping; // map anons
	private boolean instanceMapping; // map equivalent instances
	private boolean classMapping; //map equivalent classes
	
	protected UpdateMapperList UpdateMapList = new UpdateMapperList();
	protected IndividualList importClassList;
	protected IndividualList importGroupList;
	/**
	 * Constructor
	 * @param _mapEnable sets Individual Mapping enable
	 */
	public UpdateIdentifyMapper(){
		super();
		anonMapping=true; // map anons to anon ids
		instanceMapping=true; // map equivalent instances to one unique id
		classMapping=true; // map equivalent classes
		
	}
	
	/**
	 * Map anons to an unique anon_id
	 * @param mapping
	 */
	public void setAnonMapping(boolean mapping) {
		anonMapping = mapping;
	}
	
	/**
	 * Map equivalent instances to an unique identifier (ID)
	 * @param mapping
	 */
	public void setInstanceMapping(boolean mapping) {
		instanceMapping = mapping;
	}
	
	/**
	 * Map equivalent classes to each other
	 * @param mapping
	 */
	public void setClassMapping (boolean mapping) {
		classMapping = mapping;
	}
	
	/**
	 * Starts and controles the Individual Mapping process
	 * 
	 * @param _JenaModel the JENA Model of the OWL description
	 * @param _inputMapList the MapList of previous Mapping
	 * @param _writer the ProvaModel
	 * @param _importClassList the ClassList of previous Mapping
	 * @param _importGroupList the GroupList of previous Mapping
	 * @return ProvaModel
	 */
	public ProvaModel run(Model _JenaModel, MapperList _inputMapList, ProvaModel _writer, IndividualList _importClassList, IndividualList _importGroupList){
		
		importClassList = _importClassList;
		importGroupList = _importGroupList;
		
		UpdateMapList.transferToUpdateList(_inputMapList);
		

		//	Phase 1: Identify the OWL Instances
		//Phase 1: Collect the  instances, classes and anons in the respective lists
		if ((anonMapping) || (classMapping) || (instanceMapping))
		for (StmtIterator iter = _JenaModel.listStatements((Resource) null,_JenaModel.createProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),(RDFNode) null); iter.hasNext();){
			Statement stmt = iter.nextStatement();
			if ((classMapping) || (instanceMapping)) this.collectInstances_and_Classes(stmt); // collect instances and classes
			if (anonMapping) this.collectAnons(stmt); // collect anons
		}
		//Phase 2: take the differentForm predicates into account
		if ((classMapping) || (instanceMapping)) {
			
		for (StmtIterator iter = _JenaModel.listStatements((Resource) null,_JenaModel.createProperty("http://www.w3.org/2002/07/owl#differentFrom"),(RDFNode) null); iter.hasNext();){
			Statement stmt = iter.nextStatement();
			this.selectDIFFERENTFORM(stmt);
		}
		
		//Phase 3: take the sameAs predicates into account
		for (StmtIterator iter = _JenaModel.listStatements((Resource) null,_JenaModel.createProperty("http://www.w3.org/2002/07/owl#sameAs"),(RDFNode) null); iter.hasNext();){
			Statement stmt = iter.nextStatement();
			this.selectSAMEAS(stmt);
		}
		
		//Phase 3.1: take the equivalentClass predicates into account
		for (StmtIterator iter = _JenaModel.listStatements((Resource) null,_JenaModel.createProperty("http://www.w3.org/2002/07/owl#equivalentClass"),(RDFNode) null); iter.hasNext();){
			Statement stmt = iter.nextStatement();
			this.selectSAMEAS(stmt);
		}
		
		//Phase 3.2: take the sameIndividualAs predicates into account
		for (StmtIterator iter = _JenaModel.listStatements((Resource) null,_JenaModel.createProperty("http://www.w3.org/2002/07/owl#sameIndividualAs"),(RDFNode) null); iter.hasNext();){
			Statement stmt = iter.nextStatement();
			this.selectSAMEAS(stmt);
		}}
		
		//Phase 4: Create a collection of non categorizable instances
		this.createComposites();
		
		//Phase 5: Mapping of the insances to individuals and adding the mapping to the _writer
		if (instanceMapping) _writer = this.phase5(_writer);
		if (classMapping) _writer = this.mappingClasses(_writer); // map classes
		this.mergeClassLists();
		this.mergeGroupLists();
		if (anonMapping) this.mappingAnons(); // map anons
		
		
		return _writer;
	}

	/**
	 * Evaluate the "sameAs" Statements
	 * @param _JenaModel the Jena Model of the OWL description
	 */
	
	public void selectSAMEAS(Statement stmt){
		if (stmt.getPredicate().getLocalName().equals("sameAs")){
			if (stmt.getObject() instanceof ResourceImpl){
				ResourceImpl Obj = (ResourceImpl) stmt.getObject();
				String Subject = NSL.getAbbreviation(stmt.getSubject().getNameSpace()) +
								 "_" + stmt.getSubject().getLocalName();
				String Object = NSL.getAbbreviation(Obj.getNameSpace()) +
								"_" + Obj.getLocalName();
				
				/**
				 * Case 1: Both elements of the sameAs statement are contained in teh Individual List (IL)
				 */
			
				if (IndiList.isElementOfList(Subject) && IndiList.isElementOfList(Object)){
					IndividualList tmp = new IndividualList(Subject);
					tmp.addIndi(Object);
					CompositeList.addIndiList(tmp);
					IndiList.removeIndi(Subject);
					IndiList.removeIndi(Object);
				}
				/**
				 * Case 2: one element in IL and the other one in Composite List (CL)
				 */
				if (IndiList.isElementOfList(Subject) && CompositeList.isElementOfList(Object)){
					CompositeList.phase3add(Subject, Object);
					IndiList.removeIndi(Subject);
				}
				if (IndiList.isElementOfList(Object) && CompositeList.isElementOfList(Subject)){
					CompositeList.phase3add(Object, Subject);
					IndiList.removeIndi(Object);
				}
				/**
				 * Case 3: both elements in CL
				 */

				if (CompositeList.isElementOfList(Subject) && CompositeList.isElementOfList(Object)){
					if (!(CompositeList.getIndividualListOfElement(Subject).equals(
							CompositeList.getIndividualListOfElement(Object)))){
						CompositeList.mergeIndividualLists(Subject, Object);
					}
				}
				/**
				 * Case 4: both elements in UpdateMapperList
				 */
				if (UpdateMapList.isElementOfMapping(Subject) && UpdateMapList.isElementOfMapping(Object)){
					UpdateMapList.mergeIntern(Subject, Object);
				}
				/**
				 * Case 5: one elemement in UpdateMapperList
				 */
				if (UpdateMapList.isElementOfMapping(Subject) && IndiList.isElementOfList(Object)){
					UpdateMapList.addElement(Subject, Object);
					IndiList.removeIndi(Object);
				}
				if (UpdateMapList.isElementOfMapping(Subject) && CompositeList.isElementOfList(Object)){
					int marker = -1;
					
					for (int i = 0; i < CompositeList.sizeOfList(); i++){
						IndividualList tmpList = CompositeList.getIndiList(i);
						if (tmpList.isElementOfList(Object)){
							UpdateMapList.addElements(Subject, tmpList);
							marker = i;
						}
					}
					
					CompositeList.removeElementAt(marker);
				}
				if (UpdateMapList.isElementOfMapping(Object) && IndiList.isElementOfList(Subject)){
					UpdateMapList.addElement(Object, Subject);
					IndiList.removeIndi(Subject);
				}
				if (UpdateMapList.isElementOfMapping(Object) && CompositeList.isElementOfList(Subject)){
					int marker = -1;
					
					for (int i = 0; i < CompositeList.sizeOfList(); i++){
						IndividualList tmpList = CompositeList.getIndiList(i);
						if (tmpList.isElementOfList(Subject)){
							UpdateMapList.addElements(Object, tmpList);
							marker = i;
						}
					}
					
					CompositeList.removeElementAt(marker);
				}
			}	
		}		
	}
	
	/**
	 * Evaluates the "sameAs" Statement for classes
	 * 
	 * @param stmt a JENA statement
	 * @param _writer the ProvaModel
	 * @return ProvaModel
	 */
	public ProvaModel selectSAMEASforCLASSES(Statement stmt, ProvaModel _writer){		
		if (stmt.getPredicate().getLocalName().equals("sameAs")){
			if (stmt.getObject() instanceof ResourceImpl){
				ResourceImpl Obj = (ResourceImpl) stmt.getObject();
				String Subject = NSL.getAbbreviation(stmt.getSubject().getNameSpace()) +
								 "_" + stmt.getSubject().getLocalName();
				String Object = NSL.getAbbreviation(Obj.getNameSpace()) +
								"_" + Obj.getLocalName();
			/**
			 * Case 1: both elements of the sameAs statement are contained in the ClassList (CL)
			 */
				if (ClassList.isElementOfList(Subject) && ClassList.isElementOfList(Object)){
					IndividualList tmp = new IndividualList(Subject);
					tmp.addIndi(Object);
					GroupList.addIndiList(tmp);
					ClassList.removeIndi(Subject);
					ClassList.removeIndi(Object);
				}
			/**
			 * Case 2: one element in CL and the other one in GroupList (GL)
			 */			
				if (ClassList.isElementOfList(Subject) && GroupList.isElementOfList(Object)){
					GroupList.phase3add(Subject, Object);
					ClassList.removeIndi(Subject);
				}
				if (ClassList.isElementOfList(Object) && GroupList.isElementOfList(Subject)){
					GroupList.phase3add(Object, Subject);
					ClassList.removeIndi(Object);
				}
				/**
				 * Case 3: both elements in GL
				 */	
				if (GroupList.isElementOfList(Subject) && GroupList.isElementOfList(Object)){
					if (!(GroupList.getIndividualListOfElement(Subject).equals(
							GroupList.getIndividualListOfElement(Object)))){
						GroupList.mergeIndividualLists(Subject, Object);
					}
				}
				/**
				 * Case 4: both elements in importedClassList
				 */	
				if (importClassList.isElementOfList(Subject) && importClassList.isElementOfList(Object)){
					IndividualList tmp = new IndividualList(Subject);
					tmp.addIndi(Object);
					GroupList.addIndiList(tmp);
					importClassList.removeIndi(Subject);
					importClassList.removeIndi(Object);
				}
				/**
				 * Case 5: one element in importedClassList and one element in CL
				 */	
				if (importClassList.isElementOfList(Subject) && ClassList.isElementOfList(Object)){
					IndividualList tmp = new IndividualList(Subject);
					tmp.addIndi(Object);
					GroupList.addIndiList(tmp);
					importClassList.removeIndi(Subject);
					ClassList.removeIndi(Object);
				}
				if (importClassList.isElementOfList(Object) && ClassList.isElementOfList(Subject)){
					IndividualList tmp = new IndividualList(Subject);
					tmp.addIndi(Object);
					GroupList.addIndiList(tmp);
					importClassList.removeIndi(Object);
					ClassList.removeIndi(Subject);
				}
				/**
				 * Case 6: one element in importedClassList and one element in GL
				 */	
				if (importClassList.isElementOfList(Subject) && GroupList.isElementOfList(Object)){
					GroupList.phase3add(Subject, Object);
					importClassList.removeIndi(Subject);
				}
				if (importClassList.isElementOfList(Object) && GroupList.isElementOfList(Subject)){
					GroupList.phase3add(Object, Subject);
					importClassList.removeIndi(Object);
				}
				/**
				 * Case 7: both element in importedGroupList
				 */	
				if (importGroupList.isElementOfList(Subject) && importGroupList.isElementOfList(Object)){
					if (!(importGroupList.getIndividualListOfElement(Subject).equals(
							importGroupList.getIndividualListOfElement(Object)))){	
						IndividualList SubjectList = importGroupList.getIndividualListOfElement(Subject);
						IndividualList ObjectList = importGroupList.getIndividualListOfElement(Object);
						for (int i = 0; i < SubjectList.sizeOfList(); i++){
							for (int j = 0; j < ObjectList.sizeOfList(); j++){
								_writer.write(SubjectList.getIndi(i) + "(X) :- fact(" +
										ObjectList.getIndi(j) + "(X))." + "\n");
								_writer.write(ObjectList.getIndi(j) + "(X) :- fact(" +
										SubjectList.getIndi(i) + "(X))." + "\n");
							}
						}
						importGroupList.mergeIndividualLists(Subject, Object);
					}
				}
				/**
				 * Case 8: one element in importedClassList and one element in imported GroupList
				 */	
				if (importClassList.isElementOfList(Subject) && importGroupList.isElementOfList(Object)){
					IndividualList ObjectList = importGroupList.getIndividualListOfElement(Object);
					for (int i = 0; i < ObjectList.sizeOfList(); i++){
						_writer.write(Subject + "(X) :- fact(" +
								ObjectList.getIndi(i) + "(X))." + "\n");
						_writer.write(ObjectList.getIndi(i) + "(X) :- fact(" +
								Subject + "(X))." + "\n");
					}
					importGroupList.phase3add(Subject, Object);
					importClassList.removeIndi(Subject);
				}
				if (importClassList.isElementOfList(Object) && importGroupList.isElementOfList(Subject)){
					IndividualList SubjectList = importGroupList.getIndividualListOfElement(Subject);
					for (int i = 0; i < SubjectList.sizeOfList(); i++){
						_writer.write(Object + "(X) :- fact(" +
								SubjectList.getIndi(i) + "(X))." + "\n");
						_writer.write(SubjectList.getIndi(i) + "(X) :- fact(" +
								Object + "(X))." + "\n");
					}
					importGroupList.phase3add(Object, Subject);
					importClassList.removeIndi(Object);
				}
				/**
				 * Case 9: one element in importedGroupList and one element in CL
				 */	
				if (ClassList.isElementOfList(Subject) && importGroupList.isElementOfList(Object)){
					IndividualList ObjectList = importGroupList.getIndividualListOfElement(Object);
					for (int i = 0; i < ObjectList.sizeOfList(); i++){
						_writer.write(Subject + "(X) :- fact(" +
								ObjectList.getIndi(i) + "(X))." + "\n");
						_writer.write(ObjectList.getIndi(i) + "(X) :- fact(" +
								Subject + "(X))." + "\n");
					}
					importGroupList.phase3add(Subject, Object);
					ClassList.removeIndi(Subject);
				}
				if (ClassList.isElementOfList(Object) && importGroupList.isElementOfList(Subject)){
					IndividualList SubjectList = importGroupList.getIndividualListOfElement(Subject);
					for (int i = 0; i < SubjectList.sizeOfList(); i++){
						_writer.write(Object + "(X) :- fact(" +
								SubjectList.getIndi(i) + "(X))." + "\n");
						_writer.write(SubjectList.getIndi(i) + "(X) :- fact(" +
								Object + "(X))." + "\n");
					}
					importGroupList.phase3add(Object, Subject);
					ClassList.removeIndi(Object);
				}
				/**
				 * Case 10: one element in importedGroupList and one element in GL
				 */	
				if (importGroupList.isElementOfList(Subject) && GroupList.isElementOfList(Object)){
					IndividualList SubjectList = importGroupList.getIndividualListOfElement(Subject);
					importGroupList.removeIndividualListOfElement(Subject);
					IndividualList ObjectList = GroupList.getIndividualListOfElement(Object);
					GroupList.removeIndividualListOfElement(Object);
					for (int i = 0; i < SubjectList.sizeOfList(); i++){
						for (int j = 0; j < ObjectList.sizeOfList(); j++){
							_writer.write(SubjectList.getIndi(i) + "(X) :- fact(" +
									ObjectList.getIndi(j) + "(X))." + "\n");
							_writer.write(ObjectList.getIndi(j) + "(X) :- fact(" +
									SubjectList.getIndi(i) + "(X))." + "\n");
						}
					}
					//merge
					for (int i = 0; i < ObjectList.sizeOfList(); i++){
						SubjectList.addIndi(ObjectList.getIndi(i));
					}
					importGroupList.addIndiList(SubjectList);
				}
				if (importGroupList.isElementOfList(Object) && GroupList.isElementOfList(Subject)){
					IndividualList ObjectList = importGroupList.getIndividualListOfElement(Object);
					importGroupList.removeIndividualListOfElement(Object);
					IndividualList SubjectList = GroupList.getIndividualListOfElement(Subject);
					GroupList.removeIndividualListOfElement(Subject);
					for (int i = 0; i < SubjectList.sizeOfList(); i++){
						for (int j = 0; j < ObjectList.sizeOfList(); j++){
							_writer.write(SubjectList.getIndi(i) + "(X) :- fact(" +
									ObjectList.getIndi(j) + "(X))." + "\n");
							_writer.write(ObjectList.getIndi(j) + "(X) :- fact(" +
									SubjectList.getIndi(i) + "(X))." + "\n");
						}
					}
					//merge
					for (int i = 0; i < ObjectList.sizeOfList(); i++){
						SubjectList.addIndi(ObjectList.getIndi(i));
					}
					importGroupList.addIndiList(SubjectList);
				}
			}
			
		}
		return _writer;
	}
	

	/**
	 * Mapping of the Individuals and their Hash Values in PROVA Syntax
	 * 
	 * @param _writer the ProvaModel
	 * @return ProvaModel
	 */
	public ProvaModel phase5(ProvaModel _writer){
		
		for (int i = 0; i < UpdateMapList.size(); i++){
			UpdateIndividualList tmpList = (UpdateIndividualList) UpdateMapList.getAtIndex(i).getIndiList();
			for (int j = 0; j < tmpList.sizeOfList(); j++){
				if (!(tmpList.getUpdateInfoElement(j).isOld())){
					_writer.write("mapping(\"" + UpdateMapList.getAtIndex(i).getHash() + "\", \"" + 
							tmpList.getUpdateInfoElement(j).getElement() + "\")." + "\n");		
				}
			}
		}
		
		MapList = UpdateMapList.transferToMapperList();
		_writer = this.mappingIndividuals(_writer, MapList.getGreatestHashCode());
		return _writer;
	}
	
	/**
	 * Merges the imported (previous) GroupList to the intern GroupList
	 */
	private void mergeGroupLists (){
		for (int i = 0; i < importGroupList.sizeOfList(); i++){
			GroupList.addIndiList(importGroupList.getIndiList(i));
		}
	}
	
	/**
	 * Merges the imported (previous) ClassList to the intern ClassList
	 */
	
	private void mergeClassLists (){
		for (int i = 0; i < importClassList.sizeOfList(); i++){
			ClassList.addIndi(importClassList.getIndi(i));
		}
	}
	
}
