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

/**
 * RIPE attribute.
 *
 * @author ottrey@ripe.net
 * @version $Version$
 *
 */
public class AttributeDef implements Cloneable {
  
  final static int QI_SQL   = 1;
  final static int QI_RADIX = 2;

  private String name;
  private String altName;
  private String code;
  private String status;

  private String description;
  private String format;

  private boolean lookup;
  private boolean inverse;
  private boolean primary;
  private String foreign;
  private String keytype;

  private String insert;
  private String insertQ_type;
  private String update;
  private String updateQ_type;
  private String dummy;
  private String dummyQ_type;
  private String select;
  private String selectQ_type;

  private String choice;
  private String number;
    
  // radix tree representation
  private String family;
  private String load_ipv4; // query to load the ipv4 tree
  private String load_ipv6; // query to load the ipv6 tree

  private Vector queries;

  // -----------------oOo-----------------
  //              Constructors
  // -----------------oOo-----------------
  /**
   * Creates a RIPE attribute.
   *               
   * @author ottrey@ripe.net
   * @version $Version$
   *               
   * @param obj The node from which a RIPE attribute is made.
   * 
   */
  public AttributeDef(Node obj) {
    name      = obj.getAttributes().getNamedItem("name").getNodeValue();
    code      = obj.getAttributes().getNamedItem("code").getNodeValue();
    status    = obj.getAttributes().getNamedItem("status").getNodeValue();

    // Blindly ask for the optional items.
    try {
      altName   = obj.getAttributes().getNamedItem("altName").getNodeValue();
    }
    catch (NullPointerException e) {
      altName = new String();
      // Throw the exception away.  :-)
    }

    // Prepare to walk the tree.
    TreeWalker tw = new TreeWalker(obj);

    // Get the "description" node.
    description = getNodeRawValue(tw.getNextElement("description"));

    // Get the "format" node.
    format = getNodeRawValue(tw.getNextElement("format"));

    // Initialize
    foreign = new String();
    lookup = false;
    inverse = false;
    primary = false;

    insert       = new String();
    insertQ_type = new String("UD_NULL_");
    update       = new String();
    updateQ_type = new String("UD_NULL_");
    dummy        = new String();
    dummyQ_type  = new String("UD_NULL_");
    select       = new String();
    selectQ_type = new String("UD_NULL_");
    
    queries = new Vector();

    Node rp = tw.getNextElement("representation");
    if (rp != null) {
      // Get the insert.
      Node in = (new TreeWalker(rp)).getNextElement("insert");
      if (in != null) {
        insert = getTextFromNode(in);
	if( insert.length() > 0 ) {
	    insert = " " + insert + " ";
	}
        try {
          insertQ_type = in.getAttributes().getNamedItem("qtype").getNodeValue().toUpperCase();
        }
        catch (NullPointerException e) {
        }
      }

      // Get the updates.
      Node un = (new TreeWalker(rp)).getNextElement("update");
      if (un != null) {
	update = getTextFromNode(un);
	if( update.length() > 0 ) {
	      update = " " + update + " ";
	}
        try {
          updateQ_type = un.getAttributes().getNamedItem("qtype").getNodeValue().toUpperCase();
        }
        catch (NullPointerException e) {
        }
      }

      // Get the dummies.
      Node dn = (new TreeWalker(rp)).getNextElement("dummy");
      if (dn != null) {
	dummy =  getTextFromNode(dn);
	if( dummy.length() > 0 ) {
	      dummy = " " + dummy + " ";
	}
        try {
          dummyQ_type = dn.getAttributes().getNamedItem("qtype").getNodeValue().toUpperCase();
        }
        catch (NullPointerException e) {
        }
      }

      // Get the selects.
      Node sn = (new TreeWalker(rp)).getNextElement("select");
      if (sn != null) {
        select = getTextFromNode(sn);
	if( select.length() > 0 ) {
	      select = " " + select + " ";
	}
        try {
          selectQ_type = sn.getAttributes().getNamedItem("qtype").getNodeValue().toUpperCase();
        }
        catch (NullPointerException e) {
        }
      }

      Node rxtrees = (new TreeWalker(rp)).getNextElement("radixtrees");
      if (rxtrees != null) {
	  // no protection. It must be defined.
	  family = rxtrees.getAttributes().getNamedItem("family").getNodeValue();
	  
	  Node ipv4_n = (new TreeWalker(rxtrees)).getNextElement("IP_V4");
	  if( ipv4_n != null) {
	      load_ipv4 = getTextFromNode(ipv4_n);
	  }
	  
	  Node ipv6_n = (new TreeWalker(rxtrees)).getNextElement("IP_V6");
	  if( ipv6_n != null) {
	      load_ipv6 = getTextFromNode(ipv6_n);
	  }
      } // rxtrees != null

    } // rp!=NULL

    Node kn = tw.getNextElement("keys");
    if (kn != null) {
      String searchable = kn.getAttributes().getNamedItem("searchable").getNodeValue();
      inverse = searchable.equals("inverse");
      lookup = searchable.equals("lookup");

      TreeWalker fw = new TreeWalker(kn);
      Node f = fw.getNextElement("foreign");
      if (f != null) {
        foreign = f.getAttributes().getNamedItem("value").getNodeValue();
      }

      TreeWalker pw = new TreeWalker(kn);
      Node p = pw.getNextElement("primary");
      if (p != null) {
        primary = true;
      }

      // Get the queries.
      Node qsn = (new TreeWalker(kn)).getNextElement("queries");

      appendQueries(queries, qsn, "sqlquery",  code);
      appendQueries(queries, qsn, "radixquery",code);
    }

    // Now check cominations.
    // XXX TODO

  } // AttributeDef()

