
import uk.ac.ic.doc.automed.IntegrityException;
import uk.ac.ic.doc.automed.reps.*;
import uk.ac.ic.doc.automed.matching.*;
import uk.ac.ic.doc.automed.matching.modules.*;
import uk.ac.ic.doc.automed.matching.relationships.*;

import java.util.*;


public class HDMSchemaMatching{
	private	Schema[] schemas;
	private	SchemaElement[] elementsNodals1, elementsLinkNodals1, elementsLinks1;
	private	SchemaElement[] elementsNodals2, elementsLinkNodals2, elementsLinks2;
	private	SchemaElementPairRelationship[] discoveredRels;

	private ArrayList precisionRels = new ArrayList(), numofinstancesRels = new ArrayList(), existenceRels = new ArrayList();


	public void discoverRelationships(Schema s1, Schema s2)
		throws IntegrityException, UninstantiatedException, Exception	
	{

			schemas = new Schema[]{s1,s2};
			
			Util.printDate("Starting initialisation of schema: "+s1.getName());
			elementsNodals1 = SchemaElement.getSchemaElements(s1, Construct.CLASS_NODAL, false);
			elementsLinks1 = SchemaElement.getSchemaElements(s1, Construct.CLASS_LINK_NODAL, false);
			elementsLinkNodals1 = SchemaElement.getSchemaElements(s1, Construct.CLASS_LINK_NODAL, false);
			if(elementsNodals1.length==0){
				throw new Exception("Schema "+s1.getName()+" doesn't contain any comparable elements.");
			}

			Util.printDate("Starting initialisation of schema: "+s2.getName());
			elementsNodals2 = SchemaElement.getSchemaElements(s2, Construct.CLASS_NODAL, false);
			elementsLinks2 = SchemaElement.getSchemaElements(s2, Construct.CLASS_LINK_NODAL, false);
			elementsLinkNodals2 = SchemaElement.getSchemaElements(s2, Construct.CLASS_LINK_NODAL, false);
			if(elementsNodals2.length==0){
				throw new Exception("Schema "+s2.getName()+" doesn't contain any comparable elements.");
			}

			
			
			SchemaElementPairRelationship[] discoveredLinkNodalsRels = new SchemaElementPairRelationship[0]; 
			Util.printDate("Comparing link-nodals....");
			discoveredLinkNodalsRels = getRelationshipsLinkNodals(1.25, 0.85, 0.3, 0.15);
		
			SchemaElementPairRelationship[] discoveredLinksRels = new SchemaElementPairRelationship[0]; 
			Util.printDate("Comparing links....");
			discoveredLinksRels = getRelationshipsLinks(1.25, 0.85, 0.3, 0.15);
		
			SchemaElementPairRelationship[] discoveredNodalsRels = new SchemaElementPairRelationship[0]; 
			Util.printDate("Comparing nodals....");
			discoveredNodalsRels = getRelationshipsNodals(1.25, 0.85, 0.3, 0.15);


			
			System.out.println("-----------------------------------------------------------");
			for(int i=0; i<discoveredLinkNodalsRels.length; i++){
				System.out.println(discoveredLinkNodalsRels[i]);
			}
			System.out.println("---");
			for(int i=0; i<discoveredLinksRels.length; i++){
				System.out.println(discoveredLinksRels[i]);
			}
			System.out.println("---");
			for(int i=0; i<discoveredNodalsRels.length; i++){
				System.out.println(discoveredNodalsRels[i]);
			}
			System.out.println("---");

	}
		


