/**
 * Created by Tony on 2017/6/21.
 * Modified by CSL, 2017-08-01 引入多套计算程序、费率同步、人工系数同步、改进基数计算、费字段映射等。
 * added by CSL, 2017-09-01 增加公式解析对象analyzer，用于解析用户修改公式、自定义表达式。
 */

let executeObj = {
    treeNode: null,
    template: null,
    calcBase: null,

    at: function(ID) {
        let me = executeObj,
            rst = 0;
        rst = me.template.compiledCalcItems[ID].unitFee;
        rst = parseFloat(rst);
        return rst;
    },
    base: function(calcBaseName) {
        let me = executeObj, rst = 0,
            //base = getRationCalcBase(calcBaseName);
            base = me.calcBase[calcBaseName];

        if (base != null) {
            let price = 0, aprice = 0, mprice = 0, tmpSum = 0;
            for (let glj of me.treeNode.data.gljList) {
                if (base.gljTypes.indexOf(glj.type) >= 0) {
                    if (base.calcType == baseCalc){ price = glj["basePrice"];}
                    else if (base.calcType == adjustCalc){price = glj["adjustPrice"];}
                    else if (base.calcType == budgetCalc){price = glj["marketPrice"];}
                    else if (base.calcType == diffCalc){
                        aprice = glj["adjustPrice"];
                        if (!aprice) aprice = 0;
                        mprice = glj["marketPrice"];
                        if (!mprice) mprice = 0;
                        price = mprice - aprice;
                    };
                    if (!price) price = 0;
                    tmpSum = tmpSum + glj["quantity"] * price;
                };
            };
            rst = tmpSum;
        };
        return rst;
    }
};

