/*
 * $id: NameAndPropertyAlignment.java 1189 2010-01-03 17:57:13Z euzenat $
 *
 * Copyright (C) INRIA, 2003-2004, 2007-2010
 *
 * This program 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.1 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */


package fr.inrialpes.exmo.align.impl.method; 

import java.lang.Integer;
import java.util.Vector;
import java.util.Set;
import java.util.Properties;

import org.semanticweb.owl.align.Alignment;
import org.semanticweb.owl.align.AlignmentProcess;
import org.semanticweb.owl.align.AlignmentException;

import fr.inrialpes.exmo.align.impl.DistanceAlignment;
import fr.inrialpes.exmo.ontowrap.HeavyLoadedOntology;
import fr.inrialpes.exmo.ontowrap.OntologyFactory;
import fr.inrialpes.exmo.ontowrap.OntowrapException;

import fr.inrialpes.exmo.ontosim.string.StringDistances;

/** This class has been built for ISWC experiments with bibliography.
 * It implements a non iterative (one step) OLA algorithms based on
 * the name of classes and properties. It could be made iterative by
 *  just adding range/domain on properties...
 *  The parameters are:
 *  - threshold: above what do we select for the alignment;
 *  - epsillon [ignored]: for convergence
 *  - pic1: weigth for class name
 *  - pic2: weight for class attributes
 *  - pia1 [ignored=1]: weigth for property name
 *  - pia3 [ignored=0]: weigth for property domain
 *  - pia4 [ignored=0]: weigth for property range
 *
 * @author Jérôme Euzenat / Jerome Pierson
 * @version $Id: NameAndPropertyAlignment.java 1299 2010-03-07 19:47:04Z euzenat $ 
 */

public class NameAndPropertyAlignment extends DistanceAlignment implements AlignmentProcess {

    private HeavyLoadedOntology<Object> honto1 = null;
    private HeavyLoadedOntology<Object> honto2 = null;

    /** Creation **/
    public NameAndPropertyAlignment(){
	setType("**");
    };

    /**
     * Initialisation
     * The class requires HeavyLoadedOntologies
     */
    public void init(Object o1, Object o2, Object ontologies) throws AlignmentException {
	super.init( o1, o2, ontologies );
	if ( !( getOntologyObject1() instanceof HeavyLoadedOntology
		&& getOntologyObject2() instanceof HeavyLoadedOntology ))
	    throw new AlignmentException( "NameAndPropertyAlignment requires HeavyLoadedOntology ontology loader" );
    }

