import java.sql.*;
import java.io.*;
import uk.ac.ic.doc.automed.*;
import uk.ac.ic.doc.automed.reps.*;

import java.util.*;

/*
 * An application to demonstrate how various aspects of the AutoMed
 * repository may be used to link schemas together 
 * via transformation pathways
 */

public class DefineTransformations {
  public static Schema transforms1Tos4(Schema s1) 
    throws NotFoundException,TypeMismatchException,IntegrityException {

    /*
    Network.addNetworksListener(new NetworksListener() {
	  public void networkAdded(NetworkChangeEvent nce) {
	    System.out.println("Network "+ ((Network)nce.getDelta()).getName()+
			      " added");
	  }
	  public void networkRemoved(NetworkChangeEvent nce) {
	    System.out.println("Network "+ ((Network)nce.getDelta()).getName()+
			      " removed");
	  }		
      });
    List networks=Network.getNetworks();
    for (Iterator i=networks.iterator(); i.hasNext();) {
      final Network network=(Network)i.next();
      System.out.println("adding listener to network "+network.getName());
      network.addNetworkChangeListener(new NetworkChangeListener(){
	  public void nodeAdded(NetworkChangeEvent nce) {
	    System.out.println("Node \""+
			       ((Node)nce.getDelta()).getSchema().getName()+
			       "\" added to network \""+network.getName());
	  }
	  public void nodeRemoved(NetworkChangeEvent nce) {
	    System.out.println("Node \""+
			       ((Node)nce.getDelta()).getSchema().getName()+
			       "\" removed from network \""+network.getName());
	  }
	  public void edgeAdded(NetworkChangeEvent nce) {
	    System.out.println("Edge \"["+
			       ((Node[])nce.getDelta())[0].getSchema().getName()+","+
			       ((Node[])nce.getDelta())[1].getSchema().getName()+
			       
			       "]\" added to network \""+network.getName());
	  }
	  public void edgeRemoved(NetworkChangeEvent nce) {
	    System.out.println("Edge \""+
			       ((Node[])nce.getDelta())[0].getSchema().getName()+","+
			       ((Node[])nce.getDelta())[1].getSchema().getName()+
			       ((Node)nce.getDelta()).getSchema().getName()+
			       "]\" removed from network \""+network.getName());
	  }
	  public void networkMerged(NetworkChangeEvent nce) {
	    System.out.println("Network \""+
			       ((Network)nce.getDelta()).getName()+
			       "\" merged into network \""+network.getName());
	  }
	  public void networkSplit(NetworkChangeEvent nce) {
	    System.out.println("Network \""+
			       ((Network)nce.getDelta()).getName()+
			       "\" split from network \""+network.getName());
	  }
	});
    }
    */

    Model er=Model.getModel("er");
    Construct ent=er.getConstruct("entity");
    Construct att=er.getConstruct("attribute");
    Construct rel=er.getConstruct("relationship");
    Construct gen=er.getConstruct("generalisation");

    // Check that we have the objects that we want to transform
    SchemaObject staff=s1.getSchemaObject("<<staff>>");
    Schema s1a=s1.applyRenameTransformation(staff,new Object[]{"person"});

    SchemaObject newnames=
      Transformation.getTransformation(s1,s1a).getSchemaObject(s1a);
    SchemaObject person=(SchemaObject)newnames.getSchemeDefinition()[0];
    SchemaObject personsex=s1a.getSchemaObject("<<person,sex>>");

    Schema s1b=s1a.applyAddTransformation(ent,new Object[]{"male"},
    "[ {x} | {x,y} <- <<person,sex>>; (=) y 'M']",null);
    Schema s1c=s1b.applyAddTransformation(ent,new Object[]{"female"},
    "[ {x} | {x,y} <- <<person,sex>>; (=) y 'F']",null);

    SchemaObject male= 
      Transformation.getTransformation(s1a,s1b).getSchemaObject(s1b);
    SchemaObject female=
      Transformation.getTransformation(s1b,s1c).getSchemaObject(s1c);
    // No query should be given here, since generalisations are constraint
    // objects, and thus have no extent
    Schema s1d=
      s1c.applyAddTransformation
      (gen,new Object[]{"sex","total",person,male,female},null,null);
    return s1d.
      applyDeleteTransformation(personsex,
				"[ {x,y} | {x} <- <<male>>; (=) y 'M'] ++"+
				"[ {x,y} | {x} <- <<female>>; (=) y 'F']",
				null);
}

public static Schema transforms2Tos4(Schema s2) 
  throws NotFoundException,TypeMismatchException,IntegrityException {
  Model er=Model.getModel("er");
  Construct ent=er.getConstruct("entity");
  Construct att=er.getConstruct("attribute");
  Construct rel=er.getConstruct("relationship");

    // Check that we have the objects that we want to transform
    SchemaObject person=s2.getSchemaObject("<<person>>");
    SchemaObject persondname=
      s2.getSchemaObject("<<person,dname>>");

    Transformation t2_1=
      Transformation.createAddTransformation(s2,ent,
	new Object[] {"dept"},"[{x} | {y,x} <- <<person,dept>>]",null);
    Schema s2a=t2_1.getOtherSchema(s2);
    SchemaObject dept=t2_1.getSchemaObject(s2a);

    Transformation t2_2=
      Transformation.createAddTransformation(s2a,att,
	new Object[] {dept,"dname","key"},"[ {x,x} | {x} <- <<dept>>]",null);
    Schema s2b=t2_2.getOtherSchema(s2a);
    Transformation t2_3=
      Transformation.createAddTransformation(s2b,rel,
	new Object[] {"worksin",person,dept,"1:1","1:N"},"<<person,dname>>",null);
    Schema s2c=t2_3.getOtherSchema(s2b);
    Transformation t2_4=
      Transformation.createDeleteTransformation(s2c,persondname,
	"<<worksin,person,dept>>",null);
    Schema s2d=t2_4.getOtherSchema(s2c);
    SchemaObject personpid=s2d.getSchemaObject("<<person,pid,key>>");
    return s2d.applyRenameTransformation(personpid,
					 new Object[]{person,"id","key"});
  }

