#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 integrate the three versions of the University
 * database held in the sql92 datamodel.
 */

public class UniversityDatabaseIntegration {
  static Model sql92=null;
  static Construct table=null;
  static Construct field=null;
  static Construct pk=null;
  static Construct fk=null;

  public static Schema transforms1Tosg(Schema s1) 
    throws AutoMedException,SQLException,IOException{

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

    if (person==null || personsex==null || personid==null) {
      System.out.println("Schema "+s1.getName()+" does not contain "+
			 "<<person>>, <<person,sex>> and <<person,id>>");
      return null;
    }

    Schema s1a=s1.applyAddTransformation(table,
					 new Object[] {"male"},
					 "[x | (x,'M') <-- <<person,sex>>]",
					 null);
    SchemaObject male=Transformation.getTransformationToObject(s1,s1a);
    Schema s1b=s1a.applyAddTransformation(field,
					 new Object[] {male,"id"},
					 "[(x,x) | x <-- <<male>>]",
					 null);
    SchemaObject maleid=Transformation.getTransformationToObject(s1a,s1b);
    Schema s1c=s1b.applyAddTransformation(table,
					  new Object[] {"female"},
					  "[x | (x,'F') <-- <<person,sex>>]",
					  null);
    SchemaObject female=Transformation.getTransformationToObject(s1b,s1c);
    Schema s1d=s1c.applyAddTransformation(field,
					 new Object[] {female,"id"},
					 "[(x,x) | x <-- <<female>>]",
					 null);
    SchemaObject femaleid=Transformation.getTransformationToObject(s1c,s1d);
    Schema s1e=
      s1d.applyAddTransformation
      (pk,new Object[] {"male_pk",male,maleid},null,null);
    Schema s1f=
      s1e.applyAddTransformation
      (fk,new Object[] {"male_isa_person_fk",male,maleid,person,personid},null,null);
    Schema s1g=
      s1f.applyAddTransformation
      (pk,new Object[] {"female_pk",female,femaleid},null,null);
    Schema s1h=
      s1g.applyAddTransformation
      (fk,new Object[] {"female_isa_person_fk",female,femaleid,person,personid},null,null);

    return s1h.applyDeleteTransformation(personsex,
					 "",
					 null);
  }

  public static Schema transforms2Tosg(Schema s2) 
    throws AutoMedException,SQLException,IOException {

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

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

    Schema s2a=s2.applyAddTransformation(table,
					 new Object[] {"dept"},
					 "[x | (_,x) <-- <<person,dname>>]",
					 null);
    SchemaObject dept=Transformation.getTransformationToObject(s2,s2a);
    Schema s2b=s2a.applyAddTransformation(field,
					 new Object[] {dept,"dname"},
					 "[(x,x) | x <-- <<dept>>]",
					 null);
    SchemaObject deptdname=Transformation.getTransformationToObject(s2a,s2b);
    Schema s2c=s2b.applyAddTransformation(pk,
					  new Object[] {"dept_pk",dept,deptdname},
					  null,
					  null);
    Schema s2d=s2c.applyAddTransformation(fk,
					  new Object[] {"person_worksin_dept_fk",person,
							persondname,dept,deptdname},
					  null,
					  null);

    return s2d;
  }

