package templates;

import uk.ac.ic.doc.automed.templates.wrapper.*;
import uk.ac.ic.doc.automed.reps.*;
import uk.ac.ic.doc.automed.wrappers.SQLWrapperFactory;
import uk.ac.ic.doc.automed.InconsistentException;
import uk.ac.ic.doc.automed.IntegrityException;
import java.util.ArrayList;

public class NormaliseTable 
  extends TemplateCompilerSimulator {

  public NormaliseTable(SQLWrapperFactory awf)
    throws Exception {
    super(awf);
  }

  //Define the abstract methods//
  public String templateName() {
    return "column to subtables (model "+awf.getFeatures()+")";
  }

  protected String templateDescription() {
    return "Normalises a table, by treating one column as primary key of new table, and moving other columns functionally dependent on the column into the new table";
  }

  public String defaultModel() {
    return defaultModel.getName();
  }

  //Define the template//
  protected void defineTemplate() throws Exception {
    Model sql=defaultModel;
    Construct table=
      sql.getConstruct(SQLWrapperFactory.AUTOMED_SQL_MODEL_TABLE);
    Construct column=
      sql.getConstruct(SQLWrapperFactory.AUTOMED_SQL_MODEL_COLUMN);
    Construct primarykey=
      sql.getConstruct(SQLWrapperFactory.AUTOMED_SQL_MODEL_PRIMARY_KEY);

    INPUTS();
    OBJECT parentTable=askForObject("Unnormalised table",table);
    OBJECT mandatoryColumn=askForObject("New table primary key column",column);
    //OBJECT parentPrimaryKey=askForObject("Primary key of parent table",column);
    NAMELIST functionallyDependentColumns=
      askForNameList("Columns functionaly dependent on primary key");

    START();
    // Create the new table
    ALIAS(mandatoryColumn,"pkcolumn");
    LIST pkScheme=mandatoryColumn.SCHEME_LIST();
    NAMELIST dataType=null;
    if (awf.isFeatureInUse(SQLWrapperFactory.DATA_TYPE)) 
      dataType=(NAMELIST)pkScheme.ELEMENT_AT(2,TypeNameList);
    NAMELIST columnName=(NAMELIST)pkScheme.ELEMENT_AT(1,TypeNameList);
    NAME newTableName=DEFINE_NAME((NAME)dataType.ELEMENT_AT(0,TypeName));

    FUNCTION tableExtent=
      DEFINE_FUNCTION("[{y} | "+
		      "{x,y} <- @pkcolumn?scheme]");
    OBJECT newTable=
      ADD(CONSTRUCT.IS(table),
	  SCHEME.IS(new Object[]{my(newTableName)}),
	  FUNCTION.IS(tableExtent));

    FUNCTION columnExtent=
      DEFINE_FUNCTION("[{x,y} | {x,y} <- @pkcolumn?scheme;"+
		      "{x,@descriptiveInstance} <- @parentcolumn?scheme]");
    NAME columnName=DEFINE_NAME("@primarykey?name");

    FOREACH(); 
    NAME fdColumn=IN(functionallyDependentColumns);
    OBJECT parentcolumn=VARIES_WITH(mandatoryColumn);
    OBJECT parent=VARIES_WITH(parentTable);
    //OBJECT primaryKey=VARIES_WITH(parentPrimaryKey);
    
    DO(); 
    ALIAS(fdColumn,"fdColumn");
    ALIAS(parentcolumn,"parentcolumn");
    ALIAS(parent,"parent");
    //ALIAS(primaryKey,"primarykey");
    
    
    FUNCTION columnExtent=
      DEFINE_FUNCTION("[{x,y} | {x,y} <- @primarykey?scheme;"+
		      "{x,@descriptiveInstance} <- @parentcolumn?scheme]");
    NAME columnName=DEFINE_NAME("@primarykey?name");
    NAME type=DEFINE_NAME((NAME)dataType.ELEMENT_AT(0,TypeName));
    // Create new Object[]{newTable,my(columnName),...}
    ArrayList scheme=new ArrayList();
    scheme.add(newTable);
    scheme.add(my(columnName));
    if (awf.isFeatureInUse(SQLWrapperFactory.DATA_TYPE)) 
      scheme.add(my(type));

    OBJECT newColumn=
      ADD(CONSTRUCT.IS(column),
	  SCHEME.IS(scheme.toArray()),
	  FUNCTION.IS(columnExtent));
    
    OBJECTLIST primaryKeyColumns=
      (OBJECTLIST)CREATE_LIST(new ITEM[]{newColumn});
    NAME primarykeyName=DEFINE_NAME("@(fdColumn)_pk");
    ADD(CONSTRUCT.IS(primarykey),
	SCHEME.IS(new Object[]{my(primarykeyName),newTable,
				 primaryKeyColumns}));
    
    if (awf.isFeatureInUse(SQLWrapperFactory.FOREIGN_KEY)) {
      Construct foreignkey=
	sql.getConstruct(SQLWrapperFactory.AUTOMED_SQL_MODEL_FOREIGN_KEY);
      NAME foreignkeyName=DEFINE_NAME("@(fdColumn)_fk");
      OBJECTLIST foreignKeyColumns=
	(OBJECTLIST)CREATE_LIST(new ITEM[]{primaryKey});
      ADD(CONSTRUCT.IS(foreignkey),
	  SCHEME.IS(new Object[]{my(foreignkeyName),
				   newTable,primaryKeyColumns,
				   parent,foreignKeyColumns}));
    }

    ALIAS(newTable,"specialization");
    STRING partialQuery=DEFINE_STRING("[{x,@descriptiveInstance}| "+
				      "{x} <- @specialization?scheme]");
    STRINGLIST partialQueriesForMandatoryColumn=
      (STRINGLIST)COLLECTS(partialQuery);				
    
    ENDFOREACH();
    
    STRING query=DEFINE_STRING(partialQueriesForMandatoryColumn,"++");
    ALIAS(query,"query");
    FUNCTION mandatoryColumnExtent=DEFINE_FUNCTION("@query");
    mandatoryColumn.DELETE(mandatoryColumnExtent);
    END();
  }
    
  public static void main(String[] args) throws Exception {
    SQLWrapperFactory awf=new SQLWrapperFactory();
    awf.setFeatures(SQLWrapperFactory.PRIMARY_KEY |
		    SQLWrapperFactory.FOREIGN_KEY //|
		    // SQLWrapperFactory.DATA_TYPE
		    );
    new NormaliseTable(awf);
  }
}
