var DB_STATUS_UPTODATE = "utd";
var DB_STATUS_CLIENT_UPDATED = "cu";
var DB_STATUS_CLIENT_DELETED = "cd";
var DB_STATUS_CLIENT_NEW = "cn";

var RSL_NS_URI = 'http://www.resalys.com/XMLSchema/v1';

var g_nGUIID = 0;

function DBObject()
{
  this.getDBStatus = DBObject_getDBStatus;
  this.setDBStatus = DBObject_setDBStatus;
  this.setParent = DBObject_setParent;
  this.getParent = DBObject_getParent;
  this.createRootElement = DBObject_createRootElement;
  this.readRootElement = DBObject_readRootElement;
  this.clone = DBObject_clone;
  this.addControler = DBObject_addControler;
  this.removeControler = DBObject_removeControler;
  this.updateControlersView = DBObject_updateControlersView;
  this.getGUIID = DBObject_getGUIID;
  this.setGUIID = DBObject_setGUIID;
  this.cleanGUIObjects = DBObject_cleanGUIObjects;
  this.isChecked = DBObject_isChecked;
  this.toXMLString = DBObject_toXMLString;
  this.fromXMLString = DBObject_fromXMLString;
  this.refreshControlers = DBObject_refreshControlers;
  // members
  this.nGUIID = 0;
	this.aControlers = null;
}

function DBObject_addControler(pControler)
{
  if(!this.aControlers)
    this.aControlers = new Array();
  this.aControlers[this.aControlers.length] = pControler;
}

function DBObject_removeControler(pControler)
{
  if(!this.aControlers)
    this.aControlers = new Array();
  removeArrayElement(this.aControlers, pControler);
}

function DBObject_updateControlersView()
{
  if(this.aControlers)
  {
    for(var i=0;i<this.aControlers.length;i++)
      this.aControlers[i].updateView();
  }
}

function DBObject_refreshControlers()
{
  if(this.aControlers)
  {
    for(var i=0;i<this.aControlers.length;i++)
      this.aControlers[i].doRefresh();
  }
}

function DBObject_clone()
{
  var pClone = new this.constructor;
  for (var sProperty in this)
  {
    pClone[sProperty] = this[sProperty];
  }
  pClone.nGUIID = null;
  return pClone;
}

function DBObject_isChecked()
{
  try
  {
    this.check();
    return true;
  }
  catch(ex)
  {
    if(!(ex instanceof CheckerException))
      throw ex;
    else
      return false;
  }
}

function DBObject_toXMLString()
{
  var aArguments = DBObject_toXMLString.arguments;
  var xmldDoc = XmlUtils_createDocument();
  var aEvalArgs = new Array("xmldDoc");
  for(var i=0;i<aArguments.length;i++)
  {
	 aEvalArgs.push("aArguments["+i+"]");
  }
  eval("xmldDoc.appendChild(this.toXMLElement("+aEvalArgs.join(",")+"))");
  return XmlUtils_getXMLString(xmldDoc);
}

function DBObject_fromXMLString(sXML)
{
  var xmldDoc = XmlUtils_getXMLDOM(sXML);
  return this.fromXMLElement(xmldDoc.documentElement);
}

function DBObject_getDBStatus()
{
  if(this.nDBStatus == undefined)
    this.nDBStatus = DB_STATUS_CLIENT_NEW;
  return this.nDBStatus;
}