  public static Schema transforms3Tosg(Schema s3) 
    throws AutoMedException,SQLException,IOException {

    // 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,dcode>>");
    SchemaObject degreetitle=s3.getSchemaObjectByName("<<degree,title>>");
    SchemaObject degreedname=s3.getSchemaObjectByName("<<degree,dname>>");
    SchemaObject degreepk=s3.getSchemaObjectByName("<<degree_pk,degree,<<degree,dcode>>>>");
    SchemaObject runby=
      s3.getSchemaObjectByName("<<degree_runby_dept_fk,degree,<<degree,dname>>,dept,<<dept,dname>>>>");

    if (person==null || 
	degreecode==null ||
	degreetitle==null ||
	runby==null) {
      System.out.println("Schema "+s3.getName()+" does not contain "+
			 "<<person>>, "+
			 "<<degree>>, "+
			 "<<degree,name>>"+
			 "<<degree,dcode>>"+
			 "<<degree,title>>"+
			 "<<degree_pk,degree,<<degree,dcode>>>>"+
			 "<<degree_runby_dept_fk,degree,<<degree,dname>>,dept,<<dept,dname>>>>");
      return null;
    }

    Transformation t3_1=
      Transformation.createContractTransformation(s3,degreetitle);
    Schema s3a=t3_1.getOtherSchema(s3);
    Transformation t3_2=
      Transformation.createContractTransformation(s3a,degreecode);
    Schema s3b=t3_2.getOtherSchema(s3a);
    Transformation t3_3=
      Transformation.createContractTransformation(s3b,degreepk);
    Schema s3c=t3_3.getOtherSchema(s3b);
    Transformation t3_4=
      Transformation.createContractTransformation(s3c,degreedname);
    Schema s3d=t3_4.getOtherSchema(s3c);
    Transformation t3_5=
      Transformation.createContractTransformation(s3d,runby);
    Schema s3e=t3_5.getOtherSchema(s3d);
    Transformation t3_6=
      Transformation.createContractTransformation(s3e,degree);
    Schema s3f=t3_6.getOtherSchema(s3e);
    Transformation t3_7=
      Transformation.createExtendTransformation(s3f,field,
						new Object[]{person,
							     "name"});
    return t3_7.getOtherSchema(s3f);
  }


  // 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 banner(Schema s) {
    System.out.println("***********************************************");
    System.out.println("                    "+s.getName());
    System.out.println("***********************************************");
  }

  public static void main(String[] args) {
    try {
      System.out.println("AutoMed Repository: "+
			 "Integration of University Databases");

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

      Schema s1=Schema.getSchema("uni_s1");
      Schema s2=Schema.getSchema("uni_s2");
      Schema s3=Schema.getSchema("uni_s3");
      Schema s4=Schema.getSchema("uni_s4");
      Schema s5=Schema.getSchema("uni_s5");

      boolean bombOut=false;

      if (s1==null) {
	System.out.println("Schema uni_s1 missing from repository");
	bombOut=true;
      }

      if (s2==null) {
	System.out.println("Schema uni_s2 missing from repository");
	bombOut=true;
      }

      if (s3==null) {
	System.out.println("Schema uni_s3 missing from repository");
	bombOut=true;
      }

      if (s4==null) {
	System.out.println("Schema uni_s4 missing from repository");
	bombOut=true;
      }

      if (s5==null) {
	System.out.println("Schema uni_s5 missing from repository");
	bombOut=true;
      }

      if (bombOut) {
	System.out.println("Unable to perform integration.");
	System.exit(0);
      }

      banner(s1);
      s1.listSchema();
      banner(s2);
      s2.listSchema();
      banner(s3);
      s3.listSchema();
      banner(s4);
      s4.listSchema();
      banner(s5);
      s5.listSchema();

      sql92=Model.getModel("sql92");
      table=sql92.getConstruct("table");
      field=sql92.getConstruct("field");
      pk=sql92.getConstruct("primary_key");
      fk=sql92.getConstruct("foreign_key");

      Schema sga=transforms1Tosg(s1);
      banner(sga);
      sga.listSchema();

      Schema sgb=transforms2Tosg(s2); 
      banner(sgb);
      sgb.listSchema();

      Schema sgc=transforms3Tosg(s3);
      banner(sgc);
      sgc.listSchema();


      System.out.println("\n\n============== "+
			 "Attempting Integration of Schemas "+
			 "==============\n\n");
      integrateSchemas(sga,sgb);
      integrateSchemas(sga,sgc);
      //integrateSchemas(sgb,sgc); 
    }
    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();
    }
  }
}
