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 to link schemas in YATTA model together 
 * via transformation pathways 
 */

// NOTE: The transformation here doesn't take patterns into consideration. 
//       We just want to test merging schemas in YATTA model. 

public class DefineTransformations {
  public static Schema transformY1ToY3(Schema y1) 
    throws NotFoundException, TypeMismatchException {
    Model yatta=Model.getModel("yatta");
    Construct rootnode=yatta.getConstruct("rootnode");
    Construct yattanode=yatta.getConstruct("yattanode");

    // Retrieve the objects of the schema
    SchemaObject y1_root=y1.getSchemaObject("<<root>>");
    SchemaObject y1_student=
	y1.getSchemaObject("<<student,root>>");
    SchemaObject y1_name=
	y1.getSchemaObject("<<name,<<student,root>>>>");
    SchemaObject y1_course=
      y1.getSchemaObject("<<course,<<student,root>>>>");
    SchemaObject y1_code=
      y1.getSchemaObject("<<code,<<course,<<student,root>>>>>>");
    SchemaObject y1_year=
      y1.getSchemaObject("<<year,<<course,<<student,root>>>>>>");
    SchemaObject y1_grade=
      y1.getSchemaObject("<<grade,<<course,<<student,root>>>>>>");

    //********************************************//
    // Start the transformation steps Y1 -> Y2    //
    //********************************************//

    //Growing Phrase
    
    // ADD "course" node
    Transformation t2_1 = 
	Transformation.createAddTransformation(y1, yattanode, 
					       new Object[] {"course","list","*","1",y1_root},
								 //"{x,0 | <<student,root>>(z,0) && <<course,root,<<student,root>>>>>>(w,z) && <<code,string,-,1,root,<<student,root>>,<<course,root,<<student,root>>>>>>(y,w) && x=generateId(0,y)}",
								 "[{x} | {s1,r} <- <<student,root>>; {c1,s2} <- <<course,root,<<student,root>>>>; {co,c2} <- <<code,<<course,<<student,root>>>>>>; (=) s1 s2; (=) c1 c2; x <- [generateId r co]]", 
								 null);
    Schema y1a=t2_1.getOtherSchema(y1);
    SchemaObject y2_course=t2_1.getSchemaObject(y1a);

    //ADD "code" node
    Transformation t2_2 = Transformation.createAddTransformation(y1a, yattanode,
								 new Object[] {"code","string","-","1",y2_course},
								 //"{y,x | <<student,root>>(z,0) && <<course,root,<<student,root>>>>>>(w,z) && <<code,string,-,1,root,<<student,root>>,<<course,root,<<student,root>>>>>>(y,w) && x=generateId(0,y)}",
								 "[{co,x} | {s1,r} <- <<student,root>>; {c1,s2} <- <<course,<<student,root>>>>; {co,c2} <- <<code,<<course,<<student,root>>>>>>; (=) s1 s2; (=) c1 c2; x <- [generateId r co]]", 
								 null);
    Schema y1b=t2_2.getOtherSchema(y1a);

    //ADD "student" node
    Transformation t2_3 = Transformation.createAddTransformation(y1b, yattanode,
								 new Object[] {"student","list","+","2",y2_course},
								 //"{x,y | <<student,root>>(z,0) && <<course,root,<<student,root>>>>>>(w,z) && <<code,string,-,1,root,<<student,root>>,<<course,+,1,root,<<student,root>>>>>>(u,w) && <<name,string,-,1,root,<<student,root>>>>(v,z) && x=generateId(u,v) && y=generateId(0,u)}",
								 "[{x,y} | {s1,r} <- <<student,root>>; {c1,s2} <- <<course,<<student,root>>>>; (=) s1 s2; {n,s3} <- <<name,<<student,root>>>>; (=) s2 s3; {co,c2} <- <<code,<<course,<<student,root>>>>>>; (=) c1 c2;  x <- [generateId co n]; y <- [generateId r co]]",
								 null);
    Schema y1c=t2_3.getOtherSchema(y1b);
    SchemaObject y2_student=t2_3.getSchemaObject(y1c);

    //ADD "name" node
    Transformation t2_4 = Transformation.createAddTransformation(y1c, yattanode,
								 new Object[] {"name","string","-","1",y2_student},
								 //"{y,x | <<student,root>>(z,0) && <<course,root,<<student,root>>>>>>(w,z) && <<code,string,-,1,root,<<student,root>>,<<course,+,1,root,<<student,root>>>>>>(u,w) && <<name,string,-,1,root,<<student,root>>>>(y,z) && x=generateId(u,y)}",
								 "[{n,x} | {s1,r} <- <<student,root>>; {c1,s2} <- <<course,<<student,root>>>>; (=) s1 s2; {n,s3} <- <<name,<<student,root>>>>; (=) s2 s3; {co,c2} <- <<code,<<course,<<student,root>>>>>>; (=) c1 c2; x <- [generateId co n]]",
								 null);
    Schema y1d=t2_4.getOtherSchema(y1c);

    //ADD "year" node
    Transformation t2_5 = Transformation.createAddTransformation(y1d, yattanode,
								 new Object[] {"year","integer","","2", y2_student},
								 //"{y,x | <<student,root>>(z,0) && <<course,root,<<student,root>>>>(w,z) && <<code,string,-,1,root,<<student,root>>,<<course,+,1,root,<<student,root>>>>>>(u,w) && <<name,string,-,1,root,<<student,root>>>>(v,z) && x=generateId(u,v) && <<year,integer, ,2,root,<<student,root>>,<<course,root,<<student,root>>>>>>(y,w)}",
								 "[{y,x} | {s1,r} <- <<student,root>>; {c1,s2} <- <<course,root,<<student,root>>>>; {co,c2} <- <<code,<<course,<<student,root>>>>>>; {n,s3} <- <<name,root,<<student,root>>>>; (=) s1 s2; (=) s2 s3; (=) c1 c2; x <- [generateId co n]; {y,c3} <- <<year,<<course,<<student,root>>>>>>; (=) c2 c3]",
								 null);
    Schema y1e=t2_5.getOtherSchema(y1d);

    //ADD "grade" node
    Transformation t2_6 = Transformation.createAddTransformation(y1e, yattanode,
								 new Object[] {"grade","string","","3", y2_student},
								 //"{y,x | <<student,root>>(z,0) && <<course,root,<<student,root>>>>(w,z) && <<code,string,-,1,root,<<student,root>>,<<course,+,1,root,<<student,root>>>>>>(u,w) && <<name,string,-,1,root,<<student,root>>>>(v,z) && x=generateId(u,v) && <<grade,string, ,3,root,<<student,root>>,<<course,root,<<student,root>>>>>>(y,w)}",
								 "[{g,x} | {s1,r} <- <<student,root>>; {c1,s2} <- <<course,<<student,root>>>>; {co,c2} <- <<code,<<course,<<student,root>>>>>>; {n,s3} <- <<name,root,<<student,root>>>>; (=) s1 s2; (=) s2 s3; (=) c1 c2; x <- [generateId co n]; {g,c3} <- <<grade,<<course,<<student,root>>>>>> ; (=) c2 c3]",
								 null);
    Schema y1f=t2_6.getOtherSchema(y1e);

    //Shrinking Phrase

    //DELETE "grade" node
    Transformation t2_7 = Transformation.createDeleteTransformation(y1f, y1_grade,
								    //"{x,y | <<course,root>>(z,0) && <<code,string,-,1,root,<<course,root>>>>(w,z) && <<student,root,<<course,root>>>>(u,w) && <<grade,string,root,<<student,root>>,<<course,root,<<student,root>>>>>>(x,u) && <<name,string,-,1,root,<<course,root>>,<<student,root,<<course,root>>>>>>(v,u) && y=generateId(v,w)}",
								    "[{g,x} | {c1,r} <- <<course,root>>; {co, c2} <- <<code,root,<<course,root>>>>; {s1,c3} <- <<student,root,<<course,root>>>>; {g,c4} <- <<grade,root,<<course,root>>,<<student,root,<<course,root>>>>>>; {n,s2} <- <<name,root,<<course,root>>,<<student,root,<<course,root>>>>>>; (=) c1 c2; (=) c2 c3; (=) c3 c4; (=) s1 s2; y <- [generateId n co]]",
								 null);
    Schema y1g=t2_7.getOtherSchema(y1f);

    //DELETE "year" node
    Transformation t2_8 = Transformation.createDeleteTransformation(y1g, y1_year,
								    //"{x,y | <<course,root>>(z,0) && <<code,string,-,1,root,<<course,root>>>>(w,z) && <<student,root,<<course,root>>>>(u,w) && <<year,integer,_,2,root,<<student,root>>,<<course,root,<<student,root>>>>>>(x,u) && <<name,string,-,1,root,<<course,root>>,<<student,root,<<course,root>>>>>>(v,u) && y=generateId(v,w)} ",
								    "[{y,x} | {c1,r} <- <<course,root>>; {co, c2} <- <<code,<<course,root>>>>; {s1,c3} <- <<student,<<course,root>>>>; {y,c4} <- <<year,<<student,<<course,root>>>>>>; {n,s2} <- <<name,<<student,<<course,root>>>>>>; (=) c1 c2; (=) c2 c3; (=) c3 c4; (=) s1 s2; y <- [generateId n co]]",
								 null);
    Schema y1h=t2_8.getOtherSchema(y1g);

    //DELETE "code" node
    Transformation t2_9 = Transformation.createDeleteTransformation(y1h, y1_code, 
								    //"{x,y | <<course,root>>(z,0) && <<code,string,-,1,root,<<course,root>>>>(x,z) && <<student,root,<<course,root>>>>(w,z) && <<name,string,-,1,root,<<course,root>>,<<student,root,<<course,root>>>>>>(u,w) && y=generateId(x,u)}",
								    "[{co,x} | {c1,r} <- <<course,root>>; {co, c2} <- <<code,<<course,root>>>>; {s1,c3} <- <<student,<<course,root>>>>; {n,s2} <- <<name,<<student,<<course,root>>>>>>; (=) c1 c2; (=) c2 c3; (=) c3 c4; (=) s1 s2; y <- [generateId co n]]",null);
    Schema y1i=t2_9.getOtherSchema(y1h);

    //DELETE "course" node
    Transformation t2_10 = Transformation.createDeleteTransformation(y1i, y1_course, 
								     //"{x,y | <<course,root>>(z,0) && <<code,string,-,1,root,<<course,root>>>>(w,z) && <<student,root,<<course,root>>>>(u,z) && <<name,string,-,1,root,<<course,root>>,<<student,root,<<course,root>>>>>>(v,u) && x=generateId(w,v) && y=generateId(0,v)}",
								     "[{x,y} | {c1,r} <- <<course,root>>; {co,c2} <- <<code,<<course,root>>>>; {s1,c3} <- <<student,<<course,root>>>>; {n,s2} <- <<name,<<student,<<course,root>>>>>>; (=) c1 c2; (=) c2 c3; (=) s1 s2; x <- [generateId co n]; y <- [generateId r n]]",null);
    Schema y1j=t2_10.getOtherSchema(y1i);

    //DELETE "name" node
    Transformation t2_11 = Transformation.createDeleteTransformation(y1j, y1_name,
								     //"{y,x | <<course,root>>(z,0) && <<student,root,<<course,root>>>>(w,z) && <<name,string,-,1,root,<<course,root>>,<<student,root,<<course,root>>>>>>(y,w) && x=generateId(0,y)}",
								     "[{n,x} | {c1,r} <- <<course,root>>; {s1,c2} <- <<student,<<course,root>>>>; {n,s2} <- <<name,<<student,<<course,root>>>>>>; (=) c1 c2; (=) s1 s2; x <- [generateId r n]]",null);
    Schema y1k=t2_11.getOtherSchema(y1j);

    //DELETE "student" node
    Transformation t2_12 = Transformation.createDeleteTransformation(y1k, y1_student,
								     //"{x,0 | <<course,root>>(z,0) && <<student,root,<<course,root>>>>(w,z) && <<name,string,-,1,root,<<course,root>>,<<student,root,<<course,root>>>>>>(y,w) && x=generateId(0,y)}",
								     "[{x} | {c1,r} <- <<course,root>>; {s1,c2} <- <<student,<<course,root>>>>; {n,s2} <- <<name,<<student,<<course,root>>>>>>; (=) c1 c2; (=) s1 s2; x <- [generateId r n]]",null);
    Schema y1l=t2_12.getOtherSchema(y1k);

    return (y1l);
  }


  // 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 {
      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.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 of YATTA (y1 -> y2)");

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

      Schema y1=Schema.getSchema("y1");
      Schema y2=Schema.getSchema("y2");

      Schema y3=transformY1ToY3(y1);
      y3.listSchema();

      integrateSchemas(y3,y2);
    }
    catch (AutoMedException ame) {
      ame.printStackTrace();
    }
    catch (Throwable t) {
      System.err.println(t.toString());
      t.printStackTrace();
    }
  }
}