let analyzer = {
    calcTemplate: null,
    success: true,

    standard: function(expr){
        let str = expr;
        str = str.replace(/\s/g, "");               // 去空格、去中文空格
        str = str.replace(/（/g, "(");              // 中文括号"（"换成英文括号"("
        str = str.replace(/）/g, ")");              // 中文括号"）"换成英文括号")"
        str = str.replace(/f/g, "F");               // f换成F
        return str;
    },

    analyzeCalcBase: function(expr){
        // 前提：必须无空格、无特殊符号
        function getCalcBase(expr){
            let base = '',
                iPos1 = -1, iPos2 = -1;
            for (let i = 0; i < expr.length; i++) {
                if (expr[i] === '['){
                    iPos1 = i;
                }
                else if (iPos1 != -1 && expr[i]===']'){
                    iPos2 = i;
                };

                if (iPos1 != -1 && iPos2 != -1){
                    base = expr.slice(iPos1, iPos2 + 1);
                    break;
                }
            };
            return base;
        };
        function calcBaseToIDExpr(base){
            /*// for test. 公路模式，基数到ID
            let id = -1;
            if (base == '[人工费]'){
                id = 111;
            }
            else if (base == '[材料费]'){
                id = 222;
            }
            else if (base == '[机械费]'){
                id = 333;
            }
            else id = "错误";

            return "@('" + id + "')";*/
            let baseValue = base.slice(1, -1);
            return "base('" + baseValue + "')";
        };

        while (expr.indexOf('[') > 0) {
            let base = getCalcBase(expr);
            let id = calcBaseToIDExpr(base);
            let baseValue = base.slice(1, -1);   // []会给下面的正则带来干扰，这里去掉
            var pattBase =new RegExp(baseValue, "g");
            expr = expr.replace(pattBase, id);
            expr = expr.replace(/\[base\('/g, "base('");      // [@('       [base('
            expr = expr.replace(/'\)\]/g, "')");        // ')]
        };

        return expr;
    },

    analyzeLineRef: function(expr){
        let me = this;
        function isOperator(char){
            var operator = "+-*/()";
            return operator.indexOf(char) > -1;
        };
        function lineNumToID(lineNum){
            if (lineNum > me.calcTemplate.calcItems.length){
                me.success = false;
                return '越界';
            }
            else{
                let id = me.calcTemplate.calcItems[lineNum - 1].ID;
                return id;
            }
        };
        // 前提：必须无空格、无特殊符号、标准大写F
        function getSection(expr){
            let section = '',
                iPos1 = -1, iPos2 = -1;
            for (let i = 0; i < expr.length; i++) {
                if (expr[i] === 'F'){
                    iPos1 = i;
                }
                else if (iPos1 != -1 && isOperator(expr[i])){
                    iPos2 = i;
                }
                else if (iPos1 != -1 && i == expr.length - 1){
                    iPos2 = i + 1;

                };
                if (iPos1 != -1 && iPos2 != -1){
                    section = expr.slice(iPos1, iPos2);
                    break;
                }
            };
            return section;
        };
        function sectionToIDExpr(section){
            if (section){
                let lineNum = section.slice(1);
                if (isNaN(lineNum)){
                    me.success = false;
                    return '错误';      // 这里的返回提示不能加上section，因为会无限循环
                }
                else
                    return "@('" + lineNumToID(lineNum) + "')";
            }
            else return '';
        };

        while (expr.indexOf('F') > 0) {
            let sec = getSection(expr);
            let id = sectionToIDExpr(sec);
            var pattSec =new RegExp(sec, "g");
            expr = expr.replace(pattSec, id);
        };
        return expr;
    },

    analyzeUserExpr: function(calcTemplate, calcItem){
        let me = this;
        me.calcTemplate = calcTemplate;
        let expr = calcItem.dispExpr;
        // 标准化：处理特殊字符、中文符号、大小写
        expr = me.standard(expr);
        calcItem.dispExpr = expr;
        // 先换掉计算基数
        expr = me.analyzeCalcBase(expr);
        // 再换掉行引用
        expr = me.analyzeLineRef(expr);
        calcItem.expression = expr;
        return me.success;
    }
};

class Calculation {
    // 先编译公用的基础数据
    compilePublics(feeRates, labourCoes, feeTypes, calcBases){
        let me = this;
        let private_compile_feeRateFile = function() {
            if (feeRates) {
                me.compiledFeeRates = {};
                for (let rate of feeRates) {
                    me.compiledFeeRates["feeRate_" + rate.ID] = rate;
                }
            }
        };
        let private_compile_labourCoeFile = function() {
            if (labourCoes) {
                me.compiledLabourCoes = {};
                for (let coe of labourCoes) {
                    me.compiledLabourCoes["LabourCoe_" + coe.ID] = coe;
                }
            }
        };
        let private_compile_feeType = function() {
            if (feeTypes) {
                me.compiledFeeTypes = {};
                me.compiledFeeTypeNames = [];
                for (let ft of feeTypes) {
                    me.compiledFeeTypes[ft.type] = ft.name;
                    me.compiledFeeTypes[ft.name] = ft.type;    // 中文预编译，可靠性有待验证
                    me.compiledFeeTypeNames.push(ft.name);
                }
            }
        };
        let private_compile_calcBase = function() {
            if (calcBases) {
                me.compiledCalcBases = {};
                for (let cb of calcBases) {
                    me.compiledCalcBases[cb.dispName] = cb;         // 中文预编译，可靠性有待验证
                }
            }
        };

        private_compile_feeRateFile();
        private_compile_labourCoeFile();
        private_compile_feeType();
        private_compile_calcBase();
        me.compiledTemplates = {};
    };

    compileTemplate(template){
        let me = this;
        me.compiledTemplates[template.ID] = template;
        template.hasCompiled = false;
        template.errs = [];

        let private_extract_ID = function(str, idx){
            let rst = '', lBracket = 0, rBracket = 0, firstIdx = idx, lastIdx = 0;
            for (let i = idx; i < str.length; i++) {
                if (str[i] === '(') {
                    lBracket++;
                    if (lBracket == 1) firstIdx = i + 1;
                }
                if (str[i] === ')') {
                    rBracket++;
                    if (lBracket == rBracket) {
                        lastIdx = i - 1;
                        if (lastIdx > firstIdx) {
                            if (str[firstIdx] === "'") firstIdx++;
                            if (str[lastIdx] !== "'") lastIdx++;
                            if (lastIdx > firstIdx) {
                                rst = str.slice(firstIdx, lastIdx);
                            }
                        }
                        break;
                    }
                }
            }
            return rst;
        };
        let private_parse_ref = function(item, itemIdx){
            let idx = item.expression.indexOf('@(', 0);
            while (idx >= 0) {
                let ID = private_extract_ID(item.expression, idx);
                if (ID.length > 0) {
                    let subItem = template.compiledCalcItems[ID];
                    if (subItem) {
                        if (subItem.ID !== item.ID) {
                            private_parse_ref(subItem, template.compiledCalcItems[ID + "_idx"]);
                        } else {
                            template.errs.push("There exists the self refer ID: " + ID);
                        }
                    } else {
                        template.errs.push("There exists the invalid ID by which could not find the item: " + ID);
                        console.log('invalid ID： ' + ID);
                    }
                }
                idx = item.expression.indexOf('@(', idx + ID.length + 3);
            }
            if (template.compiledSeq.indexOf(itemIdx) < 0) {
                template.compiledSeq.push(itemIdx);
            }
        };
        let private_setup_seq = function(item, itemIdx){
            if (template.compiledSeq.indexOf(itemIdx) < 0) {
                private_parse_ref(item, itemIdx);
            }
        };
        let private_compile_items = function() {
            for (let idx of template.compiledSeq) {
                let item = template.calcItems[idx];
                item.compiledExpr = item.expression.split('@(').join('$CE.at(');
                item.compiledExpr = item.compiledExpr.split('base(').join('$CE.base(');

                if (item.labourCoeID){
                    let lc = me.compiledLabourCoes["LabourCoe_" + item.labourCoeID].coe;
                    item.dispExpr = item.dispExpr.replace(/L/gi, lc.toString());
                    item.compiledExpr = item.compiledExpr.replace(/L/gi, lc.toString());
                };

                if (item.feeRateID) {
                    item.feeRate = me.compiledFeeRates["feeRate_" + item.feeRateID].rate;
                };

                // 字段名映射
                item.displayFieldName = me.compiledFeeTypes[item.fieldName];
            }
        };

        if (template && template.calcItems && template.calcItems.length > 0) {
            template.compiledSeq = [];
            template.compiledCalcItems = {};

            for (let i = 0; i < template.calcItems.length; i++) {
                let item = template.calcItems[i];
                template.compiledCalcItems[item.ID] = item;
                template.compiledCalcItems[item.ID + "_idx"] = i;
            }

            for (let i = 0; i < template.calcItems.length; i++) {
                private_setup_seq(template.calcItems[i], i);
            }
            if (template.errs.length == 0) {
                private_compile_items();
                template.hasCompiled = true;
            } else {
                console.log('errors: ' + template.errs.toString());
            }
        };
    };

    calculate($treeNode){
        let me = this;
        let templateID = $treeNode.data.calcTemplateID;
        if (!templateID) templateID = 1;
        let template = me.compiledTemplates[templateID];
        $treeNode.data.calcTemplate = template;

        if ($treeNode && template.hasCompiled) {
            let $CE = executeObj;
            $CE.treeNode = $treeNode;
            $CE.template = template;
            $CE.calcBase = me.compiledCalcBases;

            if (!$treeNode.fees) {
                $treeNode.fees = [];
                $treeNode.feesIndex = {};
            };

            for (let idx of template.compiledSeq) {
                let calcItem = template.calcItems[idx];

                let feeRate = calcItem.feeRate;
                if (!feeRate) feeRate = 100;    // 100%
                calcItem.unitFee = eval(calcItem.compiledExpr) * feeRate * 0.01;   // 如果eval()对清单树有影响，就换成小麦的Expression对象再试

                let quantity = $treeNode.data.quantity;
                if (!quantity) quantity = 0;
                calcItem.totalFee = calcItem.unitFee * quantity;

                // 费用同步到定额
                // 引入小麦的字段检测后，快速切换定额出现计算卡顿现象，过多的循环造成。这里把她的代码拆出来，减少微循环。
                if (!$treeNode.data.feesIndex[calcItem.fieldName]){
                    let fee = {
                        'fieldName': calcItem.fieldName,
                        'unitFee': calcItem.unitFee,
                        'totalFee': calcItem.totalFee,
                        'tenderUnitFee': 0,
                        'tenderTotalFee': 0
                    };
                    $treeNode.data.fees.push(fee);
                    $treeNode.data.feesIndex[calcItem.fieldName] = fee;
                }
                else{
                    $treeNode.data.feesIndex[calcItem.fieldName].unitFee = calcItem.unitFee;
                    $treeNode.data.feesIndex[calcItem.fieldName].totalFee = calcItem.totalFee;
                }
            }

        }
    }
};

/*
export default analyzer;*/
