//XMLParser.js
//Base xml parser object.  Contains common parsing shells, has virtual functions for child objects
//This file is meant to be extended to handle whatever is required.  Should be able to parse
//XML-RPC messages by default; can add hooks to extend function to accomodate additional namespaces
//Dependencies:

function XMLParser(newXmlDoc)
{
  return this.init(newXmlDoc);
};

XMLParser.prototype.init = function (newXmlDoc)
{
  this.xmlDoc = newXmlDoc||null;   // XmlDocument
  if (this.xmlDoc == null)
    return (this);
};

XMLParser.prototype.setDocumentByDoc = function(newXmlDoc)
{
  if (!newXmlDoc) 
    return null;
  else
    this.xmlDoc = newXmlDoc;
};

XMLParser.prototype.setDocumentByStr = function(XMLParserStr)
{
  if (!XMLParserStr) 
    return;
  if (this.xmlDoc == null)
    this.xmlDoc = XmlDocument.create();
  this.xmlDoc.loadXML(XMLParserStr);
};

/**********************************************************
          Fault Response Functions (xml-rpc only)
***********************************************************/

XMLParser.prototype.isFault = function()
{
  /** NOTE:  This function should not be in the generic parser **/
  if (this.xmlDoc == null) return true;
  return (this.xmlDoc.getElementsByTagName("fault").length > 0);
};

XMLParser.prototype.getFaultCode = function()
{
  /** NOTE:  This function should not be in the generic parser **/
  if (this.xmlDoc == null) return (-1);
  if (!this.isFault()) return (-1);
  var members = this.xmlDoc.getElementsByTagName("member");
  for (var lcv = 0;lcv < members.length; lcv++)
  {
    var nameElem = members[lcv].getElementsByTagName("name").item(0);
    if ((nameElem == null) || (nameElem.childNodes.length == 0))  // should never happen
      return (-1);
    var nameStrNode = nameElem.childNodes.item(0);  // grab the text node
    if (nameStrNode.nodeValue == 'faultCode')
    {
      var codeNode = members[lcv].getElementsByTagName("int").item(0);  // node holding the value
      if ((codeNode == null) || (codeNode.childNodes.length == 0)) // should never happen
        return (-1);
      return (codeNode.childNodes.item(0).nodeValue);
    }
  }
  return (-1);
};


XMLParser.prototype.getFaultString = function()
{
  /** NOTE:  This function should not be in the generic parser **/
  if (this.xmlDoc == null) return ("");
  if (!this.isFault()) return ("");
  var members = this.xmlDoc.getElementsByTagName("member");
  for (var lcv = 0;lcv < members.length; lcv++)
  {
    var nameElem = members[lcv].getElementsByTagName("name").item(0);
    if ((nameElem == null) || (nameElem.childNodes.length == 0))  // should never happen
      return ("");
    var nameStrNode = nameElem.childNodes.item(0);  // grab the text node
    if (nameStrNode.nodeValue == 'faultString')
    {
      var codeNode = members[lcv].getElementsByTagName("string").item(0);  // node holding the value
      if ((codeNode == null) || (codeNode.childNodes.length == 0)) // should never happen
        return ("");
      return (codeNode.childNodes.item(0).nodeValue);
    }
  }
  return ("");
};

/**********************************************************
                      Main Parser
***********************************************************/

XMLParser.prototype.getObject = function()
{
  if (this.xmlDoc == null)
    return null;
  if (arguments.length > 0)
  {
    switch (arguments[0])
    {
      default:
        return (this.getObjectRecurse(this.xmlDoc.getElementsByTagName("value").item(0)));
    }
  }
  else
    return (this.getObjectRecurse(this.xmlDoc.getElementsByTagName("value").item(0)));
};

XMLParser.prototype.getObjectRecurse = function()  //(theNode,<widgetParent|mapConfig>)
{
  //dprintf('XMLParser.getObjectRecurse; executing native code for '+arguments[0].nodeName,false);
  var theNode = arguments[0];
  var i = 0;
  if (this.xmlDoc == null) return null;
    switch (theNode.nodeName)
    {
      case 'declarations':
        this.parseDeclarations(theNode);
        break;
      case 'value':
        return(this.getObjectRecurse(theNode.childNodes[this.__helper__getNextElementChildNode(theNode,0)]));
        break;
      case 'definedValue':
        return (document.getGuiValue(this.decodeSTRING(theNode)));
        break;
      case 'op':
      case 'string':
        return(this.decodeSTRING(theNode));
        break;
      case 'expression':
        return (this.parseExpression(theNode));
        break;
      case 'i4':
      case 'int':
        for (i=0; i<theNode.childNodes.length; i++)
          this.getObjectRecurse(theNode.childNodes[i]);
        return(this.decodeINT(theNode));
        break;
      case 'boolean':
        return(this.decodeBOOLEAN(theNode));
        break;
      case 'double':
        for (i=0; i<theNode.childNodes.length; i++)
          this.getObjectRecurse(theNode.childNodes[i]);
        return(this.decodeDOUBLE(theNode));
        break;
      case '#text':
      case '#comment':
        break;
      case 'dateTime.iso8601': return(this.decodeDATETIME(theNode)); break;
      case 'base64': return(this.decodeBASE64(theNode)); break;
      case 'array': return(this.decodeARRAY(theNode)); break;
      case 'struct': return(this.decodeSTRUCT(theNode)); break;
      default:
      {
        dprintf('<span class="criticalError">XMLParser Warning:  unknown node type: \"'+theNode.nodeName+'\"</span>',true);
        break;
      }
    }
};

