// --------------- AJAX calls to the web service routines -----------------//
function getXMLHttpRequest( url ) {
	var xmlHttp = null;
	// get the XMLHttpRequest object for the current browser
	if ( window.XMLHttpRequest ) {
			xmlHttp = new XMLHttpRequest();
	}
	else if ( window.ActiveXObject ) {
			xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
	}
	// set the parameters and make the call to the server
	xmlHttp.open("post", url, false);
	xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");			
	return xmlHttp;
}

function runSearch( params ) {
	var xmlHttp = getXMLHttpRequest(getGlobalValue('SITE_CONTEXT') + '/ajaxbridge/runsearch.jsp');
	if ( xmlHttp == null ) {
		alert("ERROR: Unable to create request object");
		showStatus(false);
		return;
	}
	
	xmlHttp.send(params);
	if ( xmlHttp.status == 200 ) {
		// get response text (which should be xml in this case)			
		return xmlHttp.responseText;
	}
	else if ( xmlHttp.status == 404 ){
		alert('ERROR: Error 404: Cannot find page ' + xmlHttp.statusText);
	}
	else {
		alert('ERROR: Request failed with status ' + xmlHttp.status + ' : ' + xmlHttp.statusText);
	}	
}

function getSearchCount(xmlDoc) {
	var elems = xmlDoc.getElementsByTagName("SearchResults");

	if ( elems.length != 1 )
		return 0;
		
	var cnt = elems[0].getAttribute("count");
	if ( isNaN(cnt) )
		return 0;
		
	return Number(cnt);
}
	
function getSearchResult(xmlDoc, index) {
	// get all property tags
	if ( isNaN(index) )
		return '';
		
	var elem = null, attr = null;
	var elems = xmlDoc.getElementsByTagName("Result");

	if ( elems.length == 0 )
		return null;

	// parse the property elements to find the one we're looking for
	for ( var e = 0; e < elems.length; e++ ) {
		// get the next element
		elem = elems[e];
		attr = elem.getAttribute("index");
		if ( isNaN(attr) )
			continue;
			
		if ( Number(attr) == Number(index) ) {
			return getNodeData(elem);
		}
		elem = null;
	}

	return elem;
}

function getEntityAsXML(entityID) {
	var xmlHttp = getXMLHttpRequest(getGlobalValue('SITE_CONTEXT') + '/ajaxbridge/getentity.jsp');
	if ( xmlHttp == null ) {
		alert("ERROR: Unable to create request object");
		showStatus(false);
		return;
	}
	
	var params = 'entityid=' + entityID;
	xmlHttp.send(params);
	if ( xmlHttp.status == 200 ) {
		// get response text (which should be xml in this case)			
		return xmlHttp.responseText;
	}
	else if ( xmlHttp.status == 404 ){
		alert('ERROR: Error 404: Cannot find page ' + xmlHttp.statusText);
	}
	else {
		alert('ERROR: Request failed with status ' + xmlHttp.status + ' : ' + xmlHttp.statusText);
	}
}

function putEntity(xml) {
	var xmlHttp = getXMLHttpRequest(getGlobalValue('SITE_CONTEXT') + '/ajaxbridge/putentity.jsp');
	if ( xmlHttp == null ) {
		alert("ERROR: Unable to create request object");
		showStatus(false);
		return;
	}
	
	var params = 'entityxml=' + xml;
	xmlHttp.send(params);
	if ( xmlHttp.status == 200 ) {
		// get response text (which should be xml in this case)			
		return xmlHttp.responseText;
	}
	else if ( xmlHttp.status == 404 ){
		alert('ERROR: Error 404: Cannot find page ' + xmlHttp.statusText);
	}
	else {
		alert('ERROR: Request failed with status ' + xmlHttp.status + ' : ' + xmlHttp.statusText);
	}
}

function sendEmail(to, subj, body, from) {	
	var xmlHttp = getXMLHttpRequest(getGlobalValue('SITE_CONTEXT') + '/ajaxbridge/sendmail.jsp');
	if ( xmlHttp == null ) {
		alert("ERROR: Unable to create request object");
		showStatus(false);
		return;
	}
	
	if ( from == undefined )
		from = '';
	
	var params = 'TO=' + to;	
	params += '&SUBJ=' + subj;
	params += '&BODY=' + body;
	params += '&FROM=' + from;
	
	xmlHttp.send(params);
	if ( xmlHttp.status == 200 ) {
		// get response text	
		return xmlHttp.responseText;
	}
	else if ( xmlHttp.status == 404 ){
		alert('ERROR: Error 404: Cannot find page ' + xmlHttp.statusText);
	}
	else {
		alert('ERROR: Request failed with status ' + xmlHttp.status + ' : ' + xmlHttp.statusText);
	}	
}