function DBObject_setDBStatus( nDBStatus, bForceStatus )
{
  var pParent = this.getParent();
  var nParentStatus = null;
  switch(nDBStatus)
  {
    case DB_STATUS_CLIENT_NEW:
    case DB_STATUS_CLIENT_UPDATED:
    case DB_STATUS_CLIENT_DELETED:
      nParentStatus = DB_STATUS_CLIENT_UPDATED;
      break;
  }
  if(pParent && nParentStatus != null && pParent.getDBStatus() != nParentStatus)
    pParent.setDBStatus(nParentStatus);
  // if want to set new an existing item, throw an error
  if(nDBStatus == DB_STATUS_CLIENT_NEW && nDBStatus != DB_STATUS_CLIENT_NEW)
    throw new Error('Cannot set existing object status to new client object');
  // if change state to updated a new client object, this object stays a new client object
  if(nDBStatus == DB_STATUS_CLIENT_UPDATED && this.getDBStatus() == DB_STATUS_CLIENT_NEW)
    nDBStatus = DB_STATUS_CLIENT_NEW;
  // if change state to updated a deleted client object, this object stays a deleted object
  if(!bForceStatus && nDBStatus == DB_STATUS_CLIENT_UPDATED && this.getDBStatus() == DB_STATUS_CLIENT_DELETED)
    nDBStatus = DB_STATUS_CLIENT_DELETED;
  this.nDBStatus = nDBStatus;
}

function DBObject_createRootElement(xmldDoc, sTagName)
{
  var xmlutils = new XmlUtils();
  xmleRoot = xmldDoc.createElement(sTagName);
  xmleRoot.setAttribute("db_status", this.getDBStatus());  
  return xmleRoot;
}

function DBObject_readRootElement(xmleElement)
{
  var xmlutils = new XmlUtils();
  var nStatus = xmleElement.getAttribute("db_status");
  if(nStatus != null)
    this.nDBStatus = nStatus;
}

aGUIObjects = new Array();

function DBObject_getGUIID()
{
  if( !this.nGUIID )
  {
    g_nGUIID++;
    this.nGUIID = g_nGUIID;
  }
  if( !aGUIObjects[this.nGUIID] )
  {
    aGUIObjects[this.nGUIID] = this;
  }
  return this.nGUIID;
}

function DBObject_setGUIID(nGUIID)
{
  this.nGUIID = nGUIID;
  aGUIObjects[this.nGUIID] = this;
}

function getObjectFromGUIID(sGUIID)
{
  return aGUIObjects[sGUIID];
}

function DBObject_cleanGUIObjects()
{
  aGUIObjects = new Array();
} 

function DBObject_getParent()
{
  return this.pParent;
}

function DBObject_setParent(pParent)
{
  this.pParent = pParent;
}

function Controler()
{
  this.aRefreshes = new Array();
  this.appendRefresh = Controler_appendRefresh;
  this.doRefresh = Controler_doRefresh;
}

function Controler_appendRefresh()
{
  // create new refresh message
  var aArgs = Controler_appendRefresh.arguments;
  var pMethod = aArgs[0];
  var aArguments = new Array();
  for(var i=1;i<aArgs.length;i++)
	 aArguments.push(aArgs[i]);
  var pCR = new ControlerRefresh(pMethod, aArguments);
  // look if already exists
  for(i=0;i<this.aRefreshes.length;i++)
	 if(this.aRefreshes[i].equals(pCR))
		return false;
  // add it
  this.aRefreshes.push(pCR);
  return true;
}

function Controler_doRefresh()
{
  for(var i=0;i<this.aRefreshes.length;i++)
  {
	 var pCR = this.aRefreshes[i];
	 pCR.pMethod.call(this, pCR.aArguments[0],
								     pCR.aArguments[1],
									  pCR.aArguments[2],
									  pCR.aArguments[3],
									  pCR.aArguments[4],
									  pCR.aArguments[5]);
  }
  this.aRefreshes = new Array();
}

function ControlerRefresh(pMethod, aArguments)
{
  this.pMethod = pMethod;
  this.aArguments = aArguments;
  
  this.equals = ControlerRefresh_equals;
  this.toString = ControlerRefresh_toString;

  this.sInnerString = this.toString( true );
}

function ControlerRefresh_equals(pCR)
{
  return this.toString() == pCR.toString();
}

function ControlerRefresh_toString( bReal )
{
  if( bReal )
  {
    var s = this.pMethod.toString().length;
    for(var i=0;i<this.aArguments.length;i++)
    {
	   if(this.aArguments[i].getGUIID)
		  s+='-'+this.aArguments[i].getGUIID();
	   else
		  s+='-'+this.aArguments[i];
    }
    
    return s;
  }
  else
  {
    return this.sInnerString;
  }
}

