/*
 * This file is part of TbSync.
 *
 * TbSync is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * TbSync is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with TbSync. If not, see <https://www.gnu.org/licenses/>.
 */
 
 "use strict";

var xmltools = {

    isString : function (obj) {
        return (Object.prototype.toString.call(obj) === '[object String]');
    },
        
    checkString : function(d, fallback = "") {
        if (this.isString(d)) return d;
        else return fallback;
    },	    
        
    nodeAsArray : function (node) {
        let a = [];
        if (node) {
            //return, if already an array
            if (node instanceof Array) return node;

            //else push node into an array
            a.push(node);
        }
        return a;
    },

    hasWbxmlDataField: function(wbxmlData, path) {
        if (wbxmlData) {		
            let pathElements = path.split(".");
            let data = wbxmlData;
            for (let x = 0; x < pathElements.length; x++) {
                if (data[pathElements[x]]) data = data[pathElements[x]];
                else return false;
            }
            return true;
        }
        return false;
    },

    getWbxmlDataField: function(wbxmlData,path) {
        if (wbxmlData) {		
            let pathElements = path.split(".");
            let data = wbxmlData;
            let valid = true;
            for (let x = 0; valid && x < pathElements.length; x++) {
                if (data[pathElements[x]]) data = data[pathElements[x]];
                else valid = false;
            }
            if (valid) return data;
        }
        return false
    },

    //print content of xml data object (if debug output enabled)
    printXmlData : function (data, printApplicationData, printData = true, lvl = 0) {
        if ((tbSync.prefSettings.getBoolPref("log.toconsole") || tbSync.prefSettings.getBoolPref("log.tofile"))) {
            let dump = "";
            
            for (let d in data) {
                if (typeof(data[d]) == "object") {
                    //if printData is false, stay false, otherwise check, if we need to switch to false
                    let nextLevelPrintData = (printData == false || (d == "ApplicationData" && printApplicationData == false)) ? false : true;
                    dump = dump + " ".repeat(lvl) + d + " => \n" + this.printXmlData(data[d], printApplicationData, nextLevelPrintData, lvl+1);
                    dump = dump + " ".repeat(lvl) + d + " <= \n";
                } else {
                    dump = dump + " ".repeat(lvl) + d + " = [" + ((printData || tbSync.prefSettings.getIntPref("log.userdatalevel")>0) ? data[d] : "***") + "]\n";
                }
            }
            if (lvl == 0) tbSync.dump("Extracted XML data", "\n" + dump);
            else return dump;
        }
    },

    getDataFromXMLString: function (str) {
        let data = null;
        let xml = "";        
        if (str == "") return data;
        
        let oParser = (Services.vc.compare(Services.appinfo.platformVersion, "61.*") >= 0) ? new DOMParser() : Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser);
        try {
            xml = oParser.parseFromString(str, "application/xml");
        } catch (e) {
            //however, domparser does not throw an error, it returns an error document
            //https://developer.mozilla.org/de/docs/Web/API/DOMParser
            //just in case
            throw eas.finishSync("mailformed-xml", eas.flags.abortWithError);
        }

        //check if xml is error document
        if (xml.documentElement.nodeName == "parsererror") {
            tbSync.dump("BAD XML", "The above XML and WBXML could not be parsed correctly, something is wrong.");
            throw eas.finishSync("mailformed-xml", eas.flags.abortWithError);
        }

        try {
            data = this.getDataFromXML(xml);
        } catch (e) {
            throw eas.finishSync("mailformed-data", eas.flags.abortWithError);
        }
        
        return data;
    },
    
    //create data object from XML node
    getDataFromXML : function (nodes) {
        
        /*
         * The passed nodes value could be an entire document in a single node (type 9) or a 
         * single element node (type 1) as returned by getElementById. It could however also 
         * be an array of nodes as returned by getElementsByTagName or a nodeList as returned
         * by childNodes. In that case node.length is defined.
         */        
        
        // create the return object
        let obj = {};
        let nodeList = [];
        let multiplicity = {};
        
        if (nodes.length === undefined) nodeList.push(nodes);
        else nodeList = nodes;
        
        // nodelist contains all childs, if two childs have the same name, we cannot add the chils as an object, but as an array of objects
        for (let node of nodeList) { 
            if (node.nodeType == 1 || node.nodeType == 3) {
                if (!multiplicity.hasOwnProperty(node.nodeName)) multiplicity[node.nodeName] = 0;
                multiplicity[node.nodeName]++;
                //if this nodeName has multiplicity > 1, prepare obj  (but only once)
                if (multiplicity[node.nodeName]==2) obj[node.nodeName] = [];
            }
        }

        // process nodes
        for (let node of nodeList) { 
            switch (node.nodeType) {
                case 9: 
                    //document node, dive directly and process all children
                    if (node.hasChildNodes) obj = this.getDataFromXML(node.childNodes);
                    break;
                case 1: 
                    //element node
                    if (node.hasChildNodes) {
                        //if this is an element with only one text child, do not dive, but get text childs value
                        let o;
                        if (node.childNodes.length == 1 && node.childNodes.item(0).nodeType==3) {
                            //the passed xml is a save xml with all special chars in the user data encoded by encodeURIComponent
                            o = decodeURIComponent(node.childNodes.item(0).nodeValue);
                        } else {
                            o = this.getDataFromXML(node.childNodes);
                        }
                        //check, if we can add the object directly, or if we have to push it into an array
                        if (multiplicity[node.nodeName]>1) obj[node.nodeName].push(o)
                        else obj[node.nodeName] = o; 
                    }
                    break;
            }
        }
        return obj;
    }
    
};

@@ -2223,35 +2223,8 @@
 Data
-, printData = true, lvl = 0
 ) %7B%0A
@@ -2338,839 +2338,92 @@
 e%22))
-) %7B%0A            let dump = %22%22;%0A            %0A            for (let d in data) %7B%0A                if (typeof(data%5Bd%5D) == %22object%22) %7B%0A                    //if printData is false, stay false, otherwise check, if we need to switch to false%0A                    let nextLevelPrintData = (printData == false %7C%7C (d == %22ApplicationData%22 && printApplicationData == false)) ? false : true;%0A                    dump = dump + %22 %22.repeat(lvl) + d + %22 =%3E %5Cn%22 + this.printXmlData(data%5Bd%5D, printApplicationData, nextLevelPrintData, lvl+1);%0A                    dump = dump + %22 %22.repeat(lvl) + d + %22 %3C= %5Cn%22;%0A                %7D else %7B%0A                    dump = dump + %22 %22.repeat(lvl) + d + %22 = %5B%22 + ((printData %7C%7C tbSync.prefSettings.getIntPref(%22log.userdatalevel%22)%3E0) ? data%5Bd%5D : %22***%22) + %22%5D%5Cn%22;%0A                %7D%0A            %7D%0A            if (lvl == 0)
+ && printApplicationData) %7B%0A            let dump = JSON.stringify(data);%0A           
  tbS
@@ -2471,38 +2471,8 @@
 p);%0A
-            else return dump;%0A
     

use JSON.stringify to print debug data