// var renamekeys = require("deep-rename-keys");
import { JSONPath } from 'jsonpath-plus';
import convert from './xml-js'
import lodash from "lodash";

// TODO: étudier possibilité de charger dynamiquement le fichier en mettant:
// - export default() dans les fichiers source
// - en important avec "import {fields} from './spaces/xml-converter.'+space+'Fields';"
import { mdFields } from "./spaces/xml-converter.mdFields";
import { getRecordFields } from "./spaces/xml-converter.getRecordFields";
import { getRecordsFields } from "./spaces/xml-converter.getRecordsFields";
import { getCapabilitiesFields } from "./spaces/xml-converter.getCapabilitiesFields";
import { getCapabilitiesWfsFields } from "./spaces/xml-converter.getCapabilitiesWfsFields";
import { getDomainsFields } from "./spaces/xml-converter.getDomainsFields";

export class XmlConverter {

    /**
     * Constructor function
     * 
     * @param {*} nameSpace 
     */
    constructor(nameSpace) {
        "ngInject";
        this.nameSpace = nameSpace || 'md';
        this.mdjs = {};
        this.mdxml = "";
    }


    /**
     * Convert xml to js object
     * 
     * @param {*} xml 
     * @param {*} options 
     */
    xml2js(xml, options) {
        this.mdjs = convert.xml2js(xml, options);
        return this.mdjs;
    }


    /**
     * Convert js object to xml
     * 
     * @param {*} js 
     * @param {*} options 
     */
    js2xml(js, options) {
        this.mdxml = convert.js2xml(js, options);
        return this.mdxml;
    }


    /**
     * Get config file with xpaths of fields according nameSpace
     * 
     * @param {*} nameSpace 
     */
    getFields(nameSpace) {
        this.nameSpace = nameSpace || "md";
        if (this.nameSpace == "md") {
            return mdFields;
        } else if (this.nameSpace == "getRecord") {
            return getRecordFields;
        } else if (this.nameSpace == "getRecords") {
            return getRecordsFields;
        } else if (this.nameSpace == "getCapabilities") {
            return getCapabilitiesFields;
        } else if (this.nameSpace == "getCapabilitiesWfs") {
            return getCapabilitiesWfsFields;
        } else if (this.nameSpace == "getDomains") {
            return getDomainsFields;
        }
    }


    /**
     * Gestion des xpaths venant de mdFields.js
     * 
     * 1. xpath simple "xpaths: ['...']" => xpath = xpaths[0] si xpaths.length == 1
     * 2. xpath composé "xpaths: ['...']" => xpath = xpaths.join('.')
     * 3. xpath simple avec codelistValue: existance d'un attribut "xpaths_code"
     * 4. xpath composé avec codelistValue: 2. + 3.
     *
     * @param {*} xpaths
     * @returns
     * @memberof XmlConverter
     */
    getXpath(xpaths) {
        // By default use last path from xpaths.value array
        var xpath = xpaths.value[xpaths.value.length - 1];
        if (xpaths.hasOwnProperty("root") && xpaths.root.length) {
            xpath = xpaths.root.concat(xpath).join(".");
        }
        return xpath;
    }


    /**
     * Clean xpath
     * Replacing all '[*]' by '[0]', then remove final [0] and [*]
     *
     * Example:
     *  - 'a[0].b[0].c[*].d[0].e[*]' => 'a[0].b[0].c[0].d[0].e'
     * 
     * @param {*} xpaths
     * @returns
     * @memberof XmlConverter
     */
    cleanXpath(xpath, type) {
        return xpath.replace('[*]', '[0]').replace(/\[[\*|0]\]$/, "");
    }


    /**
     * Get value of object from xpath using fieldName
     *
     * @param {*} object
     * @param {*} fieldName
     * @param {*} nameSpace
     * @returns
     * @memberof XmlConverter
     */
    getValue(object, fieldName, nameSpace) {
        if (fieldName) {
            var fields = this.getFields(nameSpace);
            var xpath = this.getXpath(fields[fieldName].xpaths);
            // return JSONPath.query(object, xpath);
            // const options = {
            //     path: xpath,
            //     json: object,
            //     options: {flatten: true}
            // }
            // return JSONPath(options);
            // console.log(777, xpath, JSONPath(xpath, object));
            // console.log(777, fieldName, xpath);
            return JSONPath(xpath, object) || [];
        }
        return object;
    }


