import java.sql.*;
import java.io.*;
import java.awt.Color;
import uk.ac.ic.doc.automed.*;
import uk.ac.ic.doc.automed.reps.*;
import uk.ac.ic.doc.automed.editor.drawables.*;
import uk.ac.ic.doc.automed.wrappers.*;

/*
 * An application to demonstrate how to setup models (i.e. modelling
 * languages such as ER, Relational, etc) using the
 * Automed API
 */
public class DefineModels {
  /*
   * Define a basic ER modelling language, with entities, attributes,
   * binary relationships and generalisations
   */
  public static void createERModel() 
    throws InconsistentException,
    AutoMedException, SQLException, IOException {
    // Zap any existing model called "er"
    if (Model.exists("er")) {
      System.out.println("Removed old er modelling language");
      Model.getModel("er").retract();
    }

    // COMMENT Should "add" match "remove"?
    Model er=Model.createModel("er"); 
    er.setBackgroundColor(new Color(192,192,0,128));
    // Create the four constructs this particular ER modelling language

    // ENTITY ...
    // Entities can stand by themselves; hence NODAL
    Construct entity=
      er.createConstruct("entity",Construct.CLASS_NODAL,true);
    // The scheme of an entity contains just the entity name,
    // in the form <<ent_name>> where we use ent_name as name of the
    // node

    // addNodeNameScheme defaults to isKey=true, cardinality=1..1
    // (as construct's class is Nodal, cardinality must be 1..1)
    entity.addNodeNameScheme();

    // ATTRIBUTE ...
    // Attributes have values not present in other constructs, but
    // must be associated to other objects; hence LINK_NODAL
    Construct attribute=
      er.createConstruct("attribute",Construct.CLASS_LINK_NODAL,true);
    //attribute.setBackgroundColor(Color.ORANGE);

    // addEdgeEdgeNameScheme & addReferenceScheme default to isKey=true
    // all default to cardinality 1..1 (EdgeName must be 1..1)
    attribute.addReferenceScheme(entity);
    attribute.addNodeNameScheme();
    attribute.addConstraintScheme(false); // constraint's not key

    // RELATIONSHIP ...
    // Relationships link other existing objects, and hence are LINK
    Construct relationship=
      er.createConstruct("relationship",Construct.CLASS_LINK,true);
    relationship.setArcDrawable(DrawERRelationshipArc.class.getName());
    //relationship.setBackgroundColor(Color.RED);

    // we want an edge between two existing entities and we
    // want two cardinality constraints, one for each entity
    // We just want the edgeName and entity references to be key
    relationship.addEdgeNameScheme();
    relationship.addReferenceScheme(entity,2,2);
    relationship.addConstraintScheme(2,2,false);

    // GENERALISATION ...
    // Generalisations are constraints on the values of entities, and
    // have not associated values, hence CONSTRAINT
    Construct generalisation=
      er.createConstruct("generalisation",Construct.CLASS_CONSTRAINT,true);
    generalisation.
      setVertexDrawable(DrawERGeneralisationVertex.class.getName());
    generalisation.
      setArcDrawable(DrawERGeneralisationArc.class.getName());
    
    // need labels here as we need to name the generalization and type
    // but there is no corresponding HDM edge or node
    // Note the cardinality of the sub entities ReferenceScheme, as lower>
    // bigger this means lower..n i.e. 2..n in this case
    generalisation.addLabelScheme();
    generalisation.addLabelScheme(false);
    generalisation.addReferenceScheme(entity);
    generalisation.addReferenceScheme(entity,2,0,false);
    // Add a constraint definition
    generalisation.setConstraintDefinition("key",
            Construct.CONSTRAINT_INCLUSION + ":0:" + HDMWrapperFactory.HDM_NODE_CONS_TYPE +
            ":1:" + HDMWrapperFactory.HDM_NODE_CONS_TYPE);
  
    System.out.println("Created ER Model");
  }

  // TODO: in the above er modelling language definition we have cardinality
  // constraints defined as part of the relationship scheme and a constraint
  // construct to model er generalizations.  In each case where does the 
  // MDR level constraint string (with embedded references to construct
  // references in the scheme) go and in the first case where does the STR
  // level cardinality numbers go?  mb - 25/01/02

  // TODO: or at least think about.  Constants for Construct type are defined
  // in Construct (an MDR level class) while the constants for the scheme
  // argument types are defined in Scheme (an STR level class).  Shouldn't
  // the latter be defined in ConstructScheme?  Looks like it ended up that
  // way simply because the constants were originally called CONTRUCT_*
  // and SCHEME_* when defined in Constants class.

