/**
 * XMLSQLMerge.java
 *
 *
 *
 * @author Andrew Charles Smith
 *
 * Created on 09 March 2006
 * Department of Computing, Imperial College
 */
import uk.ac.ic.doc.automed.*;
import uk.ac.ic.doc.automed.util.Debugger;
import uk.ac.ic.doc.automed.util.Logger;

import uk.ac.ic.doc.automed.wrappers.AutoMedWrapper;

import uk.ac.ic.doc.automed.reps.Construct;
import uk.ac.ic.doc.automed.reps.HDMToHighLevel;
import uk.ac.ic.doc.automed.reps.HighLevelToHDM;
import uk.ac.ic.doc.automed.reps.Model;
import uk.ac.ic.doc.automed.reps.Schema;
import uk.ac.ic.doc.automed.reps.SchemaObject;
import uk.ac.ic.doc.automed.reps.HDMTransformations;
import uk.ac.ic.doc.automed.reps.Transformation;
import uk.ac.ic.doc.automed.wrappers.AutoMedString;
import uk.ac.ic.doc.automed.wrappers.AutoMedType;
import uk.ac.ic.doc.automed.wrappers.SQLWrapperFactory;
import uk.ac.ic.doc.automed.wrappers.XMLSchemaWrapperFactory;
//import utils.AutoMedUtils;

public class XMLSQLMerge {
  
  // Location of XML file
  //private static String xmlFile = "file:/homes/acs203/DevAutoMed/automed/java/examples/data/sensorData.xml";
  private static String xmlFile = "file:E:/netbeansprogs/automed/examples/data/sensorData.xml";

  // Constants for SQL database name, driver, location, username and password
  private static String dbName = "vehicledata";
  private static String driver = "org.postgresql.Driver";
  private static String urlbase = "jdbc:postgresql://localhost/";
  private static String username = "automed";
  private static String password = "automed";  

  private static String uavTableName = "sensor";
  private static String sensorTableName = "databasedata";
  private static String sensorSrcSchemaName = "sensor";
  private static String sensorSchemaName = "sensor";
  private static String uavSrcSchemaName = "uav_src";  
  private static String uavSchemaName = "uav";
  
  /**
   * Creates a new instance of AutoMedPeerApplication
   */
  public XMLSQLMerge() {
  }


  public static void main(String args[]) throws Exception {
    
    Debugger.setSectionDebugLevel(args);
    DSRConfiguration.setDefaultConfigFileName(args);
      // Create an XMLSchema wrapper factory
      XMLSchemaWrapperFactory xwf=new XMLSchemaWrapperFactory();
      // Wrap the XML schema from vehicle A
      String driver = "uk.ac.ic.doc.automed.wrappers.XMLSchemaWrapper";
      if (Schema.exists(sensorSrcSchemaName)) Schema.retract(sensorSrcSchemaName);
      AutoMedWrapper swxml=
        AutoMedWrapper.selectNewAutoMedWrapper(username,password,null,driver,
                                           xmlFile,sensorSrcSchemaName,xwf);
      
    Schema sourceXMLSchema=swxml.getSchema();
    // Generate an AutoMed oriented schema
    Schema xmlSchema=swxml.newAutoMedSchema(sensorSchemaName);
    //Schema xmlSchema=sourceXMLSchema;

    // must reload all models for constructs in new model
    //Model.reloadModels();

    //xmlSchema.listSchema(); 

    // wrapping mediator's data source
    Schema sqlSchema = null;
    sqlSchema = wrapSQLDataSource();

    // integrate two schemas
    Schema intSchema = null;
    intSchema = integrateXML2SQL(xmlSchema, sqlSchema);

    }
    
    public static Schema wrapSQLDataSource() throws AutoMedException{
      // Create a SQL wrapper factory
      SQLWrapperFactory wf=new SQLWrapperFactory();
      wf.setFeatures(SQLWrapperFactory.PRIMARY_KEY);
      wf.setFeatures(SQLWrapperFactory.FOREIGN_KEY);
      wf.setFeatures(SQLWrapperFactory.DATA_TYPE);
      wf.setFeatures(SQLWrapperFactory.NULL_CONSTRAINT);

      // Wrap the SQL schema from the UAV
      
      if (Schema.exists(uavSrcSchemaName)) Schema.retract(uavSrcSchemaName);
      AutoMedWrapper sw=
        AutoMedWrapper.selectNewAutoMedWrapper(username,password,null,driver,
                                           urlbase+dbName,uavSrcSchemaName,wf);
      Schema sourceSQLSchema=sw.getSchema();
      // Generate an AutoMed oriented schema
      Schema sqlSchema=sw.newAutoMedSchema(uavSchemaName);

      // must reload all models for constructs in new model
      Model.reloadModels();
      
      return sqlSchema;
    }
    
