/**
 *
 *
 * @author Zhong
 * @date 2019/6/28
 * @version
 */

'use strict';

const importXMLBase = (() => {
    //人材机调整法
    const AdjustType = {
        info: 'priceInfo',  //造价信息差额调整法
        coe: 'priceCoe'     //价格指数调整法
    };
    const CONFIG = Object.freeze({
        AdjustType
    });

    // xml字符实体
    const XMLEntity = {
        '&#x0020;': 'escape{space}',
        '&#x20;': 'escape{simpleSpace}',
        '&#x0009;': 'escape{tab}',
        '&#x9;': 'escape{simpleTab}',
        '&#x000D;': 'escape{return}',
        '&#xD;': 'escape{simpleReturn}',
        '&#000A;': 'escape{newLine}',
        '&#xA;': 'escape{simpleNewLine}',
        '&lt;': 'escape{less}',
        '&gt;': 'escape{greater}',
        '&amp;': 'escape{and}',
        '&quot;': 'escape{quot}',
        '&apos;': 'escape{apos}'
    };

    // 避免字符实体进行转义。原文本中含有xml字符实体，转换为其他字符。
    function escapeXMLEntity(str) {
        for (const [key, value] of Object.entries(XMLEntity)) {
            str = str.replace(new RegExp(key, 'g'), value);
        }
        return str;
    }
    // 将文本还原为字符实体
    function restoreXMLEntity(str) {
        for (const [key, value] of Object.entries(XMLEntity)) {
            str = str.replace(new RegExp(value, 'g'), key);
        }
        return str;
    }

    /*
    * 读取文件转换为utf-8编码的字符串
    * @param {Blob}file
    * @return {Promise}
    * */
    function readAsTextSync(file) {
        return new Promise((resolve, reject) => {
            const fr = new FileReader();
            fr.readAsText(file);    // 默认utf-8，如果出现乱码，得看导入文件是什么编码
            fr.onload = function () {
                resolve(this.result);
            };
            fr.onerror = function () {
                reject('读取文件失败，请重试。');
            }
        });
    }

    /*
     * 根据字段数组获得所要字段的值 eg: 要获取标段下的单项工程： ['标段', '单项工程'];
     * @param {Object}source 源数据
     *        {Array}fields 字段数组
     * @return {String}
     * @example getValue(source, ['标段', '_文件类型'])
     * */
    function getValue(source, fields) {
        let cur = source;
        for (const field of fields) {
            if (!cur[field]) {
                return '';
            }
            cur = cur[field];
        }
        return cur || '';
    }
    // 获取数据类型
    function _plainType(v) {
        return Object.prototype.toString.call(v).slice(8, -1);
    }
    /*
     * 获取某字段的值，强制返回数组
     * @param {Object}source 数据源
     *        {Array}fields 取的字段
     * @return {Array}
     * @example arrayValue(source, ['标段', '单项工程'])
     * */
    function arrayValue(source, fields) {
        let target = getValue(source, fields);
        if (_plainType(target) === 'Object') {
            target = [target];
        } else if (_plainType(target) !== 'Array') {
            target = []
        }
        return target;
    }
    // 获取费用
    function getFee(fees, fields) {
        if (!Array.isArray(fees) || !fees.length) {
            return '0';
        }
        const feeData = fees.find(fee => fee.fieldName === fields[0]);
        return feeData && feeData[fields[1]] || '0';
    }
    // 合并价格
    function mergeFees(feesA, feesB) {
        if (!feesA) {
            return feesB;
        }
        if (!feesB) {
            return [];
        }
        feesB.forEach(feeB => {
            const sameKindFee = feesA.find(feeA => feeA.fieldName === feeB.fieldName);
            if (sameKindFee) {
                Object.assign(sameKindFee, feeB);
            } else {
                feesA.push(feeB);
            }
        });
        return feesA;
    }
    // 将A对象的一部分属性赋值到B对象上
    function assignAttr(target, source, attrs, isExcepted = false) {
        if (!source || !target) {
            return;
        }
        const sourceAttrs = attrs
            ? isExcepted
                ? Object.keys(source).filter(attr => !attrs.includes(attr))
                : attrs
            : Object.keys(source);
        sourceAttrs.forEach(attr => {
            // 如果是价格，不能简单地覆盖，要合并两个对象的价格
            target[attr] = attr === 'fees' ? mergeFees(target[attr], source[attr]) : source[attr];
        });
    }
    // 获取固定ID
    function getFlag(data) {
        return data.flags && data.flags[0] && data.flags[0].flag || 0;
    }
    // 获取布尔型的数据
    function getBool(v) {
        return v === 'true' ? true : false;
    }
    // 设置成树结构数据
    function setTreeData(data, parent, next) {
        const defalutID = -1;
        data.ID = uuid.v1();
        data.ParentID = parent && parent.ID || defalutID;
        data.NextSiblingID = next && next.ID || defalutID;
    }
    // 递归设置树结构数据，并返回设置好的数据，递归items数组
    function mergeDataRecur(parent, items) {
        const rst = [];
        for (let i = 0; i < items.length; i++) {
            const cur = items[i];
            const next = items[i + 1];
            setTreeData(cur, parent, next);
            rst.push(cur);
            if (cur.items && cur.items.length) {
                rst.push(...mergeDataRecur(cur, cur.items));
            }
        }
        return rst;
    }
    /*
     * 递归获取相关数据，eg：获取组织措施清单下的所有子清单，该子清单们可能由分类及公式措施项各种组合组成。（参考调用处）
     * @param {Object}src(数据源) {Array}fields(二维数组，数组里的成员由需要取的字段组成)
     *         eg: ['组织措施分类'], ['公式计算措施项']，该层数据可能由组织措施分类 或 公式计算措施项组成 (同层不可同时存在)
     *        {Function} 获得源数据后，需要提取的数据方法
     * @return {Array}
     * */
    function getItemsRecur(src, fields, extractFuc) {
        let itemsSrc = [];
        let curField = [''];
        for (const field of fields) {
            itemsSrc = arrayValue(src, field);
            if (itemsSrc.length) {
                curField = field;
                break;
            }
        }
        return itemsSrc.map(itemSrc => {
            const obj = extractFuc(itemSrc, curField);
            obj.items = getItemsRecur(itemSrc, fields, extractFuc);
            return obj;
        });
    }

    // 递归获取相关数据，与上方的方法不同的点在：同层可能出现不同节点,上方方法暂时不取消(防止bug)
    // fields内字段的顺序即决定了提取数据类型的顺序，如fields = [['gruop'], ['item']]，则提取的数据同层中group数据在item数据之前
    function extractItemsRecur(src, fields, extractFuc) {
        const rst = [];
        for (const field of fields) {
            const itemsSrc = arrayValue(src, field);
            if (itemsSrc.length) {
                const items = itemsSrc.map(itemSrc => {
                    const obj = extractFuc(itemSrc, field);
                    obj.items = extractItemsRecur(itemSrc, fields, extractFuc);
                    return obj;
                });
                rst.push(...items);
            }
        }
        return rst;
    }

    /*
    * 转换计算基数
    * 1.有子项数据，则清空基数
    * 2.行代号引用转换为ID引用
    * 3.对应字典代号转换，对应字典里找不到则设置成金额
    * @param {Array}billsData 清单数据
    *        {Object}CalcBaseMap 基数映射
    * @return {void}
    * */
    function transformCalcBase(billsData, CalcBaseMap) {
        //行代号 - ID映射
        let rowCodeMap = {};
        billsData.forEach(data => {
            if (data.rowCode) {
                rowCodeMap[data.rowCode] = data.ID;
            }
        });
        for (let bills of billsData) {
            if (!bills.calcBase) {
                continue;
            }
            let sub = billsData.find(data => data.ParentID === bills.ID);
            //有子项数据，则清空基数，费率
            if (sub) {
                bills.calcBase = '';
                bills.feeRate = '';
                continue;
            }
            //提取基数
            bills.calcBase = bills.calcBase.replace(/\s/g, '');
            let bases = bills.calcBase.split(/[\+\-\*\/]/g);
            //提取操作符
            let oprs = bills.calcBase.match(/[\+\-\*\/]/g);
            //转换后的基数
            let newBase = [];
            let illegal = false;    //不合法
            for (let base of bases) {
                if (rowCodeMap[base]) { //行引用
                    newBase.push(`@${rowCodeMap[base]}`);
                } else if (CalcBaseMap[base]) { //基数字典
                    newBase.push(CalcBaseMap[base]);
                } else {    //都没匹配到，说明软件无法识别此基数
                    illegal = true;
                    break;
                }
            };
            if (illegal) {
                let fee = getFee(bills.fees, ['common', 'totalFee']);
                let feeRate = bills.feeRate && parseFloat(bills.feeRate) !== 0 ? parseFloat(bills.feeRate) : 0;
                if (fee && parseFloat(fee) !== 0 && feeRate) {
                    bills.calcBase = scMathUtil.roundForObj(parseFloat(fee) * 100 / feeRate, 2);
                } else {
                    bills.calcBase = fee !== '0' ? fee : '';
                }
            } else {
                let newCalcBase = '';
                for (let i = 0; i < newBase.length; i++) {
                    newCalcBase += newBase[i];
                    if (oprs && oprs[i]) {
                        newCalcBase += oprs[i];
                    }
                }
                bills.calcBase = newCalcBase;
            }
        }
    }
    // 获取必要的清单模板
    async function getNeedfulTemplate(templateLibID) {
        return await ajaxPost('/template/bills/api/getNeedfulTemplate',
            { templateLibID });
    }

    /**
     * 根据编码内容解码数据
     * @param {Array} unit8ArraySouces - unit8array的数据源
     * @param {String} encoding - 编码方式
     * @return {Promise Array}
     */
    async function getDecodedData(unit8ArraySouces, encoding) {
        return await ajaxPost('/project/getDecodedData', { unit8ArraySouces, encoding });
    }

    const UTIL = Object.freeze({
        escapeXMLEntity,
        restoreXMLEntity,
        getValue,
        arrayValue,
        getFee,
        mergeFees,
        assignAttr,
        setTreeData,
        mergeDataRecur,
        getFlag,
        getBool,
        getItemsRecur,
        extractItemsRecur,
        readAsTextSync,
        getDecodedData,
    });

    // 获取接受上传的文件类型正则表达式
    function getAcceptReg(accepts) {
        const acceptsExp = accepts.reduce((acc, cur, index) => {
            return acc + `${index ? '|' : ''}\\${cur}`;
        }, '');
        return new RegExp(`(${acceptsExp})`, 'i');
    }

    return {
        CONFIG,
        UTIL,
        getAcceptReg,
    }
})();
