package rodex;
/**
 * DTCDemo.java
 *
 *
 *
 * @author Duc M Le  <a href="mailto:dmle@@doc.ic.ac.uk"><i>dmle@@doc.ic.ac.uk</i></a>
 * @author Andrew Charles Smith
 *
 * Created on 09 March 2006
 * Department of Computing, Imperial College
 */
import java.awt.Color;
import uk.ac.ic.doc.automed.p2p.*;
import uk.ac.ic.doc.automed.*;
import uk.ac.ic.doc.automed.p2p.state.*;
import uk.ac.ic.doc.automed.p2p.tree.AutoMedTreeException;
import uk.ac.ic.doc.automed.reps.RelationalOutputter;
import uk.ac.ic.doc.automed.util.Debugger;
import uk.ac.ic.doc.automed.util.Logger;
import uk.ac.ic.doc.automed.p2p.communication.PortInUseException;
import uk.ac.ic.doc.automed.p2p.directory.P2PDirectory;
import uk.ac.ic.doc.automed.p2p.directory.PeerDescription;

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

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 uk.ac.ic.doc.rodex.p2p.Peer;
import java.util.Map;
import java.util.Iterator;
import uk.ac.ic.doc.automed.p2p.tree.TreeNode;
import uk.ac.ic.doc.automed.p2p.util.XmlHandler;
import uk.ac.ic.doc.automed.util.LogOutputHandler;
import utils.AutoMedUtils;

public class DTCDemo {
  private MediatorPeer pm;
  private DataPeer pd;
  
  private String pmName = "UAV";
  private String pdName;
  protected static String pd1Name = "Sensor1";
  protected static String pd2Name = "Sensor2";
  //private String xmlFile = "file:data/sensorData.xml";
  private String xmlFile1 = "file:data/sensorData.xml";
  private String xmlFile2 = "file:data/sensorData2.xml";

  private String remoteAddress = "127.0.0.1";
  private int peerDaemonPort = 0;  
  private int kat = 2;    // keep-alive time period in secs
  
//  private static String controllerTableName = "sensorcontrol";
  private static String uavTableName = "sensor";
  private static String sensorTableName = "databasedata";
  private static String username = "automed";
  private static String password = "automed";  
  private static String sensorSchemaName = "sensor";
  private static String uavSrcSchemaName = "uav_src";  
  private static String uavSchemaName = "uav";

  private static long sleepTime = 100;
  
  /**
   * Creates a new instance of AutoMedPeerApplication
   */
  public DTCDemo() {
  }

  // the mediator peer
  public void runMediatorPeer(final int peerNumber) {
    // create a new peer
    new Thread() {
      public void run() {
//        while (pd == null || pd.getReadyState() == false) {
//          try {
//            Thread.sleep(2000);
//          } catch (InterruptedException ie) {
//            //
//          }
//        }        
        if (peerNumber == 1) pdName = pd1Name;
        if (peerNumber == 2) pdName = pd2Name;
        System.out.println("\n++++++++++++++++++++++++++++++++++");
        System.out.println("Creating mediator peer " + pmName);    
        System.out.println("++++++++++++++++++++++++++++++++++\n");
        // now start mediator peer
        pm = new MediatorPeer(pmName, peerNumber);
      }
    }.start();
  }
  
  // data provider peer
  public void runDataPeer(final int peerNumber) {
    if (peerNumber == 1) pdName = pd1Name;
    if (peerNumber == 2) pdName = pd2Name;
    System.out.println("\n++++++++++++++++++++++++++++++++++");
    System.out.println("Creating data peer " + pdName);
    System.out.println("++++++++++++++++++++++++++++++++++\n");
    
    new Thread() {
      public void run() {
          pd = new DataPeer(pdName, peerNumber);
      }
    }.start();
  }
  
  // start a gui peer as logged in
  public void runGuiPeer() throws StateChangedException {
    PeerStateHandler stateHandler = new PeerStateHandler();
    stateHandler.changePeerState(PeerStateHandler.STATE_STAND_ALONE);
    stateHandler.changePeerState(PeerStateHandler.STATE_JOINED_LOCAL);
    stateHandler.changePeerState(PeerStateHandler.STATE_REGISTER_GLOBAL);
    RodexApplication rodexApp = new RodexApplication();      
    rodexApp.runGuiApplication(pmName, stateHandler);    
  }
  
