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

import java.net.URI;
import java.util.Properties;
import java.lang.reflect.Method;

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

import fr.inrialpes.exmo.align.impl.method.StringDistAlignment;
import fr.inrialpes.exmo.align.impl.MatrixMeasure;
import java.util.HashSet;

public class RSDistAlignment extends StringDistAlignment implements AlignmentProcess {

    Method dissimilarity = null;
    String methodName = "rsDistance";

    public RSDistAlignment() {

    }

    protected HashSet<String> nGrams(String str, int grams) {
        HashSet<String> retVal = new HashSet<String>();


        // Creates all the substrings containing 1 to <grams>
        // characters
        for (int g = 1; g <= grams; ++g) {
            for (int i = 0; i <= str.length() - g; ++i) {
                retVal.add(str.substring(i, g));
            }
        }

        return retVal;
    }

    /** Creation **/ 
    public RSDistAlignment() {
        setSimilarity( new MatrixMeasure() {
            public double measure( Object o1, Object o2 ) throws Exception {
                // Get the two strings to compare
                String s1 = ontology1().getEntityName( o1 );
                String s2 = ontology2().getEntityName( o2 );
                
                // Return 1.0 (max distance) if either of the two entities are null
                if ( s1 == null || s2 == null ) {
                    return 1.;
                }

                // Return 0.0 (no distance) if the two strings are
                // equal
                if (s1.toLowerCase().equals(s2.toLowerCase())) {
                    return 0.;
                }

                HashSet<String> s1hash = nGrams(s1.toLowerCase(), 3);
                HashSet<String> s2hash = nGrams(s2.toLowerCase(), 3);


                int totalLength = 0;
                for (String s : s1hash) {
                    if (s2hash.contains(s)) {
                        totalLength += s.length();
                    }
                }
                double distance = 1.0 - (totalLength / ( (s1.length() + s2.length()) * 2) );
                
                // abs value of distance
                return (distance < 0.0 ? -distance : distance);
//
//                Object[] params = { s1.toLowerCase(), s2.toLowerCase() };
//
//                if ( debug > 4 ) {
//                    System.err.println( "OB:"+s1+" ++ "+s2+" ==> "+dissimilarity.invoke( null, params ));
//                }
//
//                return ((Double)dissimilarity.invoke( null, params )).doubleValue();
            }

            public double classMeasure( Object cl1, Object cl2 ) throws Exception {
                return measure( cl1, cl2 );
            }

            public double propertyMeasure( Object pr1, Object pr2 ) throws Exception{
                return measure( pr1, pr2 );
            }

            public double individualMeasure( Object id1, Object id2 ) throws Exception{
                return measure( id1, id2 );
            }
        });
        setType("**");
    }

    /* Processing */
    public void align( Alignment alignment, Properties params ) throws AlignmentException {
        // Get function from params
        String f = params.getProperty("stringFunction");
        try {
            if ( f != null ) methodName = f.trim();
            Class sClass = Class.forName("java.lang.String");
            Class[] mParams = { sClass, sClass };
            dissimilarity = Class.forName("fr.inrialpes.exmo.ontosim.string.StringDistances").getMethod( methodName, mParams );
        } catch (ClassNotFoundException e) {
            e.printStackTrace(); // never happens
        } catch (NoSuchMethodException e) {
            throw new AlignmentException( "Unknown method for RSDistAlignment : "+params.getProperty("stringFunction"), e );
        }

        // JE2010: Strange: why does it is not equivalent to call
        // super.align( alignment, params )
        // Load initial alignment
        loadInit( alignment );

        // Initialize matrix
        getSimilarity().initialize( ontology1(), ontology2(), alignment );

        // Compute similarity/dissimilarity
        getSimilarity().compute( params );

        // Print matrix if asked
        if ( params.getProperty("printMatrix") != null ) {
            printDistanceMatrix( params );
        }

        // Extract alignment
        extract( type, params );
    }

}
