import java.io.*;
import java.util.*;
import com.sun.xml.parser.*;
import com.sun.xml.tree.*;
import org.xml.sax.*;
import org.w3c.dom.*;

/**
 * RIPE classes generated from Data Object Models.
 *
 * @author ottrey@ripe.net
 * @version $Version$
 *
 */
public class Defs {


  private Hashtable ripeAttributes;
  private Vector ripeAttributeCodes; 
  private Vector ripeClasses;
  private Vector ripeAttributeAliases;
  private Vector ripeAttributeAliasesMap;
  private Vector ripeClassAliases;
  private Vector ripeClassAliasesMap;
  private Vector ripeQueries;


  // -----------------oOo-----------------
  //              Constructors
  // -----------------oOo-----------------
  public Defs(boolean normalize) {
    // Create a normalized DOM from the xml file for the attributes.
    XmlDocument attrDOM = getDOM("attributes.xml", "ripe_attribute", normalize);

    // Initialize.
    ripeAttributes = new Hashtable();
    ripeAttributeCodes = new Vector();
    ripeAttributeAliases = new Vector();
    ripeAttributeAliasesMap = new Vector();
    ripeClassAliases = new Vector();
    ripeClassAliasesMap = new Vector();
    ripeQueries = new Vector();

    // Recurse through node tree
    NodeList attrNodes = attrDOM.getElementsByTagName("ripe_attribute");
    for (int i=0; i < attrNodes.getLength(); i++) {
      // (Checking if the attribute is valid)
      if (validate("attribute", attrNodes.item(i))) {
        AttributeDef ad = new AttributeDef(attrNodes.item(i));
        
        // and each attribute,
        ripeAttributes.put(ad.getCode(), ad);

        // and it's code.
        ripeAttributeCodes.addElement(ad.getCode());

        // and it's aliases.
        // also map the alias to the attribute index.

        // set the index to map to.
        Integer mapIndex = new Integer(ripeAttributeCodes.size()-1);

        //  first the code.
        ripeAttributeAliases.addElement(ad.getCode());
        ripeAttributeAliasesMap.addElement(mapIndex);

        //  then the name.
        ripeAttributeAliases.addElement(ad.getName());
        ripeAttributeAliasesMap.addElement(mapIndex);

        if (ad.getAltName().length() > 1) {
          //  then the altName.
          ripeAttributeAliases.addElement(ad.getAltName());
          ripeAttributeAliasesMap.addElement(mapIndex);
        }
      }
    }

    // Create a normalized DOM from the xml file for the classes.
    XmlDocument objDOM = getDOM("classes.xml", "ripe_class", normalize);

    // Create a vector to store the classes.
    ripeClasses = new Vector();

    // Recurse through node tree
    NodeList objNodes = objDOM.getElementsByTagName("ripe_class");
    for (int i=0; i < objNodes.getLength(); i++) {
      // Check if the class is valid
      if (validate("class", objNodes.item(i))) {
        ClassDef od = new ClassDef(objNodes.item(i), ripeAttributes);
        
        // Add the class.
        ripeClasses.addElement(od);

        // set the index to map to.
        Integer mapIndex = new Integer(ripeClasses.size()-1);

        //  first the code.
        ripeClassAliases.addElement(od.getCode());
        ripeClassAliasesMap.addElement(mapIndex);

        //  then the name.
        ripeClassAliases.addElement(od.getName());
        ripeClassAliasesMap.addElement(mapIndex);

      }
    }
    
    // replace class/attribute variables in queries
    


  } // Defs()
    
    public String getValueByEnum(String name) {
	Enumeration e = ripeClasses.elements();
	for( int i = 0; e.hasMoreElements();  i++) {
	    ClassDef d = (ClassDef)e.nextElement();
	    String a = d.getEnum();
	    
	    //System.out.println( d );
	    
	    if( name.equals(a) ) {
		return (new Integer(i)).toString();
	    }
	}
	System.out.println("ERROR: cannot resolve variable name " + name );
	System.exit(-1);
	
	return ""; // bloody idiot, the compiler
    }
	    
	    