  public static void main(String args[]) throws Exception {
    // run peer depending on argument
    String peerType = null;
    
    String usage = "Usage: java -cp $CLASSPATH DTCDemo -t m|d|s|g [-d \"m.n (...)\"] [-c cfgFile]" +
      "\n\n  -t  peer type where m = mediator, d(1/2) = data, g = gui" +
      "\n   -d  one or more set of debug options in format section.level (if more than one then must escape with double quotes)" +
      "\n   -c  full path to the customised data source configuration file (default: $HOME/.automed/data_source_repository.cfg)" +
      "\n\n The data peer initialises a repository to wrap university_1 data source as uni_s1 and advertises it" +
      "\n to its directory service for mediator peer to wrap." +
      "\n To run both mediator and data peers, you must specify -c to use another data source configuration for at least one of these peers" +
      "\n\n Examples:" +
      "\n Run with default debug information:" +
      "\n -----------------------------------" +
      "\n java DTCDemo -t d1 " +
      "\n java DTCDemo -t m1 -c $HOME/.automed/data_source_repository_1.cfg" +
      "\n Run with detailed debug information:" +
      "\n ------------------------------------" +
      "\n java DTCDemo -t d1 -d \"0.5 3.8\"" +
      "\n java DTCDemo -t m1 -d \"0.5 3.9\" -c $HOME/.automed/data_source_repository_1.cfg";
    
    if (args == null || args.length < 1) {
      System.out.println(usage);      
      System.exit(0);
    } else {
      for (int i = 0; i < args.length; i++) {
        if (args[i].equals("-t")) {
          i++;
          peerType = (i < args.length) ? args[i] : null;
          break;
        }        
      }
    }

    if (peerType == null || new String("m1m2d1d2gs").indexOf(peerType) < 0)  {
      System.out.println(usage);
      System.exit(0);
    }
    
    Debugger.setSectionDebugLevel(args);
    
    Logger.setOutputHandler(new LogOutputHandler(
      LogOutputHandler.CONSOLE + LogOutputHandler.FILE));
    
    DSRConfiguration.setDefaultConfigFileName(args);
    DTCDemo peerApp = new DTCDemo();
    if (peerType.equals("d1"))
      peerApp.runDataPeer(1);
    else if (peerType.equals("d2"))
      peerApp.runDataPeer(2);
    else if (peerType.equals("m1"))
      peerApp.runMediatorPeer(1);
    else if (peerType.equals("m2"))
      peerApp.runMediatorPeer(2);
    else if (peerType.equals("g")) {
      // use an existing RodexApplication to start the gui
      peerApp.runGuiPeer();
    } else if (peerType.equals("s")){
      RodexApplication rodexApp = new RodexApplication();
      rodexApp.runDirectoryServicePeer();      
    }
  }
  

  /**************************************************************************
   * Definitions of two peers for use in this application:<br>
   * <ul>
   *    <li>{@@link MediatorPeer):   an {@@link AutoMedPeer} that serves as data integrator
   *    <li>{@@link DataPeer}:       an {@@link AutoMedPeer} that provides data via public schemas
   * </ul>
   * 
   * <p> The {@@link DataPeer} instantiate a peer and a registry and a directory to serve requests<br>
   * coming from {@@link MediatorPeer}. The instantiation of {@@link MediatorPeer} is delayed until <br>
   * {@@link DataPeer} is fully connected and a schema has been published<br>
   */
  
  class MediatorPeer {
    private Peer localPeer = null;
//    private KeepAliveMessageHandler keepAliveObj = null;
    private AutoMedMessagingHelper mesgHelper = null;
       