    /**
     * Set value of object from xpath using fieldName
     * 
     * Principes:
     * - Les xpaths sont récupérés dans un fichier de configuration à partir du fieldName demandé.
     * - Les xpaths sont de la forme 'a[0].b[0].c[*].d[0].e[*]' (chaque node est un tableau).
     * - Les xpaths peuvent possédés des [*] car ils sont utilisés par JSONPath dans la fonction getValue(). Les [*] doivent être remplacés par [0] pour que les xpaths soient utilisables avec lodash.set().
     * - Si un tableau est ajouté via un xpath, le xpath utilisé ne doit pas posséder de [0] ou [*] final au risque de créer un tableau dans un tableau.
     * - Les xpaths sont composés de 2 parties: la partie 'root' (tableau de xpaths) qui détermine le positionnement de l'object enfant par rapport à la racine de l'object parent et la partie 'value' (tableau de xpaths) qui détermine le chemin de base de construction de l'objet enfant.
     * - Les attributs sont définis à part dans xpaths.attributes. Chaque attribut est un tableau dont l'indice 0 est le xpath et l'indice 1 est la valeur de l'attribut. Les attributs doivent être ajoutés à l'objet enfant lors de la génération de celui-ci.
     * - En l'absence de 'root', la place de l'objet enfant est défini via xpaths.value par rapport à la racine du parent et peut être ajouté directement.
     * - En présence de 'root', les valeurs passées à la fonctions sont utilisées pour générer un tableau d'objets enfants. C'est ce tableau d'objects enfants qui est ensuite ajouté au parent en utilisant une concaténation des éléments de 'root' puis nettoyage du xpath resultant (remplacement de [*] par [0] et supression du [*] ou [0] final).
     * 
     * @param {*} object 
     * @param {*} fieldName 
     * @param {*} values 
     * @param {*} nameSpace
     * @returns
     * @memberof XmlConverter 
     */
    setValue(object, fieldName, values, nameSpace) {
        var fields = this.getFields(nameSpace);
        var xpaths = fields[fieldName].xpaths;
        var attributes = fields[fieldName].attributes;
        // console.log(object, fieldName, values, attributes);

        // xpaths.root exists and not empty: generate array of children objects and add to parent using xpaths.root path
        if (xpaths.root && xpaths.root.length) {
            // Prepare root path: replace all [*] by [0] in path and remove the last [*] or [0] 
            // var path = this.cleanXpath(xpaths.paths.join("."));
            var path = this.cleanXpath(xpaths.root.join("."));

            // Generate array of objects from values
            var objectValues = [];
            for (var v = 0; v < values.length; v++) {
                // console.log(111);
                var objectValue = {};
                // Add object value to objectValue
                for (var x = 0; x < xpaths.value.length; x++) {
                    objectValue = lodash.set(objectValue, xpaths.value[x], values[v]);
                }
                // console.log(541, objectValue, values[v], path, xpaths.value[x])
                // Add object attributes to objectValue
                if (attributes) {
                    // for (var a = 0; a < attributes.length; a++) {
                    for (const a in attributes) {
                        // console.log(542, fieldName, attributes[a]);
                        objectValue = lodash.set(objectValue, attributes[a][0], attributes[a][1]);
                    }
                }
                objectValues.push(objectValue);
            }
            // console.log(543, fieldName, object, Object.assign({}, objectValues))
            return lodash.set(object, path, objectValues);
            
            // xpaths.root not exists or emty: generate child object and add directly to parent object
        } else {
            // Add object value
            // for (var x = 0; x < xpaths.value.length; x++) {
            //     // Prepare path: replace all [*] by [0] in path and remove the last [*] or [0] if necessary
            //     console.log(xpaths.value[x]);
            //     var path = this.cleanXpath(xpaths.value[x].join("."));
            //     object = lodash.set(object, path, values);
            // }

            // var path = this.cleanXpath(xpaths.value.join("."));
            // object = lodash.set(object, path, values);
            // console.log(546, fieldName, object, values)
            // // Add object attributes
            // if (attributes) {
            //     console.log(544, fieldName)
            //     for (var a = 0; a < attributes.length; a++) {
            //         object = lodash.set(object, attributes[a][0], attributes[a][1]);
            //     }
            // }
            // console.log(545, fieldName)cd ui

            // console.log(112, attributes);

            for (var x = 0; x < xpaths.value.length; x++) {
                var path = this.cleanXpath(xpaths.value[x]);
                object = lodash.set(object, path, values);
            }
            if (attributes) {
                // for (var a = 0; a < attributes.length; a++) {
                for (const a in attributes) {
                    // console.log(544, fieldName, attributes[a]);
                    object = lodash.set(object, attributes[a][0], attributes[a][1]);
                }
            }

            return object;
        }
    }

}