    /** Processing **/
    public void align( Alignment alignment, Properties params ) throws AlignmentException {
	loadInit( alignment );
	honto1 = (HeavyLoadedOntology<Object>)getOntologyObject1();
	honto2 = (HeavyLoadedOntology<Object>)getOntologyObject2();
	double threshold = 1.; // threshold above which distances are too high
	int i, j = 0;     // index for onto1 and onto2 classes
	int nbclass1 = 0; // number of classes in onto1
	int nbclass2 = 0; // number of classes in onto2
	Vector<Object> classlist2 = new Vector<Object>(10); // onto2 classes
	Vector<Object> classlist1 = new Vector<Object>(10); // onto1 classes
	double classmatrix[][];   // class distance matrix
	int nbprop1 = 0; // number of properties in onto1
	int nbprop2 = 0; // number of properties in onto2
	Vector<Object> proplist2 = new Vector<Object>(10); // onto2 properties
	Vector<Object> proplist1 = new Vector<Object>(10); // onto1 properties
	double propmatrix[][];   // properties distance matrix
	double pic1 = 0.5; // class weigth for name
	double pia1 = 1.; // relation weight for name
	double epsillon = 0.05; // stoping condition 
       	
	if ( params.getProperty("debug") != null )
	    debug = Integer.parseInt( params.getProperty("debug") );

	    // Create property lists and matrix
	    for ( Object prop : honto1.getObjectProperties() ){
		nbprop1++;
		proplist1.add( prop );
	    }
	    for ( Object prop : honto1.getDataProperties() ){
		nbprop1++;
		proplist1.add( prop );
	    }
	    for ( Object prop : honto2.getObjectProperties() ){
		nbprop2++;
		proplist2.add( prop );
	    }
	    for ( Object prop : honto2.getDataProperties() ){
		nbprop2++;
		proplist2.add( prop );
	    }
	    propmatrix = new double[nbprop1+1][nbprop2+1];
	
	    // Create class lists
	    for ( Object cl : honto1.getClasses() ){
		nbclass1++;
		classlist1.add( cl );
	    }
	    for ( Object cl : honto2.getClasses() ){
		nbclass2++;
		classlist2.add( cl );
	    }
	    classmatrix = new double[nbclass1+1][nbclass2+1];

	    try {
		if (debug > 0) System.err.println("Initializing property distances");
		for ( i=0; i<nbprop1; i++ ){
		    Object cl1 = proplist1.get(i);
		    String st1 = honto1.getEntityName( cl1 );
		    if ( st1 != null) st1 = st1.toLowerCase();
		    for ( j=0; j<nbprop2; j++ ){
			Object cl2 = proplist2.get(j);
			String st2 = honto2.getEntityName( cl2 );
			if( st2 != null ) st2 = st2.toLowerCase();
			if ( st1 != null || st2 != null ) {
			    propmatrix[i][j] = pia1 * StringDistances.subStringDistance( st1, st2 );
			} else {
			    propmatrix[i][j] = 1.;
			}
		    }
		}

		// Initialize class distances
		if (debug > 0) System.err.println("Initializing class distances");
		for ( i=0; i<nbclass1; i++ ){
		    Object cl1 = classlist1.get(i);
		    for ( j=0; j<nbclass2; j++ ){
			classmatrix[i][j] = pic1*StringDistances.subStringDistance( honto1.getEntityName( cl1 ).toLowerCase(), honto2.getEntityName( classlist2.get(j) ).toLowerCase());
		    }
		}
	    } catch ( OntowrapException owex ) {
		throw new AlignmentException( "Cannot find entity URI", owex );
	    }

	    // Iterate until completion
	    double factor = 1.0;
	    while ( factor > epsillon ){
		// Compute property distances
		// -- FirstExp: nothing to be done: one pass
		// Here create the best matches for property distance already
		// -- FirstExp: goes directly in the alignment structure
		//    since it will never be refined anymore...
		if (debug > 0) System.err.print("Storing property alignment\n");
		for ( i=0; i<nbprop1; i++ ){
		    boolean found = false;
		    int best = 0;
		    double max = threshold;
		    for ( j=0; j<nbprop2; j++ ){
			if ( propmatrix[i][j] < max) {
			    found = true;
			    best = j;
			    max = propmatrix[i][j];
			}
		    }
		    if ( found && max < 0.5) { addAlignDistanceCell( proplist1.get(i), proplist2.get(best), "=", max ); }
		}
		if (debug > 0) System.err.print("Computing class distances\n");
		// Compute classes distances
		// -- for all of its attribute, find the best match if possible... easy
		// -- simply replace in the matrix the value by the value plus the 
		// classmatrix[i][j] =
		// pic1 * classmatrix[i][j]
		// + pic2 * 2 *
		//  (sigma (att in c[i]) getAllignCell... )
		//  / nbatts of c[i] + nbatts of c[j]

		for ( i=0; i<nbclass1; i++ ){
		    Set<Object> properties1 = honto1.getProperties( classlist1.get(i), OntologyFactory.ANY, OntologyFactory.ANY, OntologyFactory.ANY );
		    
		    int nba1 = properties1.size();
		    if ( nba1 > 0 ) { // if not, keep old values...
			//Set correspondences = new HashSet();
			for ( j=0; j<nbclass2; j++ ){
			    Set<Object> properties2 = honto2.getProperties( classlist2.get(j), OntologyFactory.ANY, OntologyFactory.ANY, OntologyFactory.ANY );
			    //int nba2 = properties1.size();
			    //double attsum = 0.;
			    // check that there is a correspondance
			    // in list of class2 atts and add their weights 
			
			    // Make a local alignment with the properties.
			    double moy_align_loc = alignLocal(properties1,properties2);

			    if (moy_align_loc > 0.7){
				classmatrix[i][j] = (classmatrix[i][j] + 2* moy_align_loc)/3;
			    }
			}
		    }
		}

		// Assess factor
		// -- FirstExp: nothing to be done: one pass
		factor = 0.;
	    }
	// This mechanism should be parametric!
	// Select the best match
	// There can be many algorithm for these:
	// n:m: get all of those above a threshold
	// 1:1: get the best discard lines and columns and iterate
	// Here we basically implement ?:* because the algorithm
	// picks up the best matching object above threshold for i.
	if (debug > 0) System.err.print("Storing class alignment\n");
	
	for ( i=0; i<nbclass1; i++ ){
	    boolean found = false;
	    int best = 0;
	    double max = threshold;
	    for ( j=0; j<nbclass2; j++ ){
		if ( classmatrix[i][j] < max) {
		    found = true;
		    best = j;
		    max = classmatrix[i][j];
		}
	    }
	    if ( found && max < 0.5) { addAlignDistanceCell( classlist1.get(i), classlist2.get(best), "=", max ); }
	}
    }
    
    /**
     * Make a local alignement
     * @return an average of all the property alignement 
     */
    private double alignLocal( Set<Object> prop1, Set<Object> prop2) {
 	int nbprop1 = prop1.size();
	int nbprop2 = prop2.size();
	double max = 0.0;
	double moy= 0.0;
	int i = 0;
	int j = 0;
	String s1 = null;
	String s2 = null;
	double[][] propmatrix = new double[nbprop1+1][nbprop2+1];

	
	for ( Object property1 : prop1 ){
	    i++;
	    try {
		s1 = honto1.getEntityName( property1 ).toLowerCase();
	    } catch ( OntowrapException aex ) { s1 = null; };
	    for ( Object property2 : prop2 ) {
		j++;
		try {
		    s2 = honto2.getEntityName( property2 ).toLowerCase();
		    propmatrix[i][j] =  1.0-StringDistances.subStringDistance( s1, s2 );
		} catch ( OntowrapException aex ) {
		    propmatrix[i][j] =  0.0;
		}
	    }
	    j=0;
	}
	
	for ( i=0; i< nbprop1; i++ ) {
	    for ( j=0; j<nbprop2; j++ ) {
		if ( propmatrix[i][j] > max ) {
		    max = propmatrix[i][j];
		}
	    }
	    moy = moy+max;
	    max = 0.0;
	}
	return moy;
    }

}
