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

/*
 * An application to integrate the three versions of the University
 * database held in the sql92 datamodel.
 */
public class StudentCourseIntegration {
  static String username="pjm";
  static String password=null;
  static String driver="org.postgresql.Driver";
  static String urlbase="jdbc:postgresql://localhost/";
  static Model sql92=null;
  static Construct table=null;
  static Construct column=null;
  static Construct pk=null;
  static Construct fk=null;
  static SQLWrapperFactory wf;
  static boolean extra=false;


  public static Schema transforms3Tosg(Schema s3) 
    throws NotFoundException,TypeMismatchException,IntegrityException, TemplateDefinitionException,Exception 
	{

  		SchemaObject student=s3.getSchemaObject("<<student>>");
		SchemaObject studentlevel = s3.getSchemaObject("<<student,level>>");
		SchemaObject studentname = s3.getSchemaObject("<<student,name>>");

		CompositeTransformation template1 = CompositeTransformation.getCompositeTransformation("Field to Hierarchy Equivalence (sql_2 model, no foreign key creation)");
		TTemplateInputArgumentList argumentList1 = new TTemplateInputArgumentList(template1);
		argumentList1.addArgument(s3);
		argumentList1.addArgument(student);
		argumentList1.addArgument(studentlevel);
		argumentList1.addArgument(studentname);
		argumentList1.addArgument(new String[]{"ug","pg"});
		argumentList1.addArgument(new String[]{"ug","pg"});
		CompositeTransformationRecord intermediateRecord = argumentList1.executeTransformation();
		Schema intermediateSchema = intermediateRecord.getFinalSchema();


		SchemaObject studentppt = intermediateSchema.getSchemaObject("<<student,ppt>>");
		SchemaObject ug = intermediateSchema.getSchemaObject("<<ug>>");	

		CompositeTransformation template2 = CompositeTransformation.getCompositeTransformation("Parent Field to Specialisations Equivalence (sql_2 model)");
		TTemplateInputArgumentList argumentList2 = new TTemplateInputArgumentList(template2);
		argumentList2.addArgument(intermediateSchema);
		argumentList2.addArgument(student);
		argumentList2.addArgument(studentppt);
		argumentList2.addArgument(new SchemaObject[]{ug});
		CompositeTransformationRecord finalRecord = argumentList2.executeTransformation();
		return finalRecord.getFinalSchema();
  }

  // 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.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 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-1)+" "+title+
			 (len%2==0 ? " " : "  ")+
			 stars.substring(0,(slen-len)/2));      
    System.out.println(stars);
  }

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

  private static boolean bombOut=false;

  private static Schema verboseGetSchema(String db,String name) 
  throws Exception {
    // Ensure we are clear of all the schemas we are to create
    String sourceName=name+"_src";
    if (Schema.exists(name))
      Schema.getSchema(name).retract();
    if (Schema.exists(sourceName))
      Schema.getSchema(sourceName).retract();

    // Get the Source oriented schema
    AutoMedWrapper sw=
      AutoMedWrapper.selectNewAutoMedWrapper(username,password,null,driver,
					     urlbase+db,sourceName,wf);
    Schema sourceSchema=sw.getSchema();
    banner(sourceSchema);
    sourceSchema.listSchema();

    // Generate an AutoMed oriented schema
    Schema s=sw.newAutoMedSchema(name);
    banner(s);
    s.listSchema();    
    
    return s;
  }

  static void printHelp() {
    System.out.println("Usage: UniversityDatabaseIntegration [ options ]\n"+
      "  -debug <0-10>         set debug level\n" +
      "  -url <dbURL>          URL where DB located\n" +
      "  -driver <jdbcDriverClass>\n" +
      "  -user <user>\n"+
      "  -password <password>\n");
  }

  public static void readArgs(String[] args) {
    int i=0;

    try {
      while (i<args.length) {     	
	if (args[i].charAt(0)!='-') {
	  System.out.println("Error at "+args[i]+"\n");
	  printHelp();
	  System.exit(10);
	} 
	else if (args[i].startsWith("-debug")) 
	  STR.setDebugLevel(Integer.parseInt(args[++i]));
	else if (args[i].startsWith("-url")) 
	  urlbase=args[++i];
	else if (args[i].startsWith("-help")) 
	  printHelp();
	else if (args[i].startsWith("-driver")) 
	  driver=args[++i];
	else if (args[i].startsWith("-user")) 
	  username=args[++i];
	else if (args[i].startsWith("-password")) 
	  password=args[++i];
	
	++i;
      }

      if (STR.atDebugLevel(5))
	System.out.println("Finished reading arguments, "+
			   "about to read database");

    }
    catch (ArrayIndexOutOfBoundsException e) {
      System.out.println("You didn't supply an argument to the "
			 +args[i-1]+" option!");
      printHelp();
      System.exit(10);
    }
  }

  public static void main(String[] args) {
    readArgs(args);

    try {
      System.out.println("AutoMed Repository: "+
			 "Integration of Student and Course Data");
      wf=new SQLWrapperFactory();
      wf.setFeatures(SQLWrapperFactory.PRIMARY_KEY);

      Schema s3=verboseGetSchema("lab_bank_customer","sc_s3");

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

      sql92=wf.getAutoMedModel();
      table=sql92.getConstruct(SQLWrapperFactory.AUTOMED_SQL_MODEL_TABLE);
      column=sql92.getConstruct(SQLWrapperFactory.AUTOMED_SQL_MODEL_COLUMN);
      if (wf.isFeatureInUse(SQLWrapperFactory.PRIMARY_KEY))
	pk=sql92.getConstruct(SQLWrapperFactory.AUTOMED_SQL_MODEL_PRIMARY_KEY);
      if (wf.isFeatureInUse(SQLWrapperFactory.FOREIGN_KEY))
        fk=sql92.getConstruct(SQLWrapperFactory.AUTOMED_SQL_MODEL_FOREIGN_KEY);


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


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