  /** 
   * Creates a Data Object Model from the RIPE classes defined
   * in the XML document.
   *               
   * @author ottrey@ripe.net
   * @version $Version$
   *               
   * @param xmlDocName The URI of the XML document.
   * @param ripeClass  The class to be created from.
   * @param normalize  Return a normalized DOM.
   *
   */
    private XmlDocument getDOM(String xmlDocName, String ripeClass, boolean normalize) {

    // Convert filename to an input source.
    InputSource inSrc= new InputSource();
    try {
      inSrc = Resolver.createInputSource(new File(xmlDocName));
    }
    catch (IOException e) {
      System.err.println("Failed to convert filename: " + xmlDocName +
                         "to an input source" + e);
      e.printStackTrace();  System.exit(-1);
    }

    // Create and validate a DOM.
    XmlDocument dom=null;
    try {
      dom = XmlDocument.createXmlDocument(inSrc, true);
    }
    catch (SAXException e) {
      System.err.println("Failed to create DOM & validate." + e);
      e.printStackTrace();  System.exit(-1);
    }
    catch (IOException e) {
      System.err.println("Failed to create DOM & validate." + e);
      e.printStackTrace();  System.exit(-1);
    }

    // Normalize the document.
    if (normalize) {
      dom.getDocumentElement().normalize();
    }

    return dom;

  } // getDOM()

  private boolean validate(String type, Node obj) {
    boolean result=false;
    String status = obj.getAttributes().getNamedItem("status").getNodeValue(); 
    String name   = obj.getAttributes().getNamedItem("name").getNodeValue();

    if (status.equals("valid")) {
      result=true;
    }
    else {
      System.err.println("Warning: " + type + " " + name + " is " + status);
    }

    return result;
  } // validClass()


  // -----------------oOo-----------------
  //              Print Methods
  // -----------------oOo-----------------
  private void printDF_attribute_aliases() {
    System.out.println("char * const Attribute_aliases[] = {");
    Enumeration e = ripeAttributeAliases.elements();
    while (e.hasMoreElements()) {
      String a = (String)e.nextElement();
      System.out.println("  \"" + a + "\",");
    }
    System.out.println("  " + "NULL" + "\n" + "}; /* Attribute_aliases */");
  } // printDF_attribute_aliases()

  private void printDF_attribute_aliases_map() {
    System.out.println("int const Attribute_aliases_map[] = {");
    Enumeration e = ripeAttributeAliasesMap.elements();
    while (e.hasMoreElements()) {
      Integer am = (Integer)e.nextElement();
      System.out.println("  " + am + ",");
    }
    System.out.println("  " + "NULL" + "\n" + "}; /* Attribute_aliases_map */");
  } // printDF_attribute_aliases_map()

  private void printDF_attribute_codes() {
    System.out.println("char * const Attribute_codes[] = {");
    Enumeration e = ripeAttributeCodes.elements();
    while (e.hasMoreElements()) {
      String ac = (String)e.nextElement();
      AttributeDef ad = (AttributeDef)ripeAttributes.get(ac);

      // If the attribute has status="valid".
      if (ad.getStatus().equals("valid")) {
        // Output the attribute code.
        System.out.println("  \"" + ad.getCode() + "\",");
      }
    }
    System.out.println("  " + "NULL" + "\n" + "}; /* Attribute_codes */");
  } // printDF_attribute_codes()

  private void printDF_attribute_enum() {
    System.out.println("typedef enum _A_Type_t {");

    // Enumerate through the attribute codes.
    Enumeration e = ripeAttributeCodes.elements();
    while (e.hasMoreElements()) {
      String ac = (String)e.nextElement();
      AttributeDef ad = (AttributeDef)ripeAttributes.get(ac);

      // If the attribute has status="valid".
      if (ad.getStatus().equals("valid")) {
        // Output the attribute enum.
        System.out.println("  " + ad.getEnum() + ",");
      }
    }

    System.out.println("  " + "A_END" + "\n" + "} A_Type_t;");
  } // printDF_attribute_enum()