	public static void main(String[] args){
		try{
			System.out.println("Schema matching");
			Schema s1 = Schema.getSchema(args[0]);
			Schema s2 = Schema.getSchema(args[1]);

			HDMSchemaMatching matching = new HDMSchemaMatching();
			
			matching.discoverRelationships(s1,s2);
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}


	
	public SchemaElementPairRelationship[] getRelationshipsNodals(double mainThreshold, double eqThreshold, double overThreshold, double disThreshold) throws UninstantiatedException, RelationshipConflictException, Exception{

		AbstractModule datatype=null, statistics=null, names=null, numOfInstances=null, existence=null, naivebayes=null, precision=null;	
		SimilarSchemaElementPair datatypePairs[], statisticsPairs[], namesPairs[], numinstancesPairs[], existencePairs[], naivebayesPairs[], precisionPairs[];  

		datatype = new DataTypeModule(elementsNodals1, elementsNodals2);
//		datatypePairs = datatype.getBidirectionalDegrees();
		statistics = new StatisticsModule(elementsNodals1, elementsNodals2);
//		statisticsPairs = statistics.getBidirectionalDegrees();
		names = new ElementNameModule(elementsNodals1, elementsNodals2);
//		namesPairs = names.getBidirectionalDegrees();
		numOfInstances = new NumberInstancesModule(elementsNodals1, elementsNodals2);
//		numinstancesPairs = numOfInstances.getBidirectionalDegrees();
		existence = new CheckExistenceModule(elementsNodals1, elementsNodals2);
//		existencePairs = existence.getBidirectionalDegrees();
		precision = new PrecisionModule(elementsNodals1, elementsNodals2);
//		precisionPairs = precision.getBidirectionalDegrees();
//		naivebayes = new NaiveBayesModule(elementsNodals1, elementsNodals2);
//		naivebayesPairs = naivebayes.getBidirectionalDegrees();
	
//		Filter filter = new Filter((SchemaElement[])elementsNodals1.toArray(new SchemaElement[0]), (SchemaElement[])elementsNodals2.toArray(new SchemaElement[0]));
		Filter filter = new Filter(elementsNodals1, elementsNodals2);
		filter.addModule(numOfInstances);
		filter.addModule(precision);
		filter.addModule(existence);
		filter.addModule(names);
		SimilarSchemaElementPair[] allpairs = filter.getAllPairs();

/*
		Aggregator aggregator = new Aggregator(allpairs);
		aggregator.addNonAuxiliaryModule(datatype);
		aggregator.addNonAuxiliaryModule(statistics);
		aggregator.addNonAuxiliaryModule(precision);
		aggregator.addNonAuxiliaryModule(existence);
		aggregator.addNonAuxiliaryModule(numOfInstances);
		aggregator.addAuxiliaryModule(names);
		SimilarSchemaElementPair[] aggregated = aggregator.getPairs();
*/		
		BidirectionalMap map = new BidirectionalMap(eqThreshold, overThreshold, disThreshold); 
		DegreeCombinator combinator = new DegreeCombinator(allpairs);
		combinator.setNumberInstancesModule(numOfInstances);
		combinator.setExistenceModule(existence);
		combinator.setPrecisionModule(precision);
		SchemaElementPairRelationship[] rels = combinator.getRelationships(map);


		addPrecisionRels(precision, map);
		addExistenceRels(existence, map);
		addNumofinstancesRels(numOfInstances, map);

		
		return rels;
	}

	public SchemaElementPairRelationship[] getRelationshipsLinkNodals(double mainThreshold, double eqThreshold, double overThreshold, double disThreshold) throws UninstantiatedException, RelationshipConflictException, Exception{

		if(elementsLinkNodals1.length==0 || elementsLinkNodals2.length==0)
			return new SchemaElementPairRelationship[0];

			
		AbstractModule datatype=null, statistics=null, names=null, numOfInstances=null, existence=null, naivebayes=null, precision=null;	
		SimilarSchemaElementPair datatypePairs[], statisticsPairs[], namesPairs[], numinstancesPairs[], existencePairs[], naivebayesPairs[], precisionPairs[];  

		datatype = new DataTypeModule(elementsLinkNodals1, elementsLinkNodals2);
		datatypePairs = datatype.getBidirectionalDegrees();

		statistics = new StatisticsModule(elementsLinkNodals1, elementsLinkNodals2);
		statisticsPairs = statistics.getBidirectionalDegrees();

		names = new ElementNameModule(elementsLinkNodals1, elementsLinkNodals2);
		namesPairs = names.getBidirectionalDegrees();

		numOfInstances = new NumberInstancesModule(elementsLinkNodals1, elementsLinkNodals2);
		numinstancesPairs = numOfInstances.getBidirectionalDegrees();

		existence = new CheckExistenceModule(elementsLinkNodals1, elementsLinkNodals2);
		existencePairs = existence.getBidirectionalDegrees();

		precision = new PrecisionModule(elementsLinkNodals1, elementsLinkNodals2);
		precisionPairs = precision.getBidirectionalDegrees();

		naivebayes = new NaiveBayesModule(elementsLinkNodals1, elementsLinkNodals2);
		naivebayesPairs = naivebayes.getBidirectionalDegrees();

	
	//	Filter filter = new Filter((SchemaElement[])elementsLinkNodals1.toArray(new SchemaElement[0]), (SchemaElement[])elementsLinkNodals2.toArray(new SchemaElement[0]));
		Filter filter = new Filter(elementsLinkNodals1, elementsLinkNodals2);
		filter.addModule(datatype);
		filter.addModule(statistics);
		filter.addModule(naivebayes);
		filter.addModule(precision);
		filter.addModule(names);
		filter.addModule(existence);
		SimilarSchemaElementPair[] filtered = filter.getCompatibles(mainThreshold);
		
		Aggregator aggregator = new Aggregator(filtered);
		aggregator.addNonAuxiliaryModule(datatype);
		aggregator.addNonAuxiliaryModule(statistics);
		aggregator.addNonAuxiliaryModule(precision);
		aggregator.addNonAuxiliaryModule(existence);
		aggregator.addNonAuxiliaryModule(numOfInstances);
		aggregator.addAuxiliaryModule(names);
		SimilarSchemaElementPair[] aggregated = aggregator.getPairs();
		
		BidirectionalMap map = new BidirectionalMap(eqThreshold, overThreshold, disThreshold); 
		DegreeCombinator combinator = new DegreeCombinator(aggregated);
		combinator.setNumberInstancesModule(numOfInstances);
		combinator.setExistenceModule(existence);
		combinator.setPrecisionModule(precision);
		SchemaElementPairRelationship[] rels = combinator.getRelationships(map);

		addPrecisionRels(precision, map);
		addExistenceRels(existence, map);
		addNumofinstancesRels(numOfInstances, map);
		
		return rels;
	}
	
	public SchemaElementPairRelationship[] getRelationshipsLinks(double mainThreshold, double eqThreshold, double overThreshold, double disThreshold) throws UninstantiatedException, RelationshipConflictException, Exception{

		if(elementsLinks1.length==0 || elementsLinks2.length==0)
			return new SchemaElementPairRelationship[0];


		AbstractModule datatype=null, statistics=null, names=null, numOfInstances=null, existence=null, naivebayes=null, precision=null;	
		SimilarSchemaElementPair datatypePairs[], statisticsPairs[], namesPairs[], numinstancesPairs[], existencePairs[], naivebayesPairs[], precisionPairs[];  

//		datatype = new DataTypeModule(elementsLinks1, elementsLinks2);
//		datatypePairs = datatype.getBidirectionalDegrees();
//		statistics = new StatisticsModule(elementsLinks1, elementsLinks2);
//		statisticsPairs = statistics.getBidirectionalDegrees();
		names = new ElementNameModule(elementsLinks1, elementsLinks2);
//		namesPairs = names.getBidirectionalDegrees();
		numOfInstances = new NumberInstancesModule(elementsLinks1, elementsLinks2);
//		numinstancesPairs = numOfInstances.getBidirectionalDegrees();
		existence = new CheckExistenceModule(elementsLinks1, elementsLinks2);
//		existencePairs = existence.getBidirectionalDegrees();
		precision = new PrecisionModule(elementsLinks1, elementsLinks2);
//		precisionPairs = precision.getBidirectionalDegrees();
//		naivebayes = new NaiveBayesModule(elementsLinks1, elementsLinks2);
//		naivebayesPairs = naivebayes.getBidirectionalDegrees();
	
	//	Filter filter = new Filter((SchemaElement[])elementsLinks1.toArray(new SchemaElement[0]), (SchemaElement[])elementsLinks2.toArray(new SchemaElement[0]));
		Filter filter = new Filter(elementsLinks1, elementsLinks2);
//		filter.addModule(datatype);
//		filter.addModule(statistics);
//		filter.addModule(naivebayes);
		filter.addModule(precision);
		filter.addModule(names);
		filter.addModule(existence);
		filter.addModule(numOfInstances);
		SimilarSchemaElementPair[] allpairs = filter.getAllPairs();
		
//		Aggregator aggregator = new Aggregator(filtered);
//		aggregator.addNonAuxiliaryModule(datatype);
//		aggregator.addNonAuxiliaryModule(statistics);
//		aggregator.addNonAuxiliaryModule(precision);
//		aggregator.addNonAuxiliaryModule(existence);
//		aggregator.addNonAuxiliaryModule(numOfInstances);
//		aggregator.addAuxiliaryModule(names);
//		SimilarSchemaElementPair[] aggregated = aggregator.getPairs();
		
		BidirectionalMap map = new BidirectionalMap(eqThreshold, overThreshold, disThreshold); 
		DegreeCombinator combinator = new DegreeCombinator(allpairs);
		combinator.setNumberInstancesModule(numOfInstances);
		combinator.setExistenceModule(existence);
		combinator.setPrecisionModule(precision);
		SchemaElementPairRelationship[] rels = combinator.getRelationships(map);

		return rels;
	}

	private void addPrecisionRels(AbstractModule module, BidirectionalMap map){
		addRels(module.getBidirectionalDegrees(), map, precisionRels);
	}
	private void addExistenceRels(AbstractModule module, BidirectionalMap map){
		addRels(module.getBidirectionalDegrees(), map, existenceRels);
	}
	private void addNumofinstancesRels(AbstractModule module, BidirectionalMap map){
		addRels(module.getBidirectionalDegrees(), map, numofinstancesRels);
	}

	private void addRels(SimilarSchemaElementPair[] pairs, BidirectionalMap map, ArrayList rels){
		for(int i=0; i<pairs.length; i++){
			SchemaElement first = pairs[i].getFirstElement();
			SchemaElement second = pairs[i].getSecondElement();
			BidirectionalDegree degree = pairs[i].getDegree();
			rels.add(new SchemaElementPairRelationship(first, second, map.getRelationship(degree)));
		}
	}

}