function ControlerRefresh_sort(a,b)
{
  if(a>b)
	 return 1;
  else if(a<b)
	 return -1;
  return 0;
}

function NodeList(aElements)
{
  if(!aElements)
    aElements = new Array();
  this.aElements = aElements;
  this.length = aElements.length;
  this.item = NodeList_item;
}

function NodeList_item(i)
{
  return this.aElements[i];
}

// XmlUtils factory
function XmlUtils()
{
  this.createDocument = XmlUtils_createDocument;
  this.getXMLString = XmlUtils_getXMLString;
  this.getXMLDOM = XmlUtils_getXMLDOM;
  this.getElementByTagName = XmlUtils_getElementByTagName;
  this.getElementText = XmlUtils_getElementText;
  this.getElementTextByTagName = XmlUtils_getElementTextByTagName;
  this.getElementDateByTagName = XmlUtils_getElementDateByTagName;
  this.getElementDateTimeByTagName = XmlUtils_getElementDateTimeByTagName;
  this.getElementBooleanByTagName = XmlUtils_getElementBooleanByTagName;
  this.createTextElement = XmlUtils_createTextElement;
  this.createDateElement = XmlUtils_createDateElement;
  this.createDateTimeElement = XmlUtils_createDateTimeElement;
  this.createBooleanElement = XmlUtils_createBooleanElement;
  this.createObjectCollection = XmlUtils_createObjectCollection;
  this.findChildrenFromCollTagName = XmlUtils_findChildrenFromCollTagName;
  this.getChildElements = XmlUtils_getChildElements;
  this.getChildElement = XmlUtils_getChildElement;
  this.getAttributeValue = XmlUtils_getAttributeValue;
}

function XmlUtils_createDocument()
{
  try
  {
    var xmldDoc;
    // DOM2
    if (document.implementation && document.implementation.createDocument)
    {
      xmldDoc = document.implementation.createDocument("", "", null);

      // some versions of Moz do not support the readyState property
      // and the onreadystate event so we patch it!
      if (xmldDoc.readyState == null)
      {
        xmldDoc.readyState = 1;
        xmldDoc.addEventListener("load", function () {
          xmldDoc.readyState = 4;
          if (typeof doc.onreadystatechange == "function")
          xmldDoc.onreadystatechange();
        }, false);
      }

      return xmldDoc;
    }
    if (window.ActiveXObject)
      xmldDoc = new ActiveXObject(getControlPrefix() + ".XmlDom");
    //xmldDoc.appendChild(xmldDoc.createProcessingInstruction('xml', "version='1.0' encoding='iso-8859-1'"));
    return xmldDoc;
  }
  catch (ex)
  {
    throw ex;
  }
  throw new Error("Your browser does not support XML");
};

function getControlPrefix() {
   if (getControlPrefix.prefix)
      return getControlPrefix.prefix;
   
   var prefixes = ["MSXML2", "Microsoft", "MSXML", "MSXML3"];
   var o, o2;
   for (var i = 0; i < prefixes.length; i++) {
      try {
         // try to create the objects
         o = new ActiveXObject(prefixes[i] + ".XmlHttp");
         o2 = new ActiveXObject(prefixes[i] + ".XmlDom");
         getControlPrefix.prefix = prefixes[i];
         return getControlPrefix.prefix;
      }
      catch (ex) {};
   }
   
   throw new Error("Could not find an installed XML parser");
}

function XmlUtils_getXMLString(xmldDoc)
{
  xmldDoc.documentElement.setAttribute("xmlns", RSL_NS_URI);
  var sXML;
  // DOM 2
  if (document.implementation && document.implementation.createDocument)
  {
    var serializer = new XMLSerializer();
    sXML = serializer.serializeToString(xmldDoc)
  }
  // MS
  else
  {
    sXML = xmldDoc.xml;
  }
  if (!(/^<\?xml version=/.test(sXML))){ //Opera ajoute déjà <?xml version="1.0">
	sXML = '<?xml version="1.0" encoding="UTF-8"?>' + sXML;
  }
  return sXML;
}