  private void appendQueries(Vector queries, Node qsn, String qrytype, String attrcode) {
    if (qsn != null) {
      TreeWalker qsw;
      Node q;
      String qryt;

      qsw = new TreeWalker(qsn);
      while ((q = qsw.getNextElement(qrytype)) != null) {
        String keytype = q.getAttributes().getNamedItem("keytype").getNodeValue();

        // Blindly get the optional values.
        String clars = new String();
        try {
          clars = q.getAttributes().getNamedItem("class").getNodeValue();
        }
        catch (NullPointerException e) {
	    // XXX take the default
	  clars = attrcode;
        }

        String space = new String();
        try {
	    space = q.getAttributes().getNamedItem("space").getNodeValue();
	}
        catch (NullPointerException e) {
        }


	String sqlQuery = getTextFromNode(q);
	//System.err.println("sqlquery = " + sqlQuery);

	if ( qrytype.equals("sqlquery") ) {
	    qryt = "SQL";
	} else { 
	    qryt = "RADIX";
	}

        Query query = new Query(qryt, lookup, keytype, code, clars, sqlQuery);
        queries.addElement(query);
      }
    }
  } // getQueries()


    
    // getting parsed contents of the text node is not simple.
    // see http://www.developerlife.com/xmljavatutorial1/default.htm
    
    // it was made simpler by the getNodeValue(Node n) method 
    // defined below, but it operated on raw XML text fragments
    
private String getTextFromNode( Node q ) {
    Element query_elem = (Element) q;
    NodeList list = query_elem.getChildNodes();
    int size = list.getLength();
    
    for (int i = 0 ; i < size ; i ++ ){
	String value =
	    ((Node)list.item( i )).getNodeValue().trim();
	//System.err.println("i=" + i + " val=" + value );
	
	if( value.equals("") || value.equals("\r") ){
	    continue; //keep iterating
	}
	else{
	    return value;
	}
    }
    
    return "";
  }
  /**
   * Aaaargh I shouldn't have to write this. :-(
   *
   * @param        node
   * @return       The value of the node.
   * @see          ClassDef
   *
   */
  private String getNodeRawValue(Node node) {
    String nodeStr = node.toString();
    int startIndex = nodeStr.indexOf('>') + 1;
    int endIndex = nodeStr.lastIndexOf('<') - 1;
    
    return nodeStr.substring(startIndex, endIndex);
  } // getNodeRaw Value()
  