  /*
   * Define a extended ER (EER) modelling language, with entities, attributes,
   * nary relationships and generalisations, where attributes may be on
   * relationships as well as entities, and relationships may be between
   * relationships as well as entities
   */
  public static void createEERModel() 
    throws InconsistentException,
    AutoMedException, SQLException, IOException {
    // Zap any existing model called "er"
    if (Model.exists("eer")) {
      System.out.println("Removed old eer modelling language");
      Model.getModel("eer").retract();
    }

    Model eer=Model.createModel("eer"); 
    eer.setBackgroundColor(new Color(212,192,0,128));

    // ENTITY ...
    Construct entity=
      eer.createConstruct("entity",Construct.CLASS_NODAL,true);
    entity.addNodeNameScheme();


    // RELATIONSHIP ...
    Construct relationship=
      eer.createConstruct("relationship",Construct.CLASS_LINK,true);
    relationship.
      setArcDrawable(DrawEERRelationshipArc.class.getName());

    Construct entityOrRelationship=
      eer.createConstruct("entity_or_relationship",
			  Construct.CLASS_ALTERNATION,false);
    entityOrRelationship.addReferenceScheme(entity);
    entityOrRelationship.addReferenceScheme(relationship);
    Construct relationshipTarget=
      eer.createConstruct("relationship_target",
			  Construct.CLASS_SEQUENCE,false);
    relationshipTarget.addReferenceScheme(entityOrRelationship,1,1);
    relationshipTarget.addConstraintScheme(1,1,false);
    relationship.setConstraintDefinition("0:N","");    
    relationship.setConstraintDefinition("0:1","Unique($2,$LINK)");    
    relationship.setConstraintDefinition("1:1","Mandatory($2,$LINK); "+
					 "Unique($2,$LINK)");    
    relationship.setConstraintDefinition("1:N","Mandatory($2,$LINK)");    
    // we want an edge between two or more existing entities and we
    // want two cardinality constraints, one for each entity
    // We just want the edgeName and entity references to be key
    relationship.addEdgeNameScheme();
    relationship.addReferenceScheme(relationshipTarget,2,0);

    // ATTRIBUTE ...
    Construct attribute=
      eer.createConstruct("attribute",Construct.CLASS_LINK_NODAL,true);
    attribute.
      setVertexDrawable(DrawEERAttributeVertex.class.getName());
    attribute.addReferenceScheme(entityOrRelationship);
    attribute.addNodeNameScheme();
    attribute.addConstraintScheme(false); // constraint's not key
    attribute.setConstraintDefinition("notnull",
      "Mandatory($1,$LINK); Unique($1,$LINK); Mandatory($NODE,$LINK)");    
    attribute.setConstraintDefinition("null",
      "Unique($1,$LINK); Mandatory($NODE,$LINK)");    

    // GENERALISATION ...
    Construct generalisation=
      eer.createConstruct("generalisation",Construct.CLASS_CONSTRAINT,true);
    generalisation.
      setVertexDrawable(DrawERGeneralisationVertex.class.getName());
    generalisation.
      setArcDrawable(DrawERGeneralisationArc.class.getName());
    generalisation.addLabelScheme();
    generalisation.addLabelScheme(false);
    generalisation.addReferenceScheme(entity);
    generalisation.addReferenceScheme(entity,2,0,false);
  
    System.out.println("Created EER Model");
  }


  /*
   * Define an relational modelling language, using nodes, edges and
   * constraints
   */
  public static void createRelationalModel() 
    throws InconsistentException,
    AutoMedException, SQLException, IOException {
    // Zap any existing model called "rel"
    if (Model.exists("rel")) {
      System.out.println("Removed old rel modelling language");
      Model.getModel("rel").retract();
    }

    Model rel=Model.createModel("rel"); 
    rel.setBackgroundColor(new Color(64,64,255,128));

    // TABLE
    Construct table=
      rel.createConstruct("table",Construct.CLASS_NODAL,true);

    table.addNodeNameScheme();

    // FIELD
    Construct field=
      rel.createConstruct("field",Construct.CLASS_LINK_NODAL,true);

    field.addReferenceScheme(table);
    field.addEdgeNameScheme();  // should be addNodeNameScheme()? mb - 11/04/02
    field.addLabelScheme(); // TODO fudged card until card constraint construct
                            // is done 01/02/02 - mb

    // PRIMARY KEY
    Construct primary_key=
      rel.createConstruct("primary_key",Construct.CLASS_CONSTRAINT,true);
    // TODO: where's the generic constraint string? mb - 01/02/02
    
    primary_key.addLabelScheme(1, 1, true);
    primary_key.addReferenceScheme(table, 1, 1, true);
    primary_key.addReferenceScheme(field, 1, 1, true);
    primary_key.setConstraintDefinition("key",
            Construct.CONSTRAINT_REFLEXIVE + ":0:" + HDMWrapperFactory.HDM_NODE_CONS_TYPE +
            ":1:" + HDMWrapperFactory.HDM_EDGE_CONS_TYPE);
    // TODO: shouldn't the above be 1..n ?  mb - 01/02/02

    // Get "uk.ac.ic.doc.automed.reps.AutoRepException: 
    // Can't find value position in construct scheme"
    // if this is not present
    Construct foreign_key=
      rel.createConstruct("foreign_key",Construct.CLASS_CONSTRAINT,true);
    // TODO: where's the generic constraint string? mb - 01/02/02

    foreign_key.addLabelScheme(1, 1, true);
    foreign_key.addReferenceScheme(table, 1, 1, true);
    foreign_key.addReferenceScheme(field, 1, 1, true);
    foreign_key.addReferenceScheme(table, 1, 1, true);
    foreign_key.addReferenceScheme(field, 1, 1, true);
    foreign_key.setConstraintDefinition("key",
            Construct.CONSTRAINT_INCLUSION + ":1:" + HDMWrapperFactory.HDM_NODE_CONS_TYPE +
            ":3:" + HDMWrapperFactory.HDM_NODE_CONS_TYPE);

    System.out.println("Created Relational Model");
  }

  public static void main(String[] args) {
    try {
      System.out.println("AutoMed Repository: Define Models");
      createERModel();
      createEERModel();
      createRelationalModel();
    }
    catch (InconsistentException aie) {
      aie.printStackTrace();
    }
    catch (SQLException se) { 
      se.printStackTrace();
    }
    catch (IOException ie) {
      ie.printStackTrace();
    }
    catch (AutoMedException ame) {
      ame.printStackTrace();
    }      
  }
}