function XmlUtils_getXMLDOM(s)
{
      
  // DOM 2
  if (document.implementation && document.implementation.createDocument)
  {
    // parse the string to a new doc   
    var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
    return doc2;
  }
  else
  {
    var doc = XmlUtils_createDocument();
    doc.loadXML(s);
    return doc;
  }
};

function XmlUtils_createTextElement(xmldDoc, sTagName, sText, sDefault)
{
  xmle = xmldDoc.createElement(sTagName);
  if(sText == null)
  {
    if(sDefault != undefined)
      sText = sDefault;
    else
      sText = "";
  }
  xmle.appendChild(xmldDoc.createTextNode(sText));
  return xmle;
}

function XmlUtils_createDateElement(xmldDoc, sTagName, dDate, sDefault)
{
  var sDate = formatFixedDate(dDate);
  return XmlUtils_createTextElement(xmldDoc, sTagName, sDate, sDefault);
}

function XmlUtils_createDateTimeElement(xmldDoc, sTagName, dDate, sDefault)
{
  var sDate = formatFixedDateTime(dDate);
  return XmlUtils_createTextElement(xmldDoc, sTagName, sDate, sDefault);
}

function XmlUtils_createBooleanElement(xmldDoc, sTagName, bBoolean, sDefault)
{
  var sValue = bBoolean ? '1' : '0';
  return XmlUtils_createTextElement(xmldDoc, sTagName, sValue, sDefault);
}

function XmlUtils_getElementByTagName(xmlElement, sTagName)
{
  return xmlElement.selectSingleNode( sTagName );
}

function XmlUtils_getElementTextByTagName(xmleElement, sTagName, sDefault)
{
  return XmlUtils_getElementText( xmleElement.selectSingleNode( sTagName ), sDefault );
}

function XmlUtils_getElementText(xmleElement, sDefault)
{
  if( xmleElement != null && xmleElement.firstChild )
  {
    return xmleElement.firstChild.nodeValue;
  }
  if(sDefault != undefined)
    return sDefault;
  return null;
}

function XmlUtils_getElementBooleanValue(xmleElement, bDefault)
{
  var sValue = XmlUtils_getElementText(xmleElement, null);
  if(sValue == null)
    return bDefault;
  if(sValue == "1" || sValue == "TRUE" || sValue == "true")
    return true;
  return false;
}

function XmlUtils_getElementDateByTagName(xmlElement, sTagName, sDefault)
{
  var sDate = XmlUtils_getElementTextByTagName(xmlElement, sTagName, sDefault);
  return parseFixedDate(sDate);
}

function XmlUtils_getElementDateTimeByTagName(xmlElement, sTagName, sDefault)
{
  var sDate = XmlUtils_getElementTextByTagName(xmlElement, sTagName, sDefault);
  return parseFixedDateTime(sDate);
}

function XmlUtils_getElementBooleanByTagName(xmlElement, sTagName, sDefault)
{
  var sValue = XmlUtils_getElementTextByTagName(xmlElement, sTagName, sDefault);
  if(sValue == '1' || sValue == 'true')
    return true;
  return false
}


/**
 * Find a list of child node for a specified collection tag name, through a specified parent element.
 *
 * @param xmleParent Parent of the collection tag.
 * @param sColltTagName Collection tag name
 * @param sChildTagName The child tag name.
 *
 * @return A NodeList of tags.
 */
function XmlUtils_findChildrenFromCollTagName(xmleParent, sCollTagName, sChildTagName)
{
  nl = new NodeList();
  //xmleCollection = XmlUtils_getElementByTagName(xmleParent,sCollTagName);
  xmleCollection = XmlUtils_getChildElement(xmleParent,sCollTagName);
  if(xmleCollection != null)
    return new NodeList(XmlUtils_getChildElements(xmleCollection,sChildTagName));
  return nl;
}