  public String getFamily() {
    return family;
  } 

  public String getV4Load() {
    return load_ipv4;
  } 

  public String getV6Load() {
    return load_ipv6;
  } 

  public String getCode() {
    return code;
  } // getCode()

  public String getName() {
    return name;
  } // getName()

  public String getAltName() {
    return altName;
  } // getAltName()

  public String getStatus() {
    return status;
  } // getStatus()

  public String getDescription() {
    return description;
  } // getDescription()

  public String getFormat() {
    return format;
  } // getFormat()

  public String getEnum() {
    return new String("A_" + code).toUpperCase();
  } // getEnum()

  public String getChoice() {
    return choice;
  } // getChoice()

  public String getNumber() {
    return number;
  } // getNumber()

  public String getKeytype() {
    return keytype;
  } // getKeytype()

  public String getInsert() {
    return insert;
  } // getInsert()

  public String getInsertQ_type() {
    return insertQ_type;
  } // getInsertQ_type()

  public String getUpdate() {
    return update;
  } // getUpdate()

  public String getUpdateQ_type() {
    return updateQ_type;
  } // getUpdateQ_type()

  public String getDummy() {
    return dummy;
  } // getDummy()

  public String getDummyQ_type() {
    return dummyQ_type;
  } // getDummyQ_type()

  public String getSelect() {
    return select;
  } // getSelect()

  public String getSelectQ_type() {
    return selectQ_type;
  } // getSelectQ_type()

  public String getKeytype2() {
    String result = new String();

    if      (!lookup && !inverse && !primary) {
      result = " ";
    }
    else if (!lookup && !inverse &&  primary) {
      result = "primary key";
    }
    else if (!lookup &&  inverse && !primary) {
      result = "inverse key";
    }
    else if (!lookup &&  inverse &&  primary) {
      result = "primary/inverse key";
    }
    else if ( lookup && !inverse && !primary) {
      result = "lookup key";
    }
    else if ( lookup && !inverse &&  primary) {
      result = "primary/look-up key";
    }
    else if ( lookup &&  inverse && !primary) {
      result = "look-up/inverse key";
    }
    else if ( lookup &&  inverse &&  primary) {
      result = "Gimmie a break!";
    }

    return result;
  } // getKeytype()

  public String getKeytype3() {
    String result = new String();
    
    if (primary) {
      result = "[P]";
    }
    else  {
      result = "   ";
    }

    if (inverse) {
      result += "[I]";
    }
    else if (lookup) {
      result += "[L]";
    }
    else {
      result += "   ";
    }

    return result;
  } // getKeytype()

  public String getForeign() {
    return foreign;
  } // getForeign()

  public boolean getInverse() {
    return inverse;
  } // getInverse()

  public boolean getPrimary() {
    return primary;
  } // getPrimary()

  public Vector getQueries() {
    return queries;
  } // getQueries()

  public boolean setChoice(String choice) {
    boolean result=true;

    this.choice = choice;

    return result;
  } // setChoice()

  public boolean setNumber(String number) {
    boolean result=true;

    this.number = number;

    return result;
  } // setNumber()

  public Object clone() throws CloneNotSupportedException {
    return (AttributeDef)super.clone();
  } // clone()

  /*
  public boolean equals(String code) {
    return code.equals(code);
  } // equals()
  */
  
  public String toString() {
    return new String("ripe attribute={" +
                         "\n\tname="        + name        +
                         "\n\taltName="     + altName     +
                         "\n\tcode="        + code        +
                         "\n\tstatus="      + status      +
                         "\n\tkeytype="     + keytype     +
                         "\n\tdescription=" + description +
                         "\n\tformat="      + format      +
                         "\n\tchoice="      + choice      +
                         "\n\tnumber="      + number      +
                         "\n}");
  } // toString()


} // AttributeDef
