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

/*
 * 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 AutoMedException,SQLException,IOException{
    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=s1.getSchemaObjectByName("<<person>>");
    SchemaObject personsex=s1.getSchemaObjectByName("<<person,sex,notnull>>");
    if (person==null || personsex==null) {
      System.out.println("Schema "+s1.getName()+" does not contain "+
			 "<<person>> and <<person,sex,notnull>>");
      System.out.println(s1.getAllSchemeNamesString(true, -1, -1));
      return null;
    }

    Schema s1a=s1.applyAddTransformation(ent,
					 new Object[] {"male"},
					 "male(X):-person-sex(X,male)",
					 null);
    Schema s1b=s1a.applyAddTransformation(ent,
					  new Object[] {"female"},
					  "female(X):-person-sex(X,female)",
					  null);
    Schema s1c=
      s1b.applyAddTransformation
      (gen,
       new Object[] { 
	 "sex",
	 "total",
	 person,
	 //	 Transformation.getTransformationToObject(s1,s1a),
	 Transformation.getTransformation(s1,s1a).getSchemaObject(s1a),
	 //	 Transformation.getTransformationToObject(s1a,s1b)
	 Transformation.getTransformation(s1a,s1b).getSchemaObject(s1b),
       },
	 null,
	 null);
    return s1c.applyDeleteTransformation(personsex,
					 "person-sex(X,Y) :- male(X),Y=m;"+
					 "female(X),Y=f",
					 null);
  }

  public static Schema transforms2Tos4(Schema s2) 
    throws AutoMedException,SQLException,IOException {
    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.getSchemaObjectByName("<<person>>");
    SchemaObject persondname=
      s2.getSchemaObjectByName("<<person,dname,notnull>>");

    if (persondname==null) {
      System.out.println("Schema "+s2.getName()+" does not contain "+
			 "<<person>> and <<person,dname,notnull>>");
      System.out.println(s2.getAllSchemeNamesString(true, -1, -1));
      return null;
    }

    Transformation t2_1=
      Transformation.createAddTransformation(s2,ent,
					     new Object[] {"dept"},
					     "dept(X):-person-dept(_,X)",
					     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"},
					     "dept-dname(X,X):-dept(X)",
					     null);
    Schema s2b=t2_2.getOtherSchema(s2a);
    Transformation t2_3=
      Transformation.createAddTransformation(s2b,rel,
					     new Object[] { 
					       "worksin",
					       person,
					       dept,
					       "1:1",
					       "1:N"
					     },
					     null,
					     null);
    Schema s2c=t2_3.getOtherSchema(s2b);
    Transformation t2_4=
      Transformation.createDeleteTransformation(s2c,
					     persondname,
					     "person-dname(X,Y) :- "+
						"person-worksin-dept(X,Y)",
					     null);
    return t2_4.getOtherSchema(s2c);
  }

  public static Schema transforms3Tos4(Schema s3) 
    throws AutoMedException,SQLException,IOException {
    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.getSchemaObjectByName("<<person>>");
    SchemaObject degree=s3.getSchemaObjectByName("<<degree>>");
    SchemaObject degreecode=s3.getSchemaObjectByName("<<degree,code,key>>");
    SchemaObject degreetitle=
      s3.getSchemaObjectByName("<<degree,title,notnull>>");
    SchemaObject runby=
      s3.getSchemaObjectByName("<<runby,degree,dept,1:1,1:N>>");

    if (person==null || degreecode==null) {
      System.out.println("Schema "+s3.getName()+" does not contain "+
			 "<<person>> and <<degree,code,key>>");
      System.out.println(s3.getAllSchemeNamesString(true, -1, -1));
      return null;
    }

    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 AutoMedException,SQLException,IOException{
    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.getSchemaObjectByName("<<sex>>");
    SchemaObject personsex=s1.getSchemaObjectByName("<<_,person,sex>>");
    if (sex==null || personsex==null) {
      System.out.println("Schema "+s1.getName()+" does not contain all of"+
			 "<<sex>> and <<_,person,sex>>");
      System.out.println(s1.getAllSchemeNamesString(true, -1, -1));
      return null;
    }

    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)  
    throws AutoMedException,SQLException,IOException {
      SchemaObject[] aLessB=s1.differenceByName(s2);
      SchemaObject[] bLessA=s2.differenceByName(s1);
      boolean integrationOK=true;

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

      if (integrationOK) {	
	try {
	  Transformation.createIdentTransformations(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("==================================================");
      */

      // We will go for double angle brackets to represent the chevrons in
      // this application
      // SchemaTransformationRepository.setOpenSchemeBracket("<<");
      // SchemaTransformationRepository.setCloseSchemeBracket(">>");

      // Build an upto date hashmap of scheme names, using the 
      // format "<<person>>", "<<person,name>>" etc. This is
      // used by the subsequent transformation steps
      // s1.updateSchemaObjectNameHashMap();
      // s2.updateSchemaObjectNameHashMap();
      // s3.updateSchemaObjectNameHashMap();

      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 (SQLException se) { 
      se.printStackTrace();
    }
    catch (IOException ie) {
      ie.printStackTrace();
    }
    catch (AutoMedException ame) {
      ame.printStackTrace();
    }
    catch (Throwable t) {
      System.err.println(t.toString());
      t.printStackTrace();
    }
  }
}