function XmlUtils_createObjectCollection(xmldDoc, aObjects, sCollTagName, aArgs )
{
  var xmle = xmldDoc.createElement(sCollTagName);
  if(aObjects == null)
    return xmle;
  var sArgs = '';
  if( aArgs != null )
  {
    for( var i = 0 ; i < aArgs.length ; i++ )
    {
      sArgs += ', aArgs[ ' + i + ']';
    }
  }
  for(var i=0;i<aObjects.length;i++)
  {
    xmle.appendChild(eval( 'aObjects[i].toXMLElement(xmldDoc ' + sArgs + ')'));
  }
  return xmle;
}

function XmlUtils_getChildElements(xmleElement, sTagNameFilter)
{
  var aChildren = new Array();
  var nl = xmleElement.childNodes;
  for(var i=0;nl && i<nl.length;i++)
  {
    var nChild = nl.item(i);
    // if a child correspond
    if(nChild.nodeType == 1  && (sTagNameFilter == null || nChild.nodeName == sTagNameFilter) )
      aChildren[aChildren.length] = nChild;
  }
  return aChildren;
}

function XmlUtils_getChildElement(xmleElement, sTagNameFilter)
{
  var nl = xmleElement.childNodes;
  if(nl)
  {
    for(var i=0;i<nl.length;i++)
    {
      var nChild = nl.item(i);
      // if a child correspond
      if(nChild.nodeType == 1  && nChild.nodeName == sTagNameFilter )
        return nChild;
    }
  }
  return null;
}

function XmlUtils_getBooleanAttributeValue(xmleElement, sAttrName, bDefault)
{
  var sValue = XmlUtils_getAttributeValue(xmleElement, sAttrName);
  if(sValue == null)
    return bDefault;
  return sValue == '1' || new String(sValue).toUpperCase() == 'TRUE' ? true : false;
}

function XmlUtils_getAttributeValue(xmleElement, sAttrName, sDefault)
{
  var nAttr = xmleElement.getAttributeNode(sAttrName);
  if(nAttr)
    return nAttr.nodeValue;
  if(sDefault != undefined)
    return sDefault;
  return null;
}

function XmlChecker(sXML)
{
  this.xmlutils = new XmlUtils();
  this.doc = this.xmlutils.getXMLDOM(sXML);
  this.checkRoot = XmlChecker_checkRoot;
  this.checkElement = XmlChecker_checkElement;
  this.checkSingleElement = XmlChecker_checkSingleElement;
  this.isElementCheckable = XmlChecker_isElementCheckable;
}

function XmlChecker_checkRoot(xmleRoot)
{
  // find corresponding element in model
  var nl = this.doc.getElementsByTagName(xmleRoot.nodeName);
  // for each corresponding model elements, check recursively
  for(var i=0;i<nl.length;i++)
    this.checkElement(xmleRoot, nl.item(i));
}


function XmlChecker_checkElement(xmleElement, xmleModelNode)
{
  this.checkSingleElement(xmleElement,xmleModelNode);
  // for model elements
  var aModelChilds = this.xmlutils.getChildElements(xmleModelNode);
  for(var i=0;i<aModelChilds.length;i++)
  {
    // get doc corresponding nodes
    var aDocChilds = new Array();
    var aDocAllChilds = this.xmlutils.getChildElements(xmleElement,aModelChilds[i].nodeName);
    for(var j=0;j<aDocAllChilds.length;j++)
    {
      if(this.isElementCheckable(aDocAllChilds[j],aModelChilds[i]))
        aDocChilds[aDocChilds.length] = aDocAllChilds[j];
    }
    // if mandatory and there is no elements, throw message
    if(!aDocChilds.length && this.xmlutils.getAttributeValue(aModelChilds[i],"check") == "mandatory")
      throw new CheckerException(this.xmlutils.getAttributeValue(aModelChilds[i],"message_id"));
    // check all found children
    for(var j=0;j<aDocChilds.length;j++)
      this.checkElement(aDocChilds[j],aModelChilds[i]);
  }
}

