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 produce in AutoMed the example in
 * the ICDE03 paper by McBrien and Poulovassilis
 *
 * Peter McBrien (pjm@doc.ic.ac.uk)
 */

public class ICDE03 {
  private static boolean testMode=false;
  static Model sql92=null;
  static Construct table=null;
  static Construct field=null;
  //static Construct pk=null;
  //static Construct fk=null;

  public static Schema transformsuTosg(Schema su)
  throws TypeMismatchException, IntegrityException {
    // Check that we have the objects that we want to transform
    SchemaObject ug=su.getSchemaObjectByName("<<ug>>");
    SchemaObject ug_id=su.getSchemaObjectByName("<<ug,id>>");
    SchemaObject ug_name=su.getSchemaObjectByName("<<ug,name>>");
    SchemaObject ug_left_on=su.getSchemaObjectByName("<<ug,left_on>>");
    SchemaObject ug_degree=su.getSchemaObjectByName("<<ug,degree>>");
    SchemaObject ug_sno=su.getSchemaObjectByName("<<ug,sno>>");
    SchemaObject tutor=su.getSchemaObjectByName("<<tutor>>");
    SchemaObject tutor_sno=su.getSchemaObjectByName("<<tutor,sno>>");
    SchemaObject tutor_sname=su.getSchemaObjectByName("<<tutor,sname>>");
    SchemaObject phd=su.getSchemaObjectByName("<<phd>>");
    SchemaObject phd_id=su.getSchemaObjectByName("<<phd,id>>");
    SchemaObject phd_name=su.getSchemaObjectByName("<<phd,name>>");
    SchemaObject phd_left_on=su.getSchemaObjectByName("<<phd,left_on>>");
    SchemaObject phd_title=su.getSchemaObjectByName("<<phd,title>>");
    SchemaObject supervises=su.getSchemaObjectByName("<<supervises>>");
    SchemaObject supervises_sno=su.getSchemaObjectByName("<<supervises,sno>>");
    SchemaObject supervises_id=su.getSchemaObjectByName("<<supervises,id>>");
    SchemaObject supervisor=su.getSchemaObjectByName("<<supervisor>>");
    SchemaObject supervisor_sno=su.getSchemaObjectByName("<<supervisor,sno>>");
    SchemaObject supervisor_sname=su.getSchemaObjectByName("<<supervisor,sname>>");
    SchemaObject supervisor_dept=su.getSchemaObjectByName("<<supervisor,dept>>");

    if (ug==null || phd==null || tutor==null 
        || supervises==null || supervisor==null) {
      System.out.println("Schema "+su.getName()+" does not contain "+
			 "<<ug>>, <<tutor>>, <<phd>>, "+
			 "<<supervises>> and <<supervisor>>");
      return null;
    }

    Schema sua=su.applyAddTransformation(table,new Object[]{"student"},
	"<<ug>> ++ <<phd>>",null);
    SchemaObject student=
      Transformation.getTransformation(su,sua).getSchemaObject(sua);
    Schema sub=sua.applyAddTransformation(field,new Object[]{student,"id"},
        "[(x,x) | x <-- <<student>>]",null);
    Schema suc=sub.applyAddTransformation(field,new Object[]{student,"name"},
	"<<phd,name>> ++ "+
        "[(x,y) | (x,y) <-- <<ug,name>>; not (member <<phd>> x)]",null);
    Schema sud=
      suc.applyAddTransformation(field,new Object[]{student,"left_on"},
	"<<phd,left_on>> ++ "+
        "[(x,y) | (x,y) <-- <<ug,left_on>>; not (member <<phd>> x)]",null);
    Schema sue=sud.applyAddTransformation(field,new Object[]{student,"degree"},
	"[(x,y) | x <-- <<phd>>; y='phd'] ++ "+
        "[(x,y) | (x,y) <-- <<ug,degree>>; not (member <<phd>> x)]",
					 null);
    Schema suf=sue.applyAddTransformation(table,new Object[]{"staff"},
	"<<tutor>> ++ <<supervisor>>",null);
    SchemaObject staff=
      Transformation.getTransformation(sue,suf).getSchemaObject(suf);
    Schema sug=suf.applyAddTransformation(field,new Object[]{staff,"sno"},
	"[(x,x) | x <-- <<staff>>]",null);
    Schema suh=sug.applyAddTransformation(field,new Object[]{staff,"sname"},
	"<<supervisor,sname>> ++ "+
        "[(x,y) | (x,y) <-- <<tutor,sname>>; not (member <<supervisor>> x)]",
					 null);
    Schema suha=suh.applyAddTransformation(field,new Object[]{staff,"dept"},
	"<<supervisor,dept>> ++ [(x,Null) | "+
               "x <-- <<tutor>>; not (member <<supervisor>> x)]",null);
    Schema sui=suha.applyAddTransformation(table,new Object[]{"monitors"},
	"<<supervises>> ++ "+
	"[(y,x) | (y,x) <-- <<ug,sno>>; not (member <<supervisor>> x)]",
					 null);
    SchemaObject monitors=
      Transformation.getTransformation(suha,sui).getSchemaObject(sui);
    Schema suj=sui.applyAddTransformation(field,new Object[]{monitors,"id"},
        "[(x,y,y) | (x,y) <-- <<monitors>>]",null);
    Schema suk=suj.applyAddTransformation(field,new Object[]{monitors,"sno"},
        "[(x,y,x) | (x,y) <-- <<monitors>>]",null);

    // Delete tutor relation
    Schema sul=suk.applyContractTransformation(tutor_sname,
        "[(x,y) | (x,y) <-- <<staff,sname>>; x <-- <<tutor>>]",null);
    Schema sum=sul.applyContractTransformation(tutor_sno,
        "[(x,y) | (x,y) <-- <<staff,sno>>; (z,x) <-- <<ug,sno>>]",null);
    Schema sun=sum.applyContractTransformation(tutor,
        "[ x | x <-- <<staff>>; (z,x) <-- <<ug,sno>>]",null);
    Schema suo=sun.applyContractTransformation(ug_name,
        "[(x,y) | (x,y) <-- <<student,name>>; x <-- <<ug>>]",null);
    Schema sup=suo.applyContractTransformation(ug_left_on,
        "[(x,y) | (x,y) <-- <<student,left_on>>; x <-- <<ug>>]",null);
    Schema suq=sup.applyContractTransformation(ug_degree,
        "[(x,y) | (x,y) <-- <<student,degree>>; x <-- <<ug>>]",null);
    Schema sur=suq.applyContractTransformation(ug_sno,
        "[(x,y) | (y,x) <-- <<monitors>>; x <-- <<ug>>]",null);
    Schema sus=sur.applyContractTransformation(ug_id,
        "[(x,y) | (x,y) <-- <<student,id>>; x <-- <<ug>>]",null);
    Schema sut=sus.applyContractTransformation(ug,
        "[ x | x <-- <<student>>; \\+ (x,'phd') <-- <<student,degree>>]",null);
    Schema suu=sut.applyContractTransformation(supervises_sno,
        "[ (x,y,x) | (x,y) <-- <<supervises>>",null);
    Schema suv=suu.applyContractTransformation(supervises_id,
        "[ (x,y,y) | (x,y) <-- <<supervises>>",null);
    Schema suw=suv.applyContractTransformation(supervises,
        "[ (x,y) | (x,y) <-- <<monitors>>; y <-- <<phd>>",null);
    Schema sux=suw.applyContractTransformation(supervisor_sname,
        "[(x,y) | (x,y) <-- <<staff,sname>>; x <-- <<supervisor>>]",null);
    Schema suy=sux.applyDeleteTransformation(supervisor_dept,
        "[(x,y) | (x,y) <-- <<staff,dept>>; x <-- <<supervisor>>]",null);
    Schema suz=suy.applyDeleteTransformation(supervisor_sno,
        "[(x,y) | (x,y) <-- <<staff,sno>>; x <-- <<supervisor>>]",null);
    Schema suaa=suz.applyDeleteTransformation(supervisor,
        "[ x | (x,y) <-- <<staff,dept>>; y<>null]",null);
    Schema suab=suaa.applyDeleteTransformation(phd_name,
        "[(x,y) | (x,y) <-- <<student,name>>; x <-- <<phd>>]",null);
    Schema suac=suab.applyDeleteTransformation(phd_left_on,
        "[(x,y) | (x,y) <-- <<student,left_on>>; x <-- <<ug>>]",null);
    Schema suad=suac.applyContractTransformation(phd_title,null,null);
    Schema suae=suad.applyDeleteTransformation(phd_id,
        "[(x,y) | (x,y) <-- <<student,id>>; x <-- <<phd>>]",null);
    Schema suaf=suae.applyDeleteTransformation(phd,
        "[ x | (x,'phd') <-- <<student,degree>>]",null,"sg");
    return suaf;
  }

