/** 
* @description Intel(r) AMT WSMAN Stack
* @author Ylian Saint-Hilaire
* @version v0.2.0
*/
// Construct a MeshServer object
var WsmanStackCreateService = function (host, port, user, pass, tls, extra) {
    var obj = {};
    //obj.onDebugMessage = null;          // Set to a function if you want to get debug messages.
    obj.NextMessageId = 1;              // Next message number, used to label WSMAN calls.
    obj.Address = '/wsman';
    obj.comm = CreateWsmanComm(host, port, user, pass, tls, extra);
    obj.PerformAjax = function (postdata, callback, tag, pri, namespaces) {
        if (namespaces == undefined) namespaces = '';
        obj.comm.PerformAjax('' + postdata, function (data, status, tag) {
            if (status != 200) { callback(obj, null, { Header: { HttpError: status } }, status, tag); return; }
            var wsresponse = obj.ParseWsman(data);
            if (!wsresponse || wsresponse == null) { callback(obj, null, { Header: { HttpError: status } }, 601, tag); } else { callback(obj, wsresponse.Header["ResourceURI"], wsresponse, 200, tag); }
        }, tag, pri);
    }
    // Private method
    //obj.Debug = function (msg) { /*console.log(msg);*/ }
    // Cancel all pending queries with given status
    obj.CancelAllQueries = function (s) { obj.comm.CancelAllQueries(s); }
    // Get the last element of a URI string
    obj.GetNameFromUrl = function (resuri) {
        var x = resuri.lastIndexOf("/");
        return (x == -1)?resuri:resuri.substring(x + 1);
    }
    // Perform a WSMAN Subscribe operation
    obj.ExecSubscribe = function (resuri, delivery, url, callback, tag, pri, selectors, opaque, user, pass) {
        var digest = "", digest2 = "";
        if (user != undefined && pass != undefined) { digest = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken' + user + '' + pass + ''; digest2 = ''; }
        if (opaque != undefined && opaque != null) { opaque = '' + opaque + ''; } else { opaque = ""; }
        var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" + _PutObjToSelectorsXml(selectors) + digest + '' + url + '' + digest2 + 'PT0.000000S';
        obj.PerformAjax(data + "", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:se="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:m="http://x.com"');
    }
    // Perform a WSMAN UnSubscribe operation
    obj.ExecUnSubscribe = function (resuri, callback, tag, pri, selectors) {
        var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" + _PutObjToSelectorsXml(selectors) + '
';
        obj.PerformAjax(data + "", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"');
    }
    // Perform a WSMAN PUT operation
    obj.ExecPut = function (resuri, putobj, callback, tag, pri, selectors) {
        var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Put" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S" + _PutObjToSelectorsXml(selectors) + '' + _PutObjToBodyXml(resuri, putobj);
        obj.PerformAjax(data + "", callback, tag, pri);
    }
		
    // Perform a WSMAN CREATE operation
    obj.ExecCreate = function (resuri, putobj, callback, tag, pri, selectors) {
        var objname = obj.GetNameFromUrl(resuri);
        var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Create" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S" + _PutObjToSelectorsXml(selectors) + "";
        for (var n in putobj) { data += "" + putobj[n] + "" } 
        obj.PerformAjax(data + "", callback, tag, pri);
    }
    // Perform a WSMAN CREATE operation
    obj.ExecCreateXml = function (resuri, argsxml, callback, tag, pri) {
        var objname = obj.GetNameFromUrl(resuri), selector = "";
        obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Create" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S" + argsxml + "", callback, tag, pri);
    }
	
    // Perform a WSMAN DELETE operation
    obj.ExecDelete = function (resuri, putobj, callback, tag, pri) {
        var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S" + _PutObjToSelectorsXml(putobj) + "" + argsxml + "", callback, tag, pri);
    }
    // Perform a WSMAN ENUM operation
    obj.ExecEnum = function (resuri, callback, tag, pri) {
        obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S", callback, tag, pri);
    }
    // Perform a WSMAN PULL operation
    obj.ExecPull = function (resuri, enumctx, callback, tag, pri) {
        obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull" + obj.Address + "" + resuri + "" + (obj.NextMessageId++) + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S" + enumctx + "99999999", callback, tag, pri);
    }
    // Private method
    obj.ParseWsman = function (xml) {
        try {
            if (!xml.childNodes) xml = _turnToXml(xml);
            var r = { Header:{} }, header = xml.getElementsByTagName("Header")[0], t;
            if (!header) header = xml.getElementsByTagName("a:Header")[0];
            if (!header) return null;
            for (var i = 0; i < header.childNodes.length; i++) {
                var child = header.childNodes[i];
                r.Header[child.localName] = child.textContent;
            }
            var body = xml.getElementsByTagName("Body")[0];
            if (!body) body = xml.getElementsByTagName("a:Body")[0];
            if (!body) return null;
            if (body.childNodes.length > 0) {
                t = body.childNodes[0].localName;
                if (t.indexOf("_OUTPUT") == t.length - 7) { t = t.substring(0, t.length - 7); }
                r.Header['Method'] = t;
                r.Body = _ParseWsmanRec(body.childNodes[0]);
		    }
            return r;
        } catch (e) {
            console.log("Unable to parse XML: " + xml);
            return null;
        }
    }
    // Private method
    function _ParseWsmanRec(node) {
        var data, r = {};
        for (var i = 0; i < node.childNodes.length; i++) {
            var child = node.childNodes[i];
            if (child.childElementCount == 0) { data = child.textContent; } else { data = _ParseWsmanRec(child); }
            if (data == 'true') data = true; // Convert 'true' into true
            if (data == 'false') data = false; // Convert 'false' into false
            var childObj = data;
			if (child.attributes.length > 0) {
				childObj = {'Value': data };
				for(var j = 0; j < child.attributes.length; j++) {
					childObj['@' + child.attributes[j].name] = child.attributes[j].value;
				}
			}
			
            if (r[child.localName] instanceof Array) { r[child.localName].push(childObj); }
            else if (r[child.localName] == undefined) { r[child.localName] = childObj; }
            else { r[child.localName] = [r[child.localName], childObj]; }
        }
        return r;
    }
    function _PutObjToBodyXml(resuri, putObj) {
		if(!resuri || putObj === undefined || putObj === null) return '';
		var objname = obj.GetNameFromUrl(resuri);
		var result = '';
		for (var prop in putObj) {
			if (!putObj.hasOwnProperty(prop) || prop.indexOf('__') === 0 || prop.indexOf('@') === 0) continue;
			if (putObj[prop] === undefined || putObj[prop] === null || typeof putObj[prop] === 'function') continue;
			if (typeof putObj[prop] === 'object' && putObj[prop]['ReferenceParameters']) {
				result += '' + putObj[prop].Address + '' + putObj[prop]['ReferenceParameters']["ResourceURI"] + '';
				var selectorArray = putObj[prop]['ReferenceParameters']['SelectorSet']['Selector'];
				if (Array.isArray(selectorArray)) {
					for (var i=0; i< selectorArray.length; i++) {
						result += '' + selectorArray[i]['Value'] + '';
					}
				}
				else {
					result += '' + selectorArray['Value'] + '';
				}
				result += '';
			}
			else {
			    if (Array.isArray(putObj[prop])) {
			        for (var i = 0; i < putObj[prop].length; i++) {
			            result += '' + putObj[prop][i].toString() + '';
			        }
			    } else {
			        result += '' + putObj[prop].toString() + '';
			    }
			}
		}
		result += '';
		return result;
	}
	/* 
	convert 
		{ @Name: 'InstanceID', @AttrName: 'Attribute Value'}
	into
		' Name="InstanceID" AttrName="Attribute Value" '
	*/
    function _ObjectToXmlAttributes(objWithAttributes) {
		if(!objWithAttributes) return '';
		var result = ' ';
		for (var propName in objWithAttributes) {
			if (!objWithAttributes.hasOwnProperty(propName) || propName.indexOf('@') !== 0) continue;
			result += propName.substring(1) + '="' + objWithAttributes[propName] + '" ';
		}
		return result;
	}
    function _PutObjToSelectorsXml(selectorSet) {
        if (!selectorSet) return '';
        if (typeof selectorSet == 'string') return selectorSet;
        if (selectorSet['InstanceID']) return "" + selectorSet['InstanceID'] + "";
		var result = '';
		for(var propName in selectorSet) {
		    if (!selectorSet.hasOwnProperty(propName)) continue;
		    result += '';
		    if (selectorSet[propName]['ReferenceParameters']) {
		        result += '';
		        result += '' + selectorSet[propName]['Address'] + '' + selectorSet[propName]['ReferenceParameters']['ResourceURI'] + '';
		        var selectorArray = selectorSet[propName]['ReferenceParameters']['SelectorSet']['Selector'];
		        if (Array.isArray(selectorArray)) {
		            for (var i = 0; i < selectorArray.length; i++) {
		                result += '' + selectorArray[i]['Value'] + '';
		            }
		        }
		        else {
		            result += '' + selectorArray['Value'] + '';
		        }
		        result += '';
		    } else {
		        result += selectorSet[propName];
		    }
			result += '';
		}
		result += '';
		return result;
	}
    function _turnToXml(text) {
        if (window.DOMParser) {
            return new DOMParser().parseFromString(text, "text/xml");
        }
        else // Internet Explorer
        {
            var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
            xmlDoc.async = false;
            xmlDoc.loadXML(text);
            return xmlDoc;
        }
    }
    return obj;
}