  private void printDF_attribute_inv_attr_mask() {
    System.out.print("#define INV_ATTR_MASK ");

    // Enumerate through the attribute codes.
    Enumeration e = ripeAttributeCodes.elements();
    while (e.hasMoreElements()) {
      String ac = (String)e.nextElement();
      AttributeDef ad = (AttributeDef)ripeAttributes.get(ac);

      // If the attribute has status="valid".
      if (ad.getStatus().equals("valid") && ad.getInverse()) {
        // Output the attribute enum.
        System.out.print(ad.getEnum() + ", ");
      }
    }

    System.out.println("MA_END");
  } // printDF_attribute_inv_attr_mask()

  private void printDF_attribute_names() {
    System.out.println("char * const Attribute_names[] = {");
    Enumeration e = ripeAttributeCodes.elements();
    while (e.hasMoreElements()) {
      String ac = (String)e.nextElement();
      AttributeDef ad = (AttributeDef)ripeAttributes.get(ac);

      // If the attribute has status="valid".
      if (ad.getStatus().equals("valid")) {
        // Output the attribute name.
        System.out.println("  \"" + ad.getName() + "\",");
      }
    }
    System.out.println("  " + "NULL" + "\n" + "}; /* Attribute_names */");
  } // printDF_attribute_names()

  private void printDF_class_aliases() {
    System.out.println("char * const Class_aliases[] = {");
    Enumeration e = ripeClassAliases.elements();
    while (e.hasMoreElements()) {
      String a = (String)e.nextElement();
      System.out.println("  \"" + a + "\",");
    }
    System.out.println("  " + "NULL" + "\n" + "}; /* Class_aliases */");
  } // printDF_class_aliases()

  private void printDF_class_aliases_map() {
    System.out.println("int const Class_aliases_map[] = {");
    Enumeration e = ripeClassAliasesMap.elements();
    while (e.hasMoreElements()) {
      Integer am = (Integer)e.nextElement();
      System.out.println("  " + am + ",");
    }
    System.out.println("  " + "NULL" + "\n" + "}; /* Class_aliases_map */");
  } // printDF_class_aliases_map()

  private void printDF_class_codes() {
    System.out.println("char * const Class_codes[] = {");
    Enumeration e = ripeClasses.elements();
    while (e.hasMoreElements()) {
      ClassDef od = (ClassDef)e.nextElement();
      System.out.println("  \"" + od.getCode() + "\",");
    }
    System.out.println("  " + "NULL" + "\n" + "}; /* Class_codes */");
  } // printDF_class_codes()

  private void printDF_class_dbase_code_map() {
    System.out.println("int const Class_dbase_code_map[] = {");
    Enumeration e = ripeClasses.elements();
    while (e.hasMoreElements()) {
      ClassDef cd = (ClassDef)e.nextElement();
      System.out.println("  " + cd.getDbaseCode() + ",");
    }
    System.out.println("  " + "NULL" + "\n" + "}; /* Class_dbase_code_map */");
  } // printDF_class_dbase_code_map()

  private void printDF_class_enum() {
    System.out.println("typedef enum _C_Type_t {");
    Enumeration e = ripeClasses.elements();
    while (e.hasMoreElements()) {
      ClassDef od = (ClassDef)e.nextElement();
      System.out.println("  " + od.getEnum() + ",");
    }
    System.out.println("  " + "C_END" + "\n" + "} C_Type_t;");
  } // printDF_class_enum()