    // integrate 2 compatible schemas
    public static Schema integrateXML2SQL(Schema xmlSchema, Schema sqlSchema) throws AutoMedException {
      
      // Convert the XML Schema to HDM and then to SQL so we can match it
      HighLevelToHDM hltohdm = new HighLevelToHDM(xmlSchema);
      
      Schema hdmSchema = hltohdm.TransformToHDM("xmlschema",0);
      //AutoMedUtils.banner("HDM version of XML Schema");
      // Transform the HDM from hierarchical to two level
      HDMTransformations trans = new HDMTransformations(hdmSchema);
      SchemaObject tempObject1 = hdmSchema.getSchemaObject("<<HDMdatabasedata>>");
      SchemaObject tempObject2 = hdmSchema.getSchemaObject("<<_,HDMdatabase,HDMdatabasedata>>");
      hdmSchema = trans.inclusionExpand(tempObject1, tempObject2);
      trans = new HDMTransformations(hdmSchema);
      tempObject1 = hdmSchema.getSchemaObject("<<HDMdatabase>>");
      hdmSchema = trans.idNodeExpand(tempObject1);
      trans = new HDMTransformations(hdmSchema);
      tempObject1 = hdmSchema.getSchemaObject("<<HDMdatabasedata>>");
      tempObject2 = hdmSchema.getSchemaObject("<<HDMdatabasedatatime>>");
      hdmSchema = trans.moveInclusion(tempObject1, tempObject2);
      // Now convert the HDM schema to SQL
      HDMToHighLevel hdmToSQL = new HDMToHighLevel(hdmSchema);
      Schema transformedSQLSchema = hdmToSQL.hdmToRelational();
      //AutoMedUtils.banner("New SQL Schema");

      // Now merge schema objects from the new sql schema with the original one
      // These will come from different peers.
      sqlSchema = merge(sqlSchema,transformedSQLSchema, null);
      //AutoMedUtils.banner("New merged schema");

      return sqlSchema;
    }
    
    private static Schema merge(Schema uavSchema, Schema sensorSchema, Schema controlSchema) throws AutoMedException {            
        Model sql=Model.getModel("sql_30");
        Construct column=sql.getConstruct("column");
        Construct pk = sql.getConstruct("primary_key");
        
        // Remove the database table - This is the root element table
        // First remove the unnecessary keys
        SchemaObject objectToRemove = sensorSchema.getSchemaObject(
            "<<databasedataIE_fk,databasedata,<<databasedata,databasedatatime,notnull,int4>>," + 
                "database,<<database,databasedataIE,notnull,anyRelType>>>>");
        sensorSchema = sensorSchema.applyContractTransformation(objectToRemove,null,null);
        objectToRemove = sensorSchema.getSchemaObject(
            "<<database_pk_pk,database,<<database,database_pk,notnull,anyRelType>>>>");
        sensorSchema = sensorSchema.applyContractTransformation(objectToRemove,null,null);
        objectToRemove = sensorSchema.getSchemaObject("<<database,databasedataIE,notnull,anyRelType>>");
        sensorSchema = sensorSchema.applyContractTransformation(objectToRemove,null,null);
        objectToRemove = sensorSchema.getSchemaObject("<<database,database_pk,notnull,anyRelType>>");
        sensorSchema = sensorSchema.applyContractTransformation(objectToRemove,null,null);
        objectToRemove = sensorSchema.getSchemaObject("<<database>>");
        sensorSchema = sensorSchema.applyContractTransformation(objectToRemove,null,null);

        // Add the sensorNum column to the sensor XML schema
        SchemaObject sensorTable = sensorSchema.getSchemaObject("<<" + sensorTableName + ">>");
        String newColumnScheme = "Void";
        String type = "int4";
        AutoMedType amType = new AutoMedString(type);
        sensorSchema = sensorSchema.applyAddTransformation(column, amType,
            new Object[]{sensorTable,"sensornum","notnull",type},newColumnScheme,null);
        
        // The sensor table has a columns all include the parent element name. This needs to removed
        SchemaObject sensorColumn = sensorSchema.getSchemaObject("<<" + sensorTableName + "," + "databasedatatime" + ">>");
        sensorSchema = sensorSchema.applyRenameTransformation(sensorColumn,new Object[]{sensorTable, "time", "notnull", "int4"});
        sensorColumn = sensorSchema.getSchemaObject("<<" + sensorTableName + "," + "databasedatadamage" + ">>");
        sensorSchema = sensorSchema.applyRenameTransformation(sensorColumn,new Object[]{sensorTable, "damage", "notnull", "int4"});
        sensorColumn = sensorSchema.getSchemaObject("<<" + sensorTableName + "," + "databasedatanumvehicles" + ">>");
        sensorSchema = sensorSchema.applyRenameTransformation(sensorColumn,new Object[]{sensorTable, "numvehicles", "notnull", "int4"});
        sensorColumn = sensorSchema.getSchemaObject("<<" + sensorTableName + "," + "databasedatanumcivilians" + ">>");
        sensorSchema = sensorSchema.applyRenameTransformation(sensorColumn,new Object[]{sensorTable, "numcivilians", "notnull", "int4"});
        
        // Rename the pk
        SchemaObject sensorPk = sensorSchema.getSchemaObject("<<databasedatatime_pk,databasedata,<<databasedata,time,notnull,int4>>>>");
        sensorColumn = sensorSchema.getSchemaObject("<<" + sensorTableName + "," + "time" + ">>");
        sensorSchema = sensorSchema.applyRenameTransformation(sensorPk,new Object[]{"sensor_pk", sensorTable, sensorColumn});
        
        // Rename the sensor table
        sensorSchema = sensorSchema.applyRenameTransformation(sensorTable,new Object[]{uavTableName});

        // The sensor schema and the UAV schema should now match so merge them
        Schema matchedSchema = sensorSchema.applyIdentTransformation(uavSchema);
        Transformation t = Transformation.getKnownTransformation(sensorSchema,uavSchema);
        t.setProperty(Transformation.PROPERTY_IQL_FUNCTION,"(++)");

        return matchedSchema;
    }
    
  }