function setSessionAttribute(name, value) {
	var xmlHttp = getXMLHttpRequest(getGlobalValue('SITE_CONTEXT') + '/ajaxbridge/ajaxgen.jsp');
	if ( xmlHttp == null ) {
		alert("ERROR: Unable to create request object");
		showStatus(false);
		return;
	}
	
	var params = 'sessionattr_name=' + name;
	params += '&sessionattr_value=' + value;
	
	xmlHttp.send(params);
	if ( xmlHttp.status == 200 ) {
		return;
	}
	else if ( xmlHttp.status == 404 ){
		alert('ERROR: Error 404: Cannot find page ' + xmlHttp.statusText);
	}
	else {
		alert('ERROR: Request failed with status ' + xmlHttp.status + ' : ' + xmlHttp.statusText);
	}
}

//------------------ XML routines for handling Adapt entity XML data ------------------ //

function getEntityID(xmlDoc) {
	var elems = xmlDoc.getElementsByTagName("Entity");

	if ( elems.length != 1 )
		return 0;
		
	return elems[0].getAttribute("id");
}


function getEntityValue(xmlDoc, ctrlpath) {
	var node = getTextNode(xmlDoc, ctrlpath);
	return (node.data == null)?"":node.data;	
}

/**
	Put the data of an Adapt attribute (field) into the provided xml DOM object
	@param: xmlDoc - A Microsoft.XMLDOM object (IE) or a DOMParser object (Moz, FF, Opera, etc).
	@param: attrpath - the Adapt attribute control path in the format PROPERTY(Occurrence)/ATTRIBUTE where (Occurrence) is optional
	@return: The data of the Adapt attribute
*/
function putEntityValue(xmlDoc, ctrlpath, data) {
	var tnode = getTextNode(xmlDoc, ctrlpath);
	
	if ( tnode != null ) {
		tnode.data = data;
		return true;
	}
	
	enode = createElementNode(xmlDoc, ctrlpath);
	if ( enode == null )
		return false;
	
	if ( data.indexOf('&') >= 0 ) {
		data = data.replace('&', 'and');	// <-- temporary until wsdl files are fixed
		tnode = xmlDoc.createCDATASection(data);
	}
	else
		tnode = xmlDoc.createTextNode(data);
	enode.appendChild(tnode);
	return true;
}

/**
This method builds the entity xml from the "ADAPT_PREFIX" form fields in the current page.
@param: entityid - the entity id for the main Entity tag's "id" attribute
@param: role - the role name for the main Entity tag's "defaultrole" attribute
@return: An XML document object containing the entity xml
*/
function buildEntityXML(entityid, role) {
	// create the base xml document
	var xmlDoc = getXMLDocument("<Entity id='" + entityid + "' defaultrole='" + role + "'><Role>" + role + "</Role></Entity>");

	// now, parse the document and retrieve data for all form fields with the ADAPT__ prefix
	var elem, attrname;	
	var prefix = getGlobalValue('ADAPT_PREFIX');
	var prefixLen = prefix.length;
	
	// parse all forms in the document
	for ( var f = 0; f < document.forms.length; f++ ) {
		// parse all elements within the current form
		for ( var e = 0; e < document.forms[f].elements.length; e++ ) {
			elem = document.forms[f].elements[e];
			// check for the Adapt prefix			
			if ( elem.name.substring(0, prefixLen) == prefix ) {
				// get the attribute path within the xml
				attrname = elem.name.substring(prefixLen);					
				putEntityValue(xmlDoc, attrname, elem.value);
			}
		}
	}
	
	return getXMLAsString(xmlDoc);
}

/**
Validates the attribute control path (form field names/ids) and splits it into a 3-element array containing the names of
the property, occurrence (could be null) and attribute.
@param: ctrlpath - the control path of the attribute in the format PROPERTY(Occurrence)/ATTRIBUTE
@return: A 3-element array in the format (property, occ, attribute)
*/
function splitCtrlPath(ctrlpath) {
	var attrsplit = ctrlpath.split('$');	
	// check the validity of the path - should be in 2 parts: property/attribute - note that the property may have an occurrence, which
	// we'll deal with later
	if ( attrsplit.length != 3 ) {
		alert('ERROR: Adapt attribute path is incorrectly formatted: ' + ctrlpath + '\nPlease use this format: PROPERTY$Occurrence$ATTRIBUTE');
		return null;
	}
	
	return attrsplit;
}

// -------- Basic XML routines ---------- //
/**
	Create a browser-independent xml dom object from the given xml string
	@param: xmlstring - contains the xml as a string
	@return: an xml dom object
 */
function getXMLDocument(xml) {
	var xmlDoc = null;
	
	// code for IE
	if ( window.ActiveXObject )	{
		xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
		xmlDoc.async = false;
		xmlDoc.loadXML(xml);
	}
	// code for Mozilla, Firefox, Opera, etc.
	else if ( document.implementation && document.implementation.createDocument ) {
		var parser = new DOMParser();
		xmlDoc = parser.parseFromString(xml,"text/xml");
	}
	else {
		alert('ERROR: Your browser does not support xml documents');
	}
	
	return xmlDoc;
}