  private void printDF_class_mask() {
    System.out.print("#define CLASS_MASK ");
    Enumeration e = ripeClasses.elements();
    while (e.hasMoreElements()) {
      ClassDef cd = (ClassDef)e.nextElement();
      System.out.print(cd.getEnum() + ", ");
    }
    System.out.println("MA_END");
  } // printDF_class_mask()

  private void printDF_class_names() {
    System.out.println("char * const Class_names[] = {");
    Enumeration e = ripeClasses.elements();
    while (e.hasMoreElements()) {
      String a = ((ClassDef)e.nextElement()).getName();
      System.out.println("  \"" + a + "\",");
    }
    System.out.println("  " + "NULL" + "\n" + "}; /* Class_names */");
  } // printDF_class_names()

  private void printQI_queries() {
    System.out.println(Query.startDoc());
    Enumeration e1 = ripeAttributes.elements();
    while (e1.hasMoreElements()) {
      AttributeDef ad = (AttributeDef)e1.nextElement();
      Enumeration e2 = ad.getQueries().elements();
      while (e2.hasMoreElements()) {
        Query q = (Query)e2.nextElement();
        System.out.println(q.getStruct("  ", this));
      }
    }
    System.out.println(Query.endDoc());
  } // printQI_queries()

  private void printUD_queries() {

    Enumeration e;

    System.out.println("UD_query Insert[] = {");
    e = ripeAttributeCodes.elements();
    while (e.hasMoreElements()) {
      String ac = (String)e.nextElement();
      AttributeDef ad = (AttributeDef)ripeAttributes.get(ac);
      System.out.println("  {" + ad.getInsertQ_type() + ", " + "\"" +  ad.getInsert() + "\"},");
    }
    System.out.println("  " + "{0, NULL}" + "\n" + "}; /* Insert */\n");


    System.out.println("UD_query Update[] = {");
    e = ripeAttributeCodes.elements();
    while (e.hasMoreElements()) {
      String ac = (String)e.nextElement();
      AttributeDef ad = (AttributeDef)ripeAttributes.get(ac);
      System.out.println("  {" + ad.getUpdateQ_type() + ", " + "\"" +  ad.getUpdate() + "\"},");
    }
    System.out.println("  " + "{0, NULL}" + "\n" + "}; /* Update */\n");

    System.out.println("UD_query Dummy[] = {");
    e = ripeAttributeCodes.elements();
    while (e.hasMoreElements()) {
      String ac = (String)e.nextElement();
      AttributeDef ad = (AttributeDef)ripeAttributes.get(ac);
      System.out.println("  {" + ad.getDummyQ_type() + ", " + "\"" +  ad.getDummy() + "\"},");
    }
    System.out.println("  " + "{0, NULL}" + "\n" + "}; /* Dummy */\n");

    System.out.println("UD_query Select[] = {");
    e = ripeAttributeCodes.elements();
    while (e.hasMoreElements()) {
      String ac = (String)e.nextElement();
      AttributeDef ad = (AttributeDef)ripeAttributes.get(ac);
      System.out.println("  {" + ad.getSelectQ_type() + ", " + "\"" +  ad.getSelect() + "\"},");
    }
    System.out.println("  " + "{0, NULL}" + "\n" + "}; /* Select */\n");

  } // printUD_queries()

  private void printTemplates() {
    Enumeration e = ripeClasses.elements();
    while (e.hasMoreElements()) {
      ClassDef cd = (ClassDef)e.nextElement();
      System.out.println(cd.getName() + "\n");
      System.out.println(cd.getTemplate(false) + "\n");
    }
  } // printTemplates()

  private void printDF_class_templates() {
    Enumeration e = ripeClasses.elements();
    System.out.println("const char *Templates[] = {");
    while (e.hasMoreElements()) {
      ClassDef cd = (ClassDef)e.nextElement();
      System.out.print(cd.getTemplate(true));
      System.out.println(",");
    }
    System.out.println("NULL");
    System.out.println("}; /* Templates */");
  } // printDF_class_templates()