  public static Schema transforms3Tos4(Schema s3) 
    throws NotFoundException,TypeMismatchException,IntegrityException {
    Model er=Model.getModel("er");
    Construct ent=er.getConstruct("entity");
    Construct att=er.getConstruct("attribute");
    Construct rel=er.getConstruct("relationship");
    Construct gen=er.getConstruct("generalisation");

    // Check that we have the objects that we want to transform
    SchemaObject person=s3.getSchemaObject("<<person>>");
    SchemaObject degree=s3.getSchemaObject("<<degree>>");
    SchemaObject degreecode=s3.getSchemaObject("<<degree,code>>");
    SchemaObject degreetitle=
      s3.getSchemaObject("<<degree,title>>");
    SchemaObject runby=
      s3.getSchemaObject("<<runby,degree,dept>>");

    // Alternative style of creating transformations, having the same
    // result as using the Schema based methods, except you have returned
    // the transformation created rather than the schema created
    Transformation t3_1=
      Transformation.createContractTransformation(s3,degreetitle,null,null);
    Schema s3a=t3_1.getOtherSchema(s3);
    Transformation t3_2=
      Transformation.createContractTransformation(s3a,degreecode,null,null);
    Schema s3b=t3_2.getOtherSchema(s3a);
    Transformation t3_3=
      Transformation.createContractTransformation(s3b,runby,null,null);
    Schema s3c=t3_3.getOtherSchema(s3b);
    Transformation t3_4=
      Transformation.createContractTransformation(s3c,degree,null,null);
    Schema s3d=t3_4.getOtherSchema(s3c);
    Transformation t3_5=
      Transformation.createExtendTransformation(s3d,att,
						new Object[]{person,
							     "name",
							     "notnull"},
						null,null);
    return t3_5.getOtherSchema(s3d);
  }

  public static Schema transforms1Tos4HDM(Schema s1) 
    throws NotFoundException,TypeMismatchException,IntegrityException {
    Model hdm=Model.getModel("hdm");
    Construct node=hdm.getConstruct("node");
    Construct edge=hdm.getConstruct("edge");
    Construct cons=hdm.getConstruct("constraint");

    // Check that we have the objects that we want to transform
    SchemaObject sex=s1.getSchemaObject("<<sex>>");
    SchemaObject personsex=s1.getSchemaObject("<<_,person,sex>>");

    Schema s1a=s1.applyAddTransformation(node,
					 new Object[] {"male"},
					 "male(X):-person-sex(X,male)",
					 null);
    Schema s1b=s1a.applyAddTransformation(node,
					  new Object[] {"female"},
					  "female(X):-person-sex(X,female)",
					  null);
    Schema s1c=
      s1b.applyAddTransformation(cons,
				 new Object[]{"subset(<<male>>,<<person>>)"},
				 null,
				 null);
    Schema s1d=
      s1c.applyAddTransformation(cons,
				 new Object[]{"subset(<<female>>,<<person>>)"},
				 null,
				 null);
    Schema s1e=
      s1d.applyDeleteTransformation(personsex,
				    "person-sex(X,Y) :- male(X),Y=m;"+
				    "female(X),Y=f",
				    null);
    return s1e.applyDeleteTransformation(sex,
				    "sex(X) :- X=m;X=f",
				    null);
  }