/**
Browser-independent method to return the xml text of the given xml dom object
@param: xmlDoc - an xml dom object
@return: the xml as a string
*/
function getXMLAsString(xmlDoc) {
	// code for IE
	if ( window.ActiveXObject )	{
		return xmlDoc.xml
	}
	// code for Mozilla, Firefox, Opera, etc.
	else if ( document.implementation && document.implementation.createDocument ) {
		return (new XMLSerializer()).serializeToString(xmlDoc);
	}
	else {
		alert('ERROR: Your browser does not support xml documents');
	}
}

/**
Creates an xml dom node for the given attribute path.  If the Adapt properry node (parent node) doesn not exist, it
is created as well.
@param: xmlDoc - the xml dom object to parse
@param: ctrlpath - the control path of the Adapt attribute
@return: the newly created node
*/
function createElementNode(xmlDoc, ctrlpath) {
	var attrsplit = splitCtrlPath(ctrlpath);
	
	// assign the parts
	var prop = attrsplit[0],
	occ = attrsplit[1],
	attr = attrsplit[2];
	
	var pnode, elem, elems, ename;
	
	pnode = getPropertyNode(xmlDoc, prop, occ);
	
	// return null if the property wasn't found
	if ( pnode == null ) {
		var elems = xmlDoc.getElementsByTagName("Entity");
		var elem = elems[0];
		pnode = xmlDoc.createElement('Property');
		if ( pnode == null )
			return null;
		
		pnode.setAttribute('name', prop);
		if ( occ != null ) {
			if ( isNaN(occ) )
				pnode.setAttribute('occurrence', occ);
		}
			
		elem.appendChild(pnode);
	}
	
	var attrnode = xmlDoc.createElement('Attribute');
	if ( attrnode == null )
		return null;
	
	attrnode.setAttribute('name', attr);
	
	pnode.appendChild(attrnode);
	return attrnode;
}

function getNodeData(elem) {
	var enodes = elem.childNodes;
	for ( var n = 0; n < enodes.length; n++ ) {
		var enode = enodes[n];
		if ( enode.nodeType == 3 ) {
			// this is the text node
			return (enode.data == null)?"":enode.data;
		}
	}
}

/**
Retrieves the xml node for the given Adapt property
@param: xmlDoc - the xml dom object to parse
@param: prop - the Adapt property name
@param: occ - the Adapt occurrence name (may be null)
@return: the corresponding xml dom node object
*/
function getPropertyNode(xmlDoc, prop, occ) {
	// get all property tags
	var elem = null;
	var elems = xmlDoc.getElementsByTagName("Property");
	var ename;

	if ( elems.length == 0 )
		return null;
	
	// parse the property elements to find the one we're looking for
	for ( var e = 0; e < elems.length; e++ ) {
		// get the next element
		elem = elems[e];
		ename = elem.getAttribute("name");
		if ( ename == prop ) {
			// this may be the one, but check the occurrence first, if there is one
			if ( (occ != null) && (occ != '') ) {
				// there is an occ, so see if it matches
				ename = elem.getAttribute("occurrence");
				if ( ename == occ )
					break;
			}
			else
				break;	// there was no occurrence, so it's a match
		}
		elem = null;
	}
	
	return elem;
}

/**
Retrieves the xml node for the give Adapt attribute
@param: propNode - this is an xml dom node object that represent the property, or parent, of the sought attribute node
@param: attr - the name of the Adapt attribute to find
@return: an xml dom node representing the Adapt attribute
*/
function getAttributeNode(propNode, attr) {
	if ( propNode == null )
		return null;
	
	var elem, elems;
	
	// get all of this property tag's child nodes, which are the Adapt attributes (fields)
	elems = propNode.childNodes;
	
	for ( var e = 0; e < elems.length; e++ ) {
		elem = elems[e];
		// make sure this is an element type node
		if ( elem.nodeType == 1 ) {
			// get the name, which is an xml attribute of the <Atrribute> tag
			ename = elem.getAttribute("name");
			if ( ename == attr ) {
				return elem;
			}
		}
	}
	
	return null;
}

/**
Get the node (text type) of an Adapt attribute (field) within the provided xml DOM object
@param: xmlDoc - A Microsoft.XMLDOM object (IE) or a DOMParser object (Moz, FF, Opera, etc).
@param: ctrlpath - the Adapt attribute control path in the format PROPERTY(Occurrence)/ATTRIBUTE where (Occurrence) is optional
@return: The xml node of the Adapt attribute
*/
function getTextNode(xmlDoc, ctrlpath) {
	var attrsplit = splitCtrlPath(ctrlpath);
	
	// assign the parts
	var prop = attrsplit[0],
	    occ = attrsplit[1],
	    attr = attrsplit[2];
	
	var pnode, elem, elems, ename;
	
	pnode = getPropertyNode(xmlDoc, prop, occ);
	
	// return null if the property wasn't found
	if ( pnode == null )
		return null;
	
	elem = getAttributeNode(pnode, attr);
	
	if ( elem != null ) {
		var enodes = elem.childNodes;
		for ( var n = 0; n < enodes.length; n++ ) {
			var enode = enodes[n];
			if ( enode.nodeType == 3 ) {
				// this is the text node
				return enode;
			}
		}
	}
	
	return null;
}
