import java.sql.*;
import java.io.*;
import uk.ac.ic.doc.automed.*;
import uk.ac.ic.doc.automed.reps.*;

public class ERDemo {
  private static Construct entity;
  private static Construct cardConstraint;
  private static Construct entityRole;
  private static Construct relationship;
  private static Construct attribute;
  private static Construct generalisation;

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


  /*
   * Define an ER+ modelling language, with entities, attributes,
   * n-ary relationships (with attributes) and generalisations
   */
  public static void createERModel() throws InconsistentException, 
  AutoMedException, SQLException, IOException {

    // Zap any existing model called "er"
    if (Model.exists("er+")) {
      Model.getModel("er+").retract();
      System.out.println("Removed old er+ modelling language");
    }

    System.out.println("Creating er+ modelling language");

    Model er=Model.createModel("er+"); 

    // Create the four constructs this particular ER modelling language
    // note the defaults for all addXXXScheme = isKey=tryue. cardinality=1..1

    // ********* ENTITY *********
    // Entities can stand by themselves; hence NODAL
    // 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

    entity = er.createConstruct("entity",
				Construct.CLASS_NODAL,
				true);
    entity.addNodeNameScheme(); // the entity name

      
    // ********* RELATIONSHIP *********
    // Relationships link existing entities and hence are LINK
    // Each entity in the relationship has a cardinality associated with it so
    // we first need an entityRole construct which itself requires a
    // cardinality constraint construct.  To enable us to have n-ary entities
    // each with an associated constraint we need to use a sequence construct
    // which we can then use with 2..n in the relationship scheme
    //
    // Note the false to isRoot in createConstruct for cardConstraint and
    // entityRole, you can't create a cardinality constraint outside
    // the context of an entityRole and you can't create an entityRole
    // outside the context of a relationship
    //
    // The lower and upper bound schemes of cardConstraint make up the
    // cardinality constraint of the associated entity in the relationship
    // in the er+ modelling language and can only be enforced through the 
    // constraint string given to the constraint construct.  The 2..N
    // (expressed as 2..0) for the entityRole scheme of relationship states
    // how many entity roles may be given for a relationship and is enforced
    // when schema objects of construct type relationship are created.

    // TODO: we need something like this - mb 04/02/02
    // cardConstraint.setConstraintFormule("blah blah $1 blah $2");

    cardConstraint = er.createConstruct("cardinalityConstraint",
					Construct.CLASS_CONSTRAINT,
					false);
    cardConstraint.addLabelScheme(); // lower bound
    cardConstraint.addLabelScheme(); // upper bound

    entityRole = er.createConstruct("entityRole",
				    Construct.CLASS_SEQUENCE,
				    false);
    entityRole.addReferenceScheme(entity);
    entityRole.addReferenceScheme(cardConstraint, false);
    entityRole.addLabelScheme(0, 1, false); // optional role name
    

    relationship = er.createConstruct("relationship",
				      Construct.CLASS_LINK,
				      true);
    relationship.addEdgeNameScheme();
    relationship.addReferenceScheme(entityRole, 2, 0); // i.e. 2..N


    // ********* ATTRIBUTE *********
    // Attributes have values not present in other constructs, but must be
    // associated to other objects; hence LINK_NODAL
    // In this flavour of er we allow attributes to associated with entities or
    // relationships, hence the alternation construct attributeObject.
    // 
    // Just like each entity role in a relationshop, each attribute has an
    // associated lower to upper cardinality constraint 1..1 means candidate
    // key, 1..n means non key attribute.
    //
    // TODO: do we need complex attribites to enable us to have a complex key?
    //       does the lower bound 1 imply that the extent of the attribute is
    //       just those values in use rather than the whole domain of possible
    //       values?  - mb 04/02/02

    Construct attributedObject= er.createConstruct("attributedObject",
						   Construct.CLASS_ALTERNATION,
						   false);
    attributedObject.addReferenceScheme(entity);
    attributedObject.addReferenceScheme(relationship);

    attribute = er.createConstruct("attribute",
				   Construct.CLASS_LINK_NODAL,
				   true);

    attribute.addReferenceScheme(attributedObject);
    attribute.addEdgeNameScheme(); // the name of the attribute
    attribute.addReferenceScheme(cardConstraint); // entity / rel cardinality
    attribute.addReferenceScheme(cardConstraint); // atttribute's cardinality
    

    // ********* GENERALISATION *********    
    // Generalisations are constraints on the values of entities, and
    // have not associated values, hence CONSTRAINT
    // 
    // The key to generalization is the name and the super entity which it
    // generalizes.

    // TODO: should we allow a lower bound of 1 on the sub entities, this with
    //       a partial generalization would be a way of expressing a subset.
    //       Still need a constraints string somewere to tie the super and
    //       sub entities together  - mb 04/02/02

    generalisation = er.createConstruct("generalisation",
					Construct.CLASS_CONSTRAINT,
					true);
    
    generalisation.addLabelScheme(); // name of generalization
    generalisation.addLabelScheme(false); // total/partial
    generalisation.addReferenceScheme(entity); // super entity
    generalisation.addReferenceScheme(entity, 2, 0, false); // sub entities
  
    System.out.println(er.getModelDescription());
  }

    /* Define ER+ schema
     */
  public static void createERSchema() 
    throws AutoRepException, AutoMedException, SQLException, IOException {
    
    //    SchemaTransformationRepository.setDebugLevel(5);

    System.out.println("Creating er+ schema");
    
    Schema.retract("ER+_s3");
    Schema s = Schema.createSchema("ER+_s3",  false);

    SchemaObject oneToOne =
      s.createSchemaObject(cardConstraint, new Object[]{"1", "1"});

    SchemaObject oneToN = 
      s.createSchemaObject(cardConstraint, new Object[]{"1", "N"});


    // person entity
    SchemaObject person = s.createSchemaObject(entity, new Object[]{"person"});

    s.createSchemaObject(attribute, new Object[]{person, "id",
						 oneToOne, oneToOne});

    s.createSchemaObject(generalisation, 
			 new Object[]{
			   "sex", "total", person,
			   s.createSchemaObject(entity, new Object[]{"male"}),
			   s.createSchemaObject(entity, new Object[]{"female"})
			 });
    
    // dept entity
    SchemaObject dept =
      s.createSchemaObject(entity, new Object[] {"dept"});
    
    s.createSchemaObject(attribute, new Object[]{dept, "dname", 
						 oneToOne, oneToOne});
    
    s.createSchemaObject(relationship, new Object[]{"worksin", 
    						    person, oneToOne,
    						    dept, oneToN});
    
    // TODO: this is really what we want to do, the createSchemaObject method
    // needs to spot that we've supplied strings where it expects a construct
    // and try to create a cardinality construct for each pair bounds
    //
    //    s.createSchemaObject(relationship, new Object[]{"worksin", 
    //						    person, "1", "1",
    //						    dept, "1", "n"});

    // degree entity
    SchemaObject degree =
      s.createSchemaObject(entity, new Object[]{"degree"});

    s.createSchemaObject(attribute, new Object[]{degree, "code", 
						 oneToOne, oneToOne});

    s.createSchemaObject(attribute,new Object[]{degree, "title",
						oneToOne, oneToN});
    
    s.createSchemaObject(relationship, new Object[]{"runby", 
						    degree, oneToOne,
						    dept, oneToN});
    
    System.out.println("======================================================================");
    System.out.println(s.getAllSchemeNamesString(true,-1,-1));
    System.out.println("======================================================================");
    System.out.println(s.getAllSchemeNamesString(false,0,1));
  }
}