    public MediatorPeer(String name, int peerNumber) {
      try {
        if (AutoMedUtils.dialogYesNo("Re-initialise local repository?", 0) == 1) {                
          AutoMedUtils.banner("Initialising peer repository "+DSRConfiguration.getDefaultConfigFileName()+"...");
          AutoMedPeer.initRepository();
        }
        
        AutoMedUtils.banner("Initialising a new peer "+name+"...");        
        localPeer = Peer.newPeer(name, new PeerStateHandler());

        // initialise the registry
        AutoMedUtils.banner("Initialising registry attached to peer "+name+"...");
        try {
          localPeer.initRegistry();
        } catch (PortInUseException piue) { 
          // registry is already created locally? try to use it
          Logger.debug("MediatorPeer", "", "Registry seems to be available, using it instead!");
        }
        
        // start hello multicast...
        AutoMedUtils.banner("Initialising hello protocol for peer " + name);
        localPeer.initHelloProtocol();
        
        // wait until receiving hello ack from server ...
        System.out.print("Waiting for server's hello acknowledgement");
        while (localPeer.getPeerState() < PeerStateHandler.STATE_HELLO_COMPLETE) {
          try {
            Thread.sleep(sleepTime);
            System.out.print(".");
          } catch (InterruptedException ie) {
            //
          }
        }
        System.out.println("\n");
        
        AutoMedUtils.banner("Initialising authentication "+name+"...");        
        localPeer.initAuthentication();
        localPeer.authenticate();

        // don't need to initialise standard messaging helpers because we will
        // create our own
        AutoMedUtils.banner("Initialising messaging service for peer "+name+"...");                
        localPeer.initMessagingPeer();          
        
        // initialise SICP protocol
        AutoMedUtils.banner("Initialising SICP protocol for " + name);
        localPeer.initSICP();
        
        // data peer description to use
        PeerDescription peerDesc = new PeerDescription(pdName, remoteAddress);

//        Logger.debug("MediatorPeer", "", "Getting schema from peer...");    
//        getSchema(peerDesc, "uni_s1");
        AutoMedWrapper xmlWrapper = null;
        Schema xmlSchema = null;
        if (AutoMedUtils.dialogYesNo("Wrap sensor's XML schema from the network?", 1) == 1) {
          AutoMedUtils.banner("Mediator: Wrapping sensor schema "+sensorSchemaName+" from "+peerDesc.getName()+"...");    
          xmlWrapper = wrapSchema(peerDesc, sensorSchemaName += peerNumber); 
          xmlSchema = xmlWrapper.getSchema();
        }        
        
        // wrapping mediator's data source
        Schema sqlSchema = null;
        if (AutoMedUtils.dialogYesNo("Wrap the UAV local data source?", 0) == 1) {
          AutoMedUtils.banner("Mediator: Wrapping mediator data source "+uavSrcSchemaName+"...");    
          sqlSchema = wrapSQLDataSource();
        } else {
            sqlSchema = Schema.getSchema(uavSchemaName);
        }
        
        // integrate two schemas
        Schema intSchema = null;
        if (AutoMedUtils.dialogYesNo("Integrate two schemas?", 1) == 1) {        
          //AutoMedUtils.banner("Mediator: Integrating sensor "+xmlSchema.getName()+"and mediator schema "+sqlSchema.getName()+"...");    
          intSchema = integrateXML2SQL(xmlSchema, sqlSchema, peerNumber);
          intSchema.setBackgroundColor(Color.RED);
        }
        
        // END
        System.exit(0);
        
        // if things went well, run the gui
//        AutoMedUtils.banner("Starting GUI...");
//        new DTCDemo().runGuiPeer();
                
//        Logger.debug("MediatorPeer", "", "Getting pathways to source...");    
//        getPathwayToSource(peerDesc, "uni_s1"); 
      } catch (AutoMedException ame) {
        ame.printStackTrace();
//        try {
//          localPeer.close();       
//        } catch (AutoMedException amex) {
//          //
//        }
        System.exit(-1);
      }                

    }

    public void getSchema(PeerDescription peerDesc, String name) {
      //
      // get schema from peer
      SchemaGetter sgetter = new SchemaGetter(localPeer);
      try {
        sgetter.getSchema(name);
      } catch (Exception e) {
        e.printStackTrace();         
        localPeer.close();       
        System.exit(-1);
      }  
    }
    
    public 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
      String driver = "org.postgresql.Driver";
      String urlbase = "jdbc:postgresql://localhost/";
      