  private void printDF_class_templates_v() {
    Enumeration e = ripeClasses.elements();
    System.out.println("const char *Templates_v[] = {");
    while (e.hasMoreElements()) {
      ClassDef od = (ClassDef)e.nextElement();
      System.out.print(od.getTemplateV(true));
      System.out.println(",");
    }
    System.out.println("NULL");
    System.out.println("}; /* Templates_v */");
  } // printDF_class_templates_v()

  private void printTemplatesV() {
    Enumeration e = ripeClasses.elements();
    while (e.hasMoreElements()) {
      ClassDef od = (ClassDef)e.nextElement();
      System.out.println(od.getName() + "\n");
      System.out.println(od.getTemplateV(false) + "\n");
    }
  } // printTemplatesV()

  private void printDiagrams() {
    int maxWidth=0;  // Widest diagram
    Hashtable foreigns = new Hashtable();

    Enumeration e1 = ripeClasses.elements();
    while (e1.hasMoreElements()) {
      ClassDef od = (ClassDef)e1.nextElement();
      if (maxWidth < od.getWidth()) {
        maxWidth = od.getWidth();
      }

      Hashtable foriegnAttrs = od.getForeignAttrs();
      if (foriegnAttrs != null) {
        Enumeration e2 = foriegnAttrs.keys();
        while (e2.hasMoreElements()) {
          String key = (String)e2.nextElement();
          if (!foreigns.containsKey(key)) {
            foreigns.put(key, foriegnAttrs.get(key));
          }
        }
      }
    }

    System.out.print("Classes:");
    for (int i=0; i < maxWidth; i++) {
      System.out.print(" ");
    }
    System.out.println("Foreign keys:");

    Enumeration e3 = ripeClasses.elements();
    while (e3.hasMoreElements()) {
      ClassDef od = (ClassDef)e3.nextElement();
      System.out.print(od.getDiagram(maxWidth, foreigns));
    }
  } // printDiagrams()


  // -----------------oOo-----------------
  //            Unit test driver
  // -----------------oOo-----------------
  public static void main (String argv[]) {
    int n=0;
    boolean err=true;
    boolean normalize=false;

    if (argv.length > 0) {
      try {
        n = Integer.parseInt(argv[0]);
        err=false;
      }
      catch (NumberFormatException e) {
      }
      if (argv.length == 2) {
        if (argv[1].equals("normalize")) {
          normalize = true;
        }
      }
    }

    if (!err) {
      Defs rc = new Defs(normalize);

      switch (n) {
        case  1: rc.printDF_attribute_aliases();          break; 
        case  2: rc.printDF_attribute_aliases_map();      break; 
        case  3: rc.printDF_attribute_codes();            break; 
        case  4: rc.printDF_attribute_enum();             break; 
        case  5: rc.printDF_attribute_inv_attr_mask();    break; 
        case  6: rc.printDF_attribute_names();            break; 
        case  7: rc.printDF_class_aliases();              break; 
        case  8: rc.printDF_class_aliases_map();          break; 
        case  9: rc.printDF_class_codes();                break; 
        case 10: rc.printDF_class_dbase_code_map();       break; 
        case 11: rc.printDF_class_enum();                 break; 
        case 12: rc.printDF_class_mask();                 break; 
        case 13: rc.printDF_class_names();                break; 
        case 14: rc.printQI_queries();                    break; 
        case 15: rc.printUD_queries();                    break; 
        case 16: rc.printDF_class_templates();            break; 
        case 17: rc.printDF_class_templates_v();          break; 
        case 18: rc.printDiagrams();                      break; 
        case 19: rc.printTemplates();                     break; 
        case 20: rc.printTemplatesV();                    break; 
        default:
          err=true;
      }
    }

    if (err) {
      System.err.println("Usage: makedefs n (Where n = a number 1..21)");
    }

  } // main()
  // -----------------oOo-----------------

} // Defs