  public static void banner(String title) {
    int len=title.length();
    String stars="******************************************************************************";
    int slen=stars.length();

    System.out.println(stars);
    if (len>slen-4)
      System.out.println(title);
    else 
      System.out.println(stars.substring(0,(slen-len-2)/2)+" "+title+
			 (len%2==0 ? " " : "  ")+
			 stars.substring(0,(slen-len-2)/2));      
    System.out.println(stars);
  }

  public static void banner(Schema s) {
    banner(s.getName());
  }

  private static boolean bombOut=false;

  private static Schema verboseGetSchema(String name) 
  throws Exception {
    Schema s=Schema.getSchema(name);

    if (s==null) {
      System.out.println("Schema "+name+" missing from repository");
      bombOut=true;
    }
    else {
      banner(s);
      //s.write();   
      s.listSchema();
    }
    
    return s;
  }

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

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

      Schema ug=verboseGetSchema("ug");
      Schema phd=verboseGetSchema("phd");
      if (testMode && Schema.exists("su")) {
      	System.out.println("Retracting old su");
      	Schema.getSchema("su").retract();
      }

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

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

      System.out.println("Perform union");
      Schema su=ug.union(phd);
      if (testMode) {
	System.out.println("Schema has name "+su.getName());
	su.setName("su");
      }
      banner(su);
      su.listSchema();

      banner("ug-->su");
      TransformationPathway.findShortestPath(ug,su).write();
      banner("phd-->su");
      TransformationPathway.findShortestPath(phd,su).write();
      banner("su");
      su.listSchema();

      Schema sg=transformsuTosg(su);
      banner("su-->sg");
      TransformationPathway.findShortestPath(su,sg).write();
      banner(sg);
      sg.listSchema();
    }
    catch (Throwable t) {
      System.err.println(t.toString());
      t.printStackTrace();
    }
  }
}