      if (Schema.exists(uavSrcSchemaName)) Schema.retract(uavSrcSchemaName);
      AutoMedWrapper sw=
        AutoMedWrapper.selectNewAutoMedWrapper(username,password,null,driver,
                                           urlbase+"vehicledata",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();
      
      //sqlSchema.listSchema();   
      
      return sqlSchema;
    }
          
    // integrate 2 compatible schemas
    public Schema integrateXML2SQL(Schema xmlSchema, Schema sqlSchema, int peerNumber)
        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);
      // 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();

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

      return sqlSchema;
    }
    
    private Schema merge(Schema uavSchema, Schema sensorSchema,int peerNumber)
        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(
            "<<databasedataHDMdatabasefk_fk,databasedata,<<databasedata,databasedatatime,null,int4>>," +
                "database,<<database,databasedataHDMdatabasefk,not null,anyRelType>>>>");
        sensorSchema = sensorSchema.applyContractTransformation(objectToRemove,null,null);
        objectToRemove = sensorSchema.getSchemaObject(
            "<<database_pk_pk,database,<<database,database_pk,not null,anyRelType>>>>");
        sensorSchema = sensorSchema.applyContractTransformation(objectToRemove,null,null);
        objectToRemove = sensorSchema.getSchemaObject("<<database,databasedataHDMdatabasefk,not null,anyRelType>>");
        sensorSchema = sensorSchema.applyContractTransformation(objectToRemove,null,null);
        objectToRemove = sensorSchema.getSchemaObject("<<database,database_pk,not null,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's 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", type});
        
        // Rename the appropriate columns for each peer        
        if (peerNumber == 1) {
            sensorColumn = sensorSchema.getSchemaObject("<<" + sensorTableName + "," + "databasedatadamage" + ">>");
            sensorSchema = sensorSchema.applyRenameTransformation(sensorColumn,new Object[]{sensorTable, "damage", "notnull", type});
            sensorColumn = sensorSchema.getSchemaObject("<<" + sensorTableName + "," + "databasedatanumvehicles" + ">>");
            sensorSchema = sensorSchema.applyRenameTransformation(sensorColumn,new Object[]{sensorTable, "numvehicles", "notnull", type});
            sensorColumn = sensorSchema.getSchemaObject("<<" + sensorTableName + "," + "databasedatanumcivilians" + ">>");
            sensorSchema = sensorSchema.applyRenameTransformation(sensorColumn,new Object[]{sensorTable, "numcivilians", "notnull", type});
        } else if (peerNumber == 2) {
            sensorColumn = sensorSchema.getSchemaObject("<<" + sensorTableName + "," + "databasedatatemp" + ">>");
            sensorSchema = sensorSchema.applyRenameTransformation(sensorColumn,new Object[]{sensorTable, "temp", "notnull", type});            
            // Peer 2 needs some columns added
            sensorSchema = sensorSchema.applyAddTransformation(column, amType,
                new Object[]{sensorTable,"damage","notnull",type},newColumnScheme,null);
            sensorSchema = sensorSchema.applyAddTransformation(column, amType,
                new Object[]{sensorTable,"numvehicles","notnull",type},newColumnScheme,null);
            sensorSchema = sensorSchema.applyAddTransformation(column, amType,
                new Object[]{sensorTable,"numcivilians","notnull",type},newColumnScheme,null);            
            // Add the temp column to the UAV schema
            SchemaObject uavTable = uavSchema.getSchemaObject("<<" + uavTableName + ">>");
            uavSchema = uavSchema.applyAddTransformation(column, amType,
                new Object[]{uavTable,"temp","notnull",type},newColumnScheme,null);
        }
        
        // 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;
        Schema matchedSchema = sensorSchema.applyIdentTransformation(uavSchema);
        Transformation t = Transformation.getKnownTransformation(sensorSchema,uavSchema);
        t.setProperty(Transformation.PROPERTY_IQL_FUNCTION,"(++)");

        return matchedSchema;
    }

    public AutoMedWrapper wrapSchema(PeerDescription destPeerDesc, String name) throws AutoMedException {
        // wrapping a schema from directory
      //    
      try {
        AutoMedWrapper wrapper = AutoMedWrapper.selectNewAutoMedWrapper(destPeerDesc.getName(),"",
          null, P2PWrapper.class.getName(),
          "p2p:", name, null);

        Logger.debug("Mediator", "wrapSchema()", "Schema name "+name+" was wrapped from " + destPeerDesc.getName());
        return wrapper;
      } catch(Exception e) {
        throw new AutoMedException("Fail to wrap public schema "+
          name+" wrapped from " + destPeerDesc.getName(), e);
      }      
    }
    
    // get all possible pathways to a data source
    public void getPathwayToSource(PeerDescription destPeerDesc, String schemaName) 
      throws AutoMedException {
      try {
        new PathwayGetter(localPeer).
          getPathToSource(destPeerDesc.getName(), destPeerDesc.getURL(), schemaName);
        
        Logger.debug(this.getClass(),"getPathwayToSource()", "Paths to source on peer "+destPeerDesc.getName()+
          " were obtained");
      } catch(Exception e) {
        throw new AutoMedException("Failed to obtain paths to source on peer "+
          destPeerDesc.getName(), e);
      }
    }
  }
  
  class DataPeer {
    private Peer localPeer = null;
    private KeepAliveMessageHandler keepAliveObj = null;
    private AutoMedMessagingHelper mesgHelper = null;
    private P2PRegistry registry = null;

    private P2PDirectory directory = null;

    private boolean isReady  = false;
    
    public DataPeer(String name, int peerNumber) {
      try {

        if (AutoMedUtils.dialogYesNo("Re-initialise local repository?", 0) == 1) {                
          AutoMedUtils.banner("Initialising peer repository "+DSRConfiguration.getDefaultConfigFileName()+"...");
          AutoMedPeer.initRepository();
        }
        
        /****START the peer*/
        AutoMedUtils.banner("Initialising a new peer "+name+"...");
        localPeer = Peer.newPeer(name, new PeerStateHandler());
        
        // initialise the registry
        AutoMedUtils.banner("Initialising registry attached to peer "+name+"...");
        try {
          registry = localPeer.initRegistry();
        } catch (PortInUseException piue) { 
          // registry is already created locally? try to use it
          Logger.debug("MediatorPeer", "", "Registry seems to be available, using it instead!");
        }

        // start hello multicast...
        AutoMedUtils.banner("Initialising hello protocol for peer " + name);
        localPeer.initHelloProtocol();
        
        // wait until receiving hello ack from server ...
        System.out.print("Waiting for server's hello acknowledgement");
        while (localPeer.getPeerState() < PeerStateHandler.STATE_HELLO_COMPLETE) {
          try {
            Thread.sleep(sleepTime);
            System.out.print(".");
          } catch (InterruptedException ie) {
            //
          }
        }
        System.out.println("\n");
        
        
        AutoMedUtils.banner("Initialising authentication and wait to proceed...");        
        localPeer.initAuthentication();
        
        AutoMedUtils.banner("Start authenticating peer "+name+"...");                
        localPeer.authenticate();

        // don't need to initialise standard messaging helpers because we will
        // create our own
        AutoMedUtils.banner("Initialising messaging service for peer "+name+"...");                
        localPeer.initMessagingPeer();

      } catch (AutoMedException ame) {
        ame.printStackTrace();
        System.exit(-1);
      }                

      
      try {
        // get schema from peer
        SchemaPublisher spub = new SchemaPublisher(localPeer);
        
        // wrap XML schema
        Schema xmlSchema = null;
        if (AutoMedUtils.dialogYesNo("Wrap my local XML data source?", 0) == 1) {        
          AutoMedUtils.banner("DataPeer: Wrapping XML data source "+sensorSchemaName+"...");
          xmlSchema = wrapXMLDataSource(peerNumber);
        }
        
        // initialise SICP protocol
        AutoMedUtils.banner("Initialising SICP protocol for " + name);
        localPeer.initSICP();
        
        // test serialising XML model associated to sensor schema        
//        if (AutoMedUtils.dialogYesNo("Test serialising the XML models associated to "+
//          sensorSchemaName+" schema?", 0) == 1) {
//          if (xmlSchema == null)
//            xmlSchema = Schema.getKnownSchema(sensorSchemaName);
//          
//          Map models = xmlSchema.getModels();
//          int count = 100;
//          if (models != null && models.size() > 0) {
//            for (Iterator it = models.keySet().iterator(); it.hasNext();) {
//              String mid = (String) it.next();
//              final Model m = (Model) models.get(mid);
//              final String structure = null;
//              for (int i = 0; i < count; i++) {
//                System.out.println(">>>> iteration " + (i+1));
//                new Thread(new Runnable() {
//                  public void run() {
//                    try {
//                      System.out.println("\t new thread -> serialising model " + m.getName());
//                      XmlHandler.asString(TreeNode.createTree(m).getXMLDocument());
//                    } catch (AutoMedTreeException ex) {
//                      ex.printStackTrace();
//                    }
//                  }
//                }).start();
//                                
//              }
////              System.out.println("Model " + mid + " has serialised structure:\n" +
////                structure);
//            }
//          } else {
//            System.out.println("No models defined for schema " + sensorSchemaName);
//          }
//        }
        
        // publish XML schema
        if (AutoMedUtils.dialogYesNo("Publish my XML schema to the directory?", 0) == 1) {        
          if (xmlSchema == null) {
            xmlSchema = Schema.getKnownSchema(sensorSchemaName+peerNumber);
          }
          AutoMedUtils.banner("DataPeer: Publishing XML schema "+xmlSchema.getName()+"...");
          spub.publishSchema(xmlSchema.getName(), "schema "+xmlSchema.getName()+" from " + name);
        }                
        
        // materialise xml data into relational
        if (AutoMedUtils.dialogYesNo("Add XML data source to the replication service?", 0) == 1) {        
          if (xmlSchema == null) {
            xmlSchema = Schema.getKnownSchema(sensorSchemaName+peerNumber);
          }
          outputToRelational(localPeer, xmlSchema);
        }
      
//        if (AutoMedUtils.dialogYesNo("Run the data replication service?", 0) == 1) {        
//          if (xmlSchema == null) {
//            xmlSchema = Schema.getKnownSchema(sensorSchemaName+peerNumber);
//          }
//          AutoMedUtils.banner("Replicating data for XML schema "+xmlSchema.getName()+"...");
//          runDONService(localPeer);
//        }

      } catch (Exception e) {
        e.printStackTrace();
//        try {
//          localPeer.close();       
//        } catch (AutoMedException ame) {
//          //
//        }
        System.exit(-1);
      }
      
      // now peer is ready
      isReady = true;
    }
    
    public boolean getReadyState() {
      return isReady;
    }
    
    public void runDONService(Peer thePeer) throws AutoMedException, Exception {
      String[] args = new String[] {
        "-x", "-y", "-s 20", "-j host9:8809",
        "-d \"0.6 20.8 9.10\"", 
        "-f etc/DTCDemoOverlayScenario.properties",
        "-m " + thePeer.getName()
      };
      
      DTCDemoOverlay.main(args);
    }
    
    public Schema wrapXMLDataSource(int peerNumber) throws AutoMedException, Exception {
      // Construct names      
      // Create an XMLSchema wrapper factory
      XMLSchemaWrapperFactory xwf=new XMLSchemaWrapperFactory();
      // Wrap the XML schema from sensor A
      String driver = "uk.ac.ic.doc.automed.wrappers.XMLSchemaWrapper";
      String xmlFile;
      sensorSchemaName += peerNumber;
      if (peerNumber == 1) xmlFile = xmlFile1;
      else xmlFile = xmlFile2;
      if (Schema.exists(sensorSchemaName)) Schema.retract(sensorSchemaName);
      AutoMedWrapper swxml=
        AutoMedWrapper.selectNewAutoMedWrapper(username,password,null,driver,
                                           xmlFile,sensorSchemaName,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();
      
      return xmlSchema;
    }
  }
  
      // convert the Database-data XML schema to a relational schema and materialise
    // the data to this relational source
    public Schema outputToRelational(AutoMedPeer localPeer, Schema xmlSchema ) throws AutoMedException, Exception {
      // 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("Adding schema to replication repository");
      // 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();
      
      // materialise data in the XML schema to the new relational table
      RelationalOutputter ro = new RelationalOutputter(localPeer,transformedSQLSchema);
      
      ro.createRelationalTables();
      
      return transformedSQLSchema;
    }
}