  // This is a fairly standard check to see if two schemas can be
  // "integrated" via ID transformations
  public static void integrateSchemas(Schema s1,Schema s2) {
      SchemaObject[] aLessB=s1.differenceByName(s2);
      SchemaObject[] bLessA=s2.differenceByName(s1);
      boolean integrationOK=true;

      if (aLessB.length>0) {
	System.out.println(s1.getName()+"-"+s2.getName()+"=");
	for (int i=0;i<aLessB.length;i++)
	  System.out.println("  "+aLessB[i]);
	integrationOK=false;
      }
	  
      if (bLessA.length>0) {
	System.out.println(s2.getName()+"-"+s1.getName()+"=");
	for (int i=0;i<bLessA.length;i++)
	  System.out.println("  "+bLessA[i]);
	integrationOK=false;
      }

      if (integrationOK) {	
	try {
	  Transformation.createIdentTransformation(s1,s2,null,null);
	  System.out.println("Integration of "+s1.getName()+" and "+
			     s2.getName()+" succeeded");
	}
	catch (SchemasNotIdenticalException e) {
	  System.out.println(e.toString());
	  e.printStackTrace(System.out);
	  System.out.println("================");
	  System.out.println("*** Schema " + e.getFromSchema().getName());
	  System.out.println(e.getFromSchema().getAllSchemeNamesString(true, -1, -1));
	  //	  s1.listSchema();
	  System.out.println("----------------");
	  System.out.println("*** Objects not matched");
	  SchemaObject[] fromSo = e.getFromObjectsUnmatched();
	  for (int i=0; i<fromSo.length; i++)
	    System.out.println(fromSo[i].getSchemeNamesString(-1, -1));
	  System.out.println("================");
	  System.out.println("*** Schema " + e.getToSchema().getName());
	  System.out.println(e.getToSchema().getAllSchemeNamesString(true, -1, -1));
	  //	  s2.listSchema();
	  System.out.println("----------------");
	  System.out.println("*** Objects not matched");
	  SchemaObject[] toSo = e.getToObjectsUnmatched();
	  for (int i=0; i<toSo.length; i++)
	    System.out.println(toSo[i].getSchemeNamesString(-1, -1));

	  System.out.println("================");
	}
	catch (Throwable t) {
	  System.out.println("================");
	  System.out.println("*** Schema " + s1.getName());
	  System.out.println(s1.getAllSchemeNamesString(true, -1, -1));
	  System.out.println("----------------");
	  System.out.println("*** Schema " + s2.getName());
	  System.out.println(s2.getAllSchemeNamesString(true, -1, -1));
	  System.out.println("================");
	  System.out.println(t.toString());
	  t.printStackTrace(System.out);
	}
      }
      else
	System.out.println("Integration failed, since it is "+
			   "only allowed if schemas identical");
  }

  public static void main(String[] args) {
    try {
      System.out.println("AutoMed Repository: Transformation and "+
			 "Integration Demo");

      // level 5 prints all the old debug information, 0 makes it silent
      STR.setDebugLevel(0);

      Schema s1=Schema.getSchema("er_s1");
      Schema s2=Schema.getSchema("er_s2");
      Schema s3=Schema.getSchema("er_s3");

      /*
      System.out.println("==================================================");
      System.out.println(s1.getName());
      System.out.println(s1.getAllSchemeNamesString(true, -1, -1));    
      System.out.println("--------------------------------------------------");
      System.out.println(s2.getName());
      System.out.println(s2.getAllSchemeNamesString(true, -1, -1));    
      System.out.println("--------------------------------------------------");
      System.out.println(s3.getName());
      System.out.println(s3.getAllSchemeNamesString(true, -1, -1));    
      System.out.println("==================================================");
      */

      Schema s4a=transforms1Tos4(s1);
      s4a.listSchema();
      Schema s4b=transforms2Tos4(s2); 
      s4b.listSchema();
      Schema s4c=transforms3Tos4(s3);
      s4c.listSchema();

      /*
      System.out.println("==================================================");
      System.out.println(s4a.getName());
      System.out.println(s4a.getAllSchemeNamesString(true, -1, -1));    
      System.out.println("--------------------------------------------------");
      System.out.println(s4b.getName());
      System.out.println(s4b.getAllSchemeNamesString(true, -1, -1));    
      System.out.println("--------------------------------------------------");
      System.out.println(s4c.getName());
      System.out.println(s4c.getAllSchemeNamesString(true, -1, -1));    
      System.out.println("==================================================");
      */

      integrateSchemas(s4a,s4b);
      integrateSchemas(s4b,s4c); 
      
    }
    catch (AutoMedException ame) {
      ame.printStackTrace();
    }
  }
}