function XmlChecker_isElementCheckable(xmleElement, xmleModel)
{
  // check if a child value test exists
  var nCondition = this.xmlutils.getChildElement(xmleModel,"tag_value_cond");
  if(nCondition)
  {
    var sCondTag = this.xmlutils.getAttributeValue(nCondition,"tag_name");
    var sCondValue = this.xmlutils.getAttributeValue(nCondition,"text_value");
    // if valid
    if(sCondTag && sCondValue)
    {
      // look for child
      var n = this.xmlutils.getChildElement(xmleElement, sCondTag);
      if(n && this.xmlutils.getElementText(n) == sCondValue)
        return true;
      else
        return false;
    }
  }
  return true;
}

function XmlChecker_checkSingleElement(xmleElement, xmleModel)
{
  // get check value
  var sCheckTextValue = this.xmlutils.getAttributeValue(xmleModel,"check_text");
  switch(sCheckTextValue)
  {
    case "not_empty":
      var sValue = this.xmlutils.getElementText(xmleElement);
      if(!sValue || sValue.length < 1)
        throw new CheckerException(this.xmlutils.getAttributeValue(xmleModel,"message_id"));
  }
}


// réécrit la fonction selectNodes pour Firefox

// mozXPath [http://km0ti0n.blunted.co.uk/mozxpath/] km0ti0n@gmail.com
// Code licensed under Creative Commons Attribution-ShareAlike License 
// http://creativecommons.org/licenses/by-sa/2.5/
if( getBrowser() == NETSCAPE &&
    document.implementation.hasFeature("XPath", "3.0") )
{
  if( typeof XMLDocument == "undefined" ){ XMLDocument = Document; }
  XMLDocument.prototype.selectNodes = function(cXPathString, xNode){
    if( !xNode ) { xNode = this; }
    
    var oNSResolver = this.createNSResolver(this.documentElement)
    var aItems = this.evaluate(cXPathString, xNode, oNSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
    var aResult = [];
    for( var i = 0; i < aItems.snapshotLength; i++){aResult[i] =  aItems.snapshotItem(i); }
    return aResult;
  }
  XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode){
    if( !xNode ) { xNode = this; } 
    var xItems = this.selectNodes(cXPathString, xNode);
    if( xItems.length > 0 ){return xItems[0]; }
    else{return null; }
  }
  Element.prototype.selectNodes = function(cXPathString){
    if(this.ownerDocument.selectNodes){ return this.ownerDocument.selectNodes(cXPathString, this);}
    else{throw "For XML Elements Only";}
  }
  Element.prototype.selectSingleNode = function(cXPathString){  
    if(this.ownerDocument.selectSingleNode){return this.ownerDocument.selectSingleNode(cXPathString, this); }
    else{throw "For XML Elements Only";}
  }
}

/**
 * Allow to call class methods on this. Purely esthetic.
 */
XmlUtilsSingleton = {
  createDocument : XmlUtils_createDocument,
  getXMLString : XmlUtils_getXMLString,
  getXMLDOM : XmlUtils_getXMLDOM,
  getElementByTagName : XmlUtils_getElementByTagName,
  getElementText : XmlUtils_getElementText,
  getElementTextByTagName : XmlUtils_getElementTextByTagName,
  getElementDateByTagName : XmlUtils_getElementDateByTagName,
  getElementDateTimeByTagName : XmlUtils_getElementDateTimeByTagName,
  getElementBooleanByTagName : XmlUtils_getElementBooleanByTagName,
  createTextElement : XmlUtils_createTextElement,
  createDateElement : XmlUtils_createDateElement,
  createDateTimeElement : XmlUtils_createDateTimeElement,
  createBooleanElement : XmlUtils_createBooleanElement,
  createObjectCollection : XmlUtils_createObjectCollection,
  findChildrenFromCollTagName : XmlUtils_findChildrenFromCollTagName,
  getChildElements : XmlUtils_getChildElements,
  getChildElement : XmlUtils_getChildElement,
  getAttributeValue : XmlUtils_getAttributeValue
}