/**********************************************************
              Generic data handling
***********************************************************/

XMLParser.prototype.decodeSTRING = function(theNode)
{
  if (theNode.childNodes.length > 0) // has textNode
    return (theNode.childNodes[0].nodeValue);
  else return ('');
};

XMLParser.prototype.decodeINT = function(theNode) // leaf node
{ 
  var theSTRING = this.decodeSTRING(theNode);
  return (parseInt(theSTRING,10));
};

XMLParser.prototype.decodeBOOLEAN = function(theNode)
{
  var theSTRING = this.decodeSTRING(theNode);
  return (parseInt(theSTRING) == 1);
};

XMLParser.prototype.decodeDOUBLE = function(theNode)
{
  var theSTRING = this.decodeSTRING(theNode);
  return (parseFloat(theSTRING));
};

XMLParser.prototype.decodeDATETIME = function(theNode) {};
XMLParser.prototype.decodeBASE64 = function(theNode) {};
XMLParser.prototype.decodeSTRUCT = function(theNode)
{
  var thisStruct = new Array();
  for (var lcv = 0; lcv < theNode.childNodes.length; lcv++)
  {
    var memberNode = theNode.childNodes[lcv];
    if (memberNode.nodeName == "member")
    {
      var valueNameNode = memberNode.getElementsByTagName("name").item(0);
      var valueNameStr = this.decodeSTRING(valueNameNode);
      thisStruct[valueNameStr] = this.getObjectRecurse(memberNode.getElementsByTagName("value").item(0));
    }
  }
  return (thisStruct);
};

XMLParser.prototype.decodeARRAY = function(theNode)
{
  var thisArray = new Array();
  var thisArrayPos = 0;

  var dataNode = theNode.childNodes[this.__helper__getNextElementNamedChildNode(theNode,0,"data")];
  var valueNodePos = 0;
  while (valueNodePos >= 0)
  {
    valueNodePos = this.__helper__getNextElementNamedChildNode(dataNode,valueNodePos,"value");
    if (valueNodePos >= 0)
    {
      var valueNode = dataNode.childNodes[valueNodePos];
      thisArray[thisArrayPos] = this.getObjectRecurse(valueNode.firstChild);
      thisArrayPos++;
    }
    if (valueNodePos >= 0) valueNodePos++;
  }
  return (thisArray);
};

XMLParser.prototype.parseExpression = function(expressionNode)
{
  var value1Index = this.__helper__getNextElementNamedChildNode(expressionNode,0,"value");
  var value2Index = this.__helper__getNextElementNamedChildNode(expressionNode,value1Index+1,"value");
  var operatorIndex = this.__helper__getNextElementNamedChildNode(expressionNode,0,"op");
  var value1 = this.getObjectRecurse(expressionNode.childNodes[value1Index]);
  var value2 = this.getObjectRecurse(expressionNode.childNodes[value2Index]);
  var operator = this.decodeSTRING(expressionNode.childNodes[operatorIndex]);
  var returnValue = 0;
  switch (operator)
  {
    case 'add':
      returnValue = value1+value2;
      break;
    case 'subtract':
    case 'sub':
      returnValue = value1-value2;
      break;
    case 'multiply':
    case 'mpy':
      returnValue = value1*value2;
      break;
    case 'divide':
    case 'div':
      returnValue = value1/value2;
      break;
    case 'mod':
      returnValue = value1%value2;
      break;
    default:
      alert("Unknown operator in expression: \""+operator+"\"");
      returnValue = null;
      break;
  }
  return returnValue;
};

/**********************************************************
                  Helper Functions
***********************************************************/

XMLParser.prototype.__helper__getNextElementChildNode = function(aNode,firstpos)
{
  var foundpos = -1;
  for (var lcv=firstpos;(lcv < aNode.childNodes.length) && (foundpos == -1); lcv++)
    if (aNode.childNodes[lcv].nodeType == 1)
      foundpos = lcv;
  return (foundpos);
};

XMLParser.prototype.__helper__getNextElementNamedChildNode = function(aNode,firstpos,nodeName)
{
  for (var lcv=firstpos;lcv < aNode.childNodes.length; lcv++)
    if ((aNode.childNodes[lcv].nodeType == 1) && (aNode.childNodes[lcv].nodeName == nodeName))
      return(lcv);
  return (-1);
};
XMLParser.prototype.__helper__getRemainingNextElementNamedChildNodes = function(aNode,firstpos,nodeName)
{
  var nodeCollection = new Array();
  for (var lcv=firstpos;lcv < aNode.childNodes.length; lcv++)
    if ((aNode.childNodes[lcv].nodeType == 1) && (aNode.childNodes[lcv].nodeName == nodeName))
      nodeCollection[nodeCollection.length] = aNode.childNodes[lcv];
  return (nodeCollection);
};

/**********************************************************
                 GUI Parser Functions
***********************************************************/

