'use strict';

/**
 *
 *
 * @author Zhong
 * @date 2018/6/11
 * @version
 */

//清单指引/精灵获取完清单数据后的回调函数
let doAfterLoadGuidance = null;

const billsGuidance = (function () {
    //更新类型
    const updateType = {update: 'update', create: 'create'};
    //库类型
    const libType = {'guidance': 1, 'elf': 2}; //清单指引、清单精灵
    const libSel = $('#stdBillsGuidanceLibSelect');
    //工作内容
    let stdBillsJobData = [];
    //项目特征
    let stdBillsFeatureData = [];
    //正在插入
    let isInserting = false;
    const bills = {
        dom: $('#billsGuidance_bills'),
        workBook: null,
        cache: [],
        tree: null,
        controller: null,
        treeSetting: {
            emptyRowHeader: true,
            rowHeaderWidth: 15,
            treeCol: 0,
            emptyRows: 0,
            headRows: 1,
            headRowHeight: [40],
            defaultRowHeight: 21,
            cols: [{
                width: 105,
                readOnly: true,
                showHint: true,
                head: {
                    titleNames: ["项目编码"],
                    spanCols: [1],
                    spanRows: [1],
                    vAlign: [1],
                    hAlign: [1],
                    font: ["Arial"]
                },
                data: {
                    field: "code",
                    vAlign: 1,
                    hAlign: 0,
                    font: "Arial"
                }
            }, {
                width: 190,
                readOnly: true,
                head: {
                    titleNames: ["项目名称"],
                    spanCols: [1],
                    spanRows: [1],
                    vAlign: [1],
                    hAlign: [1],
                    font: ["Arial"]
                },
                data: {
                    field: "name",
                    vAlign: 1,
                    hAlign: 0,
                    font: "Arial"
                }
            },
               {
                width: 60,
                readOnly: true,
                 head: {
                     titleNames: ["计量单位"],
                     spanCols: [1],
                     spanRows: [1],
                     vAlign: [1],
                     hAlign: [1],
                     font: ["Arial"]
                 },
                 data: {
                     field: "unit",
                     vAlign: 1,
                     hAlign: 1,
                     font: "Arial"
                 }
            }
            ]
        },
        headers: [
            {name: '项目编码', dataCode: 'code', width: 105, vAlign: 'center', hAlign: 'left', formatter: '@'},
            {name: '项目名称', dataCode: 'name', width: 190, vAlign: 'center', hAlign: 'left', formatter: '@'},
            {name: '单位', dataCode: 'unit', width: 60, vAlign: 'center', hAlign: 'center', formatter: '@'},
        ],
        rowHeaderWidth:1,
        events: {
            CellDoubleClick: function (sender, args) {
                if(!bills.tree){
                    return;
                }
                let node = bills.tree.items[args.row];
                if(!node){
                    return;
                }
                //展开收起(非最底层节点且双击的是第一列)
                if (args.col === 0 && node.children.length > 0) {
                    node.setExpanded(!node.expanded);
                    //设置展开收起状态
                    sessionStorage.setItem('stdBillsGuidanceExpState', bills.tree.getExpState(bills.tree.items));
                    renderSheetFunc(args.sheet, function () {
                        let iCount = node.posterityCount(), i, child;
                        for (i = 0; i < iCount; i++) {
                            child = bills.tree.items[args.row + i + 1];
                            args.sheet.setRowVisible(args.row + i + 1, child.visible, args.sheetArea);
                        }
                        args.sheet.invalidateLayout();
                    });
                    args.sheet.repaint();
                } else if (!projectReadOnly && !isInserting && !projectObj.project.isBillsLocked() && (args.col !== 0 || node.children.length === 0)) {
                    //选中部分的最底层（只是选中部分的最底）
                    let lowestNodes = [bills.tree.items[args.row]];
                    insertBills(lowestNodes);
                }
            }
        }
    };
    // 获取对比树片段数据的方法，此方法可能会被覆盖，方法存在一个对象中，使得外部可以覆盖相关方法
    // 在农村公路2020中，主树对比片段与这里的逻辑是不相同的
    const overwrite = {
        getFragment() {
            return { parent: null, mainTreeFragment: projectObj.project.Bills.tree.roots }
        }
    };
    //插入清单
    async function insertBills(lowestNodes) {
        try {
            let selTree = getSelTree(lowestNodes);
            const { errMsg, parent, mainTreeFragment } = overwrite.getFragment();
            if (errMsg) {
                alert(errMsg);
                return;
            }
            let compareData = compareTree(parent, mainTreeFragment, selTree.roots);
            let sheet = projectObj.mainSpread.getActiveSheet(),
                row = sheet.getActiveColumnIndex(),
                col = sheet.getActiveColumnIndex();
            if (compareData.postData.length > 0) {
                //如果插入的是固定清单，则需要判断该固定清单在造价书中是否已存在，造价书中不可存在相同的固定清单
                let fixedDatas = compareData.postData.filter((data) =>
                data.updateType === updateType.create && Array.isArray(data.updateData.flags));
                if (fixedDatas.length > 0) {
                    //提示已存在此固定清单并且定位
                    let firstFixed = fixedDatas[0].updateData;
                    let existNode = projectObj.project.mainTree.items.find((node) =>
                    node.data && node.data.flagsIndex && node.data.flagsIndex.fixed && node.data.flagsIndex.fixed.flag === firstFixed.flags[0].flag);
                    if (existNode) {
                        alert(`固定清单<strong>“${firstFixed.name}”</strong>已被第${existNode.serialNo() + 1}行清单占用。`);
                        locateAtSpread(sheet, existNode.serialNo(), col);
                        return;
                    }
                }
                isInserting = true;
                const newNodes = await ProjectController.addBillsByData(compareData.postData);
                row = newNodes[newNodes.length - 1].serialNo();
                //有新的节点插入，也有可能定位至旧节点（批量选用的情况下）
                if (compareData.locateNode) {
                    //该清单节点在主树的位置
                    row = projectObj.project.mainTree.nodes[projectObj.project.mainTree.prefix + compareData.locateNode.data.ID].serialNo();
                }
                locateAtSpread(sheet, row, col);
            } else if (compareData.locateNode) {
                //该清单节点在主树的位置
                row = projectObj.project.mainTree.nodes[projectObj.project.mainTree.prefix + compareData.locateNode.data.ID].serialNo();
                locateAtSpread(sheet, row, col);
            }
        } catch (err) {
            console.log(err);
            if (!$('hintBox_form').is(':visible')) {
                alert(err);
            }
        } finally {
            isInserting = false;
        }
    }
    /* function insertBills(lowestNodes) {
        let selTree = getSelTree(lowestNodes);
        const { errMsg, parent, mainTreeFragment } = overwrite.getFragment();
        if (errMsg) {
            alert(errMsg);
            return;
        }
        let compareData = compareTree(parent, mainTreeFragment, selTree.roots);
        let sheet = projectObj.mainSpread.getActiveSheet(),
            row = sheet.getActiveColumnIndex(),
            col = sheet.getActiveColumnIndex();
        if (compareData.postData.length > 0) {
            //如果插入的是固定清单，则需要判断该固定清单在造价书中是否已存在，造价书中不可存在相同的固定清单
            let fixedDatas = compareData.postData.filter((data) =>
            data.updateType === updateType.create && Array.isArray(data.updateData.flags));
            if (fixedDatas.length > 0) {
                //提示已存在此固定清单并且定位
                let firstFixed = fixedDatas[0].updateData;
                let existNode = projectObj.project.mainTree.items.find((node) =>
                node.data && node.data.flagsIndex && node.data.flagsIndex.fixed && node.data.flagsIndex.fixed.flag === firstFixed.flags[0].flag);
                if (existNode) {
                    alert(`固定清单<strong>“${firstFixed.name}”</strong>已被第${existNode.serialNo() + 1}行清单占用。`);
                    locateAtSpread(sheet, existNode.serialNo(), col);
                    return;
                }
            }
            isInserting = true;
            CommonAjax.post('/bills/insertBills', {postData: compareData.postData}, function () {
                //插入
                let insertData = _.filter(compareData.postData, {updateType: updateType.create});
                let treeData = [];
                for (let data of insertData) {
                    treeData.push(data.updateData);
                }
                //插入清单节点
                projectObj.project.Bills.tree.insertByDatas(treeData);
                projectObj.project.Bills.datas = projectObj.project.Bills.datas.concat(treeData);
                //插入主树节点
                let newNodes = projectObj.project.mainTree.insertByDatas(treeData);
                for (let node of newNodes) {
                    node.source = projectObj.project.Bills.tree.nodes[projectObj.project.Bills.tree.prefix + node.getID()];
                    node.data = node.source.data;
                    node.sourceType = projectObj.project.Bills.getSourceType();
                }
                ProjectController.syncDisplayNewNodes(projectObj.mainController, newNodes, true);
                row = newNodes[newNodes.length - 1].serialNo();
                //有新的节点插入，也有可能定位至旧节点（批量选用的情况下）
                if (compareData.locateNode) {
                    //该清单节点在主树的位置
                    row = projectObj.project.mainTree.nodes[projectObj.project.mainTree.prefix + compareData.locateNode.data.ID].serialNo();
                }
                locateAtSpread(sheet, row, col);
                isInserting = false;
            }, function () {
                isInserting = false;
            });
        } else if (compareData.locateNode) {
            //该清单节点在主树的位置
            row = projectObj.project.mainTree.nodes[projectObj.project.mainTree.prefix + compareData.locateNode.data.ID].serialNo();
            locateAtSpread(sheet, row, col);
        }
    } */
    function locateAtSpread(sheet, row, col) {
        sheet.setSelection(row, col, 1, 1);
        projectObj.mainController.setTreeSelected(projectObj.mainController.tree.items[row]);//触发树节点选中事件
        sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
    }
    /*
    *
    * 1.选中的树结构（清单规则选中的节点及其所有父项）与主树对比(主树中节点“编码-名称-单位”与选中树~组合相同视为同一节点)，
    *   将主树中不含有的选中节点全部插入
    * 2.插入位置由1对比得出，主树有与选中树结构相同层次结构的片段，这个相同片段为插入位置
    * 3.选中树结构除去相同片段，为需要插入的节点，插入时，遇到同层节点，根据编码的字符编码值确定顺序
    *   a.code <= b.code，则a节点在b节点前
    * */
    //获取选中的树(将选中的节点及其所有父项，组合成一棵树，没有重复节点)
    //@param {Array}lowestNodes(选中的最底层节点)  @return {Array}
    function getSelTree(lowestNodes) {
        let allNodes = [];
        //获取树上所有的节点
        for (let node of lowestNodes) {
            while (node && !allNodes.includes(node)) {
                allNodes.push(node);
                node = node.parent;
            }
        }
        //根据原节点serialNo排序，排序后，后一项节点只可能前节点的后兄弟项，或子项(根据原节点ID-ParentID判定)
        allNodes.sort(function (a, b) {
            let aV = a.serialNo(),
                bV = b.serialNo();
            if (aV > bV) {
                return 1;
            } else if (aV < bV) {
                return -1;
            }
            return 0;
        });
        //生成树数据
        let treeData = [];
        //旧ID与新ID映射
        let IDMapping = {};
        for (let i = 0; i < allNodes.length; i++) {
            //原节点
            let preN = allNodes[i - 1],
                thisN = allNodes[i];
            let newNodeData = {ID: uuid.v1(), NextSiblingID: -1, ParentID: -1, orgID: thisN.data.ID};
            IDMapping[newNodeData.orgID] = newNodeData.ID;
            treeData.push(newNodeData);
            //新树数据
            let preData = treeData[i - 1],
                thisData = treeData[i];
            //节点与上节点为同层节点，则此节点设为上节点的后兄弟
            if (preN && preN.data.ParentID === thisN.data.ParentID) {
                preData.NextSiblingID = thisData.ID;
                //节点与上节点不为同层节点，则此节点为某上节点的子几点
            } else if (preN && preN.data.ParentID !== thisN.data.ParentID) {
                let parentID = IDMapping[thisN.data.ParentID];
                thisData.ParentID = parentID;
            }
        }
        let selTree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
        selTree.loadDatas(treeData);
        return selTree;
    }
    /*
    * 选中树与清单主树进行对比，(自上而下，从roots开始)获取插入、更新数据
    * 找到的清单树中清单子项含有非清单项或输入了计算基数，则中止该清单的对比，不可插入数据
    * */

    //获取节点的匹配数据：编码-名称-单位
    //@param {Object}node @return {String}
    function getMatchContent(node) {
        return `${node.data.code ? node.data.code : '*'}-${node.data.name ? node.data.name : '*'}-${node.data.unit ? node.data.unit: '*'}`;
    }
    //**对比清单主树节点片段与选中树节点片段，获取需要更新和插入的数据**
    //@param {Object}parent - 主树片段的父节点 {Array}mainTreeFragment {Array}stdTreeFragment
    function compareTree(parent, mainTreeFragment, stdTreeFragment) {
        //需要插入、更新的数据
        let postData = [],
            //跟树结构自动定位至的清单节点（最近匹配到的节点）
            locateNode = null;
        if (!mainTreeFragment || !stdTreeFragment) {
            return postData;
        }
        comparePeer(parent, mainTreeFragment, stdTreeFragment);
        return {postData, locateNode};
        /*
        * 该清单节点是否可以继续往下递归匹配，即该节点是否还可插入子项(在该层匹配到的时候判断)
        * 1.该底层节点不能含有非清单子项
        * 2.该节点不能含有计算基数
        * todo 3.数量单价等相关概念，等有了补上
        * @param {Object}billsNode(清单树中的某节点) @return {Boolean}
        * */
        function canRecursive(billsNode) {
            //主树节点含有子节点，清单节点不含子节点，说明该节点含有非清单子项
            //这里需要去mainTree查，不能在清单树查，因为清单树的节点children里只会有清单，就算实际上有定额，这个children里也不含该定额
            let mainTreeNode = projectObj.project.mainTree.getNodeByID(billsNode.data.ID);
            if (!mainTreeNode) {
                return false;
            }
            if (mainTreeNode && mainTreeNode.children.length > 0 && billsNode.children.length === 0) {
                return false;
            } else if (billsNode.data.calcBase) {
                return false;
            }
            return true;
        }
        //获取某选中节点要往清单主树同层插入的位置
        function insertPos(peerNodes, node) {
            //node选中树中没有记录原清单的数据，只记录了原清单ID，需要node的数据，则要找回原清单
            let orgNode = bills.tree.nodes[`${bills.tree.prefix}${node.data.orgID}`];
            if (!orgNode) {
                return -1;
            }
            let insertCode = orgNode.data.code ? orgNode.data.code : '';
            //插入最顶层节点或者无编码，对比选中节点后兄弟节点和同层主树节点的名称，插在的匹配的节点前面
            function mathNext(selNode, compareNodes) {
                let selNext = selNode.nextSibling;
                while (selNext) {
                    for (let i = 0; i < compareNodes.length; i++) {
                        if (compareNodes[i].data.name === selNext.data.name) {
                            return i;
                        }
                    }
                    selNext = selNext.nextSibling;
                }
                return compareNodes.length;
            }
            if (orgNode.depth() === 0 || insertCode === '') {
                return mathNext(orgNode, peerNodes);
                //return peerNodes.length;
            }
            for (let i = 0; i < peerNodes.length; i++) {
                let thisNode = peerNodes[i];
                let thisCode = thisNode.data.code ? thisNode.data.code : '';
                //确定同层节点的顺序，编码小于等于在前，大于在后
                if (insertCode <= thisCode) {
                    return i;
                } else if (insertCode > thisCode) {
                    continue;
                }
            }
            return peerNodes.length;
        }
        //获取插入清单数据
        function getInsertData(insertObj) {
            let stdNode = bills.tree.nodes[`${bills.tree.prefix}${insertObj.orgID}`];
            if (!stdNode) {
                return null;
            }
            let stdData = {};
            stdData.projectID = projectObj.project.projectInfo.ID;
            stdData.isAdd = 1;
            stdData.ID = insertObj.ID;
            stdData.ParentID = insertObj.ParentID;
            stdData.NextSiblingID = insertObj.NextSiblingID;
            //顶层节点是大项费用
            stdData.type = stdNode.parent ? billType.BILL : billType.DXFY;
            stdData.code = stdNode.data.code;
            stdData.name = stdNode.data.name;
            stdData.unit = stdNode.data.unit;
            stdData.ruleText = stdNode.data.ruleText;
            stdData.comments = stdNode.data.comments;
            stdData.programID = stdNode.data.engineering;
            stdData.billsLibId = stdNode.data.billsLibId;
            if (stdNode.data.fixedFlag) {
                stdData.flags = [{flag : stdNode.data.fixedFlag, fieldName : 'fixed'}];
                stdData.flagsIndex = {fixed: {fieldName: 'fixed', flag: stdNode.data.fixedFlag}};   //前端用
            }
            if (projectObj.project.property.valuationType === commonConstants.ValuationType.BOQ) {
                stdData.unitPriceAnalysis = 1;
            }
            return stdData;
        }
        //从同层节点中获取更新数据
        //@param {Object}parentNode(同层节点挂载的父节点,清单主树中) {Array}peerNodes(同层节点，含有清单树和选中树节点)
        //       {Array}billsNodes(该层清单树节点) {Array}selNodes(该层选中树节点) @return {Array}
        function getUpdateDataFromPeer(parentNode, peerNodes, billsNodes, selNodes) {
            let rst = [];
            //向下获取插入数据直到最底层
            function getDataTillDeepest(node) {
                if (node) {
                    let insertData = getInsertData(node.data);
                    if (insertData) {
                        rst.push({updateType: updateType.create, updateData: insertData});
                    }
                    for (let child of node.children) {
                        getDataTillDeepest(child);
                    }
                }
            }
            for (let i = 0; i < peerNodes.length; i++) {
                let thisNode = peerNodes[i],
                    nextNode = peerNodes[i + 1];
                //更新原清单节点NextSiblingID
                if (billsNodes.includes(thisNode) && selNodes.includes(nextNode)) {
                    rst.push({updateType: updateType.update, updateData: {ID: thisNode.data.ID, NextSiblingID: nextNode.data.ID}});
                } else if (selNodes.includes(thisNode) && billsNodes.includes(nextNode)) { //变更选中节点的NextSiblingID
                    thisNode.data.NextSiblingID = nextNode.data.ID;
                }
                //所有选中的同层节点设为清单树父节点的子项,获取插入数据(插入该同层节点及其所有子节点)
                if (selNodes.includes(thisNode)) {
                    thisNode.data.ParentID = parentNode ? parentNode.data.ID : -1;
                    getDataTillDeepest(thisNode);
                }
            }
            return rst;
        }
        //同层节点之间比较，匹配到的则继续往下匹配，匹配不到的节点则按照规定的顺序进行排序，插入更新(更新清单树节点中NextSiblingID改变的节点)
        function comparePeer(parentNode, billsNodes, selNodes) {
            let peerNodes = [].concat(billsNodes); //同层节点
            let matchNode = null; //匹配到的清单主树节点
            for (let selNode of selNodes) {
                let stdNode = bills.tree.nodes[`${bills.tree.prefix}${selNode.data.orgID}`],
                    selMatch = getMatchContent(stdNode),
                    isMatched = false;
                for( let billsNode of billsNodes) {
                    let billsMatch = getMatchContent(billsNode);
                    if (selMatch === billsMatch) {//只进行一次成功匹配
                        matchNode = billsNode;
                        isMatched = true;
                        if (selNode.children.length === 0) {//成功匹配且为选中的最底节点，则为自动定位节点
                            locateNode = matchNode;
                        }
                        break;
                    }
                }
                if (isMatched && canRecursive(matchNode)) {//匹配成功，且该匹配到的节点可插入子项，递归匹配子项
                    comparePeer(matchNode, matchNode.children, selNode.children);
                } else if (!isMatched) { //匹配不成功，其节点与该层清单节点同层，根据编码插入同层数组中
                    let pos = insertPos(peerNodes, selNode);
                    if (pos >= 0) {
                        peerNodes.splice(pos, 0, selNode);
                    }
                }
            }
            //同层节点比清单树节点多了，说明在该层有选中节点插入清单树中
            if (peerNodes.length > billsNodes.length) {
                let updateData = getUpdateDataFromPeer(parentNode, peerNodes, billsNodes, selNodes);
                postData = postData.concat(updateData);
            }
        }
    }




    //项目指引类型
    const itemType = {
        job: 0,
        ration: 1
    };
    const guideItem = {
        dom: $('#billsGuidance_items'),
        workBook: null,
        tree: null,
        controller: null,
        treeSetting: {
            treeCol: 1,
            emptyRows: 0,
            headRows: 1,
            headRowHeight: [40],
            defaultRowHeight: 21,
            cols: [
                {
                    width: 35,
                    readOnly: false,
                    head: {
                        titleNames: ["选择"],
                        spanCols: [1],
                        spanRows: [1],
                        vAlign: [1],
                        hAlign: [1],
                        font: ["Arial"]
                    },
                    data: {
                        field: "select",
                        vAlign: 1,
                        hAlign: 1,
                        font: "Arial"
                    }
                },
                {
                width: 420,
                readOnly: false,
                head: {
                    titleNames: ["项目指引"],
                    spanCols: [1],
                    spanRows: [1],
                    vAlign: [1],
                    hAlign: [1],
                    font: ["Arial"]
                },
                data: {
                    field: "name",
                    vAlign: 1,
                    hAlign: 0,
                    font: "Arial"
                }
            }
            ]
        },
        headers: [
            {name: '选择', dataCode: 'select', width: 35, vAlign: 'center', hAlign: 'center', formatter: '@'},
            {name: '项目指引', dataCode: 'name', width: 300, vAlign: 'center', hAlign: 'left', formatter: '@'},
        ],
        rowHeaderWidth:25,
        events: {
            EditStarting: function (sender, args) {
                if(!bills.tree || guideItem.headers[args.col]['dataCode'] === 'name'){
                    args.cancel = true;
                }
            },
            ButtonClicked: function (sender, args) {
                if(args.sheet.isEditing()){
                    args.sheet.endEdit(true);
                }
                refreshInsertRation();
            },
            CellDoubleClick: function (sender, args) {
                if(!bills.tree || !bills.tree.selected){
                    return;
                }
                let node = bills.tree.selected.guidance.tree.selected;
                if(!node){
                    return;
                }
                if(node.children.length === 0){
                    if(guideItem.headers[args.col]['dataCode'] === 'name'){
                        insertRations(getInsertRationData([args.row]));
                    }
                }
                else {
                    node.setExpanded(!node.expanded);
                    renderSheetFunc(args.sheet, function () {
                        let iCount = node.posterityCount(), i, child;
                        for (i = 0; i < iCount; i++) {
                            child = bills.tree.selected.guidance.tree.items[args.row + i + 1];
                            args.sheet.setRowVisible(args.row + i + 1, child.visible, args.sheetArea);
                        }
                        args.sheet.invalidateLayout();
                    });
                    args.sheet.repaint();
                }
            }
        }
    };
    const elfItem = {
        dom: $('#billsGuidance_items'),
        workBook: null,
        tree: null,
        controller: null,
        treeSetting: {
            treeCol: 0,
            emptyRows: 0,
            headRows: 1,
            headRowHeight: [40],
            defaultRowHeight: 21,
            cols: [
                {
                    width: 250,
                    readOnly: true,
                    head: {
                        titleNames: ["施工工序"],
                        spanCols: [1],
                        spanRows: [1],
                        vAlign: [1],
                        hAlign: [1],
                        font: ["Arial"]
                    },
                    data: {
                        field: "name",
                        vAlign: 1,
                        hAlign: 0,
                        font: "Arial"
                    }
                },
                {
                    width: 250,
                    readOnly: false,
                    head: {
                        titleNames: ["选项"],
                        spanCols: [1],
                        spanRows: [1],
                        vAlign: [1],
                        hAlign: [1],
                        font: ["Arial"]
                    },
                    data: {
                        field: "options",
                        vAlign: 1,
                        hAlign: 0,
                        font: "Arial"
                    }
                }
            ]
        },
        headers: [
            {name: '施工工序', dataCode: 'name', width: 250, rateWidth: 0.5, vAlign: 'center', hAlign: 'center', formatter: '@'},
            {name: '选项', dataCode: 'options', width: 250, rateWidth: 0.5,  vAlign: 'center', hAlign: 'left', formatter: '@'},
        ],
        rowHeaderWidth:25,
        events: {
            CellClick: function (sender, args) {
                if(elfItem.headers[args.col]['dataCode'] === 'options' && args.sheetArea === 3){
                    if(!args.sheet.getCell(args.row, args.col).locked() && !args.sheet.isEditing()){
                        args.sheet.startEdit();
                    }
                }
            },
            ClipboardPasting: function (sender, info) {
                info.cancel = true;
            }
        }
    };
    const options = {
        workBook: {
            tabStripVisible:  false,
            allowContextMenu: false,
            allowCopyPasteExcelStyle : false,
            allowExtendPasteRange: false,
            allowUserDragDrop : false,
            allowUserDragFill: false,
            scrollbarMaxAlign : true
        },
        sheet: {
            protectionOptions: {allowResizeRows: true, allowResizeColumns: true},
            clipBoardOptions: GC.Spread.Sheets.ClipboardPasteOptions.values
        }
    };
    //渲染时方法，停止渲染
    //@param {Object}sheet {Function}func @return {void}
    function renderSheetFunc(sheet, func){
        sheet.suspendEvent();
        sheet.suspendPaint();
        if(func){
            func();
        }
        sheet.resumeEvent();
        sheet.resumePaint();
    }
    //设置表选项
    //@param {Object}workBook {Object}opts @return {void}
    function setOptions (workBook, opts) {
        for(let opt in opts.workBook){
            workBook.options[opt] = opts.workBook[opt];
        }
        for(let opt in opts.sheet){
            workBook.getActiveSheet().options[opt] = opts.sheet[opt];
        }
    }
    //建表头
    //@param {Object}sheet {Array}headers @return {void}
    function buildHeader(sheet, headers) {
        let fuc = function () {
            sheet.setColumnCount(headers.length);
            sheet.setRowHeight(0, 30, GC.Spread.Sheets.SheetArea.colHeader);
            //sheet.setColumnWidth(0, sheet.getParent() === bills.workBook ? 15 : 25, GC.Spread.Sheets.SheetArea.rowHeader);
            if(sheet.getParent() === elfItem.workBook || sheet.getParent() === guideItem.workBook){
                sheet.setRowHeight(0, 20, GC.Spread.Sheets.SheetArea.colHeader);
            }
            for(let i = 0, len = headers.length; i < len; i++){
                sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
                sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
                if(headers[i].formatter){
                    sheet.setFormatter(-1, i, headers[i].formatter);
                }
                sheet.getRange(-1, i, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[headers[i]['hAlign']]);
                sheet.getRange(-1, i, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[headers[i]['vAlign']]);
            }
        };
        renderSheetFunc(sheet, fuc);
    }
    //表监听事件
    //@param {Object}workBook @return {void}
    function bindEvent(workBook, events) {
        if(Object.keys(events).length === 0){
            return;
        }
        const Events = GC.Spread.Sheets.Events;
        for(let event in events){
            workBook.bind(Events[event], events[event]);
        }
    }
    //根据宽度比例设置列宽
    //@param {Object}workBook {Number}workBookWidth {Array}headers @return {void}
    function setColumnWidthByRate(workBook, workBookWidth, headers) {
        if(workBook){
            workBookWidth -= 48;
            const sheet = workBook.getActiveSheet();
            sheet.suspendEvent();
            sheet.suspendPaint();
            for(let col = 0; col < headers.length; col++){
                if(headers[col]['rateWidth'] !== undefined && headers[col]['rateWidth'] !== null && headers[col]['rateWidth'] !== ''){
                    let width = workBookWidth * headers[col]['rateWidth'];
                    if(headers[col]['dataCode'] === 'options'){
                        width = width;
                    }
                    sheet.setColumnWidth(col, width, GC.Spread.Sheets.SheetArea.colHeader)
                }
                else {
                    if(headers[col]['headerWidth'] !== undefined && headers[col]['headerWidth'] !== null && headers[col]['headerWidth'] !== ''){
                        sheet.setColumnWidth(col, headers[col]['headerWidth'], GC.Spread.Sheets.SheetArea.colHeader)
                    }
                }
            }
            sheet.resumeEvent();
            sheet.resumePaint();
        }
    }
    //建表
    //@param {Object}module @return {void}
    function buildSheet(module) {
        if(!module.workBook){
            module.workBook = new GC.Spread.Sheets.Workbook(module.dom[0], {sheetCount: 1});
            sheetCommonObj.spreadDefaultStyle(module.workBook);
            let sheet = module.workBook.getActiveSheet();
            if(module === bills){
                //默认初始可控制焦点在清单表中
                sheet.options.rowHeaderVisible = false;
                module.workBook.focus();
                sheet.options.isProtected = true;
                sheet.name('stdBillsGuidance_bills');
                //设置悬浮提示
                TREE_SHEET_HELPER.initSetting(bills.dom[0], bills.treeSetting);
            }
            if(module === guideItem){
                sheet.options.isProtected = true;
                sheet.getRange(-1, 0, -1, 1).locked(false);
                sheet.getRange(-1, 1, -1, 1).locked(true);
            }
            if(module === elfItem){
                sheet.options.isProtected = true;
                sheet.getRange(-1, 0, -1, 1).locked(true);
                sheet.getRange(-1, 1, -1, 1).locked(false);
            }
            if(module.rowHeaderWidth) {
                sheet.setColumnWidth(0, module.rowHeaderWidth, GC.Spread.Sheets.SheetArea.rowHeader);
            }
            setOptions(module.workBook, options);
            buildHeader(module.workBook.getActiveSheet(), module.headers);
            if(module === elfItem){
                setColumnWidthByRate(elfItem.workBook, $('#zy').width(), elfItem.headers)
            }
            bindEvent(module.workBook, module.events);
        }
    }
    //清空表数据
    //@param {Object}sheet {Array}headers {Number}rowCount @return {void}
    function cleanData(sheet, headers, rowCount){
        renderSheetFunc(sheet, function () {
            sheet.clear(-1, 0, -1, headers.length, GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
            if (rowCount > 0) {
                sheet.setRowCount(rowCount);
            }
        });
    }
    //初始化各工作表
    //@param {Array}modules @return {void}
    function initWorkBooks(modules){
        for(let module of modules){
            buildSheet(module);
        }
    }
    //点击清单名称后面的问号，弹出补注窗口并设置当前节点（或xxx父节点）的补注
    //@param {Number}row(当前焦点行) @return {void}
    function initRechargeModal(row) {
        let node = bills.tree.items[row];
        while (node && !node.data.recharge){
            node = node.parent;
        }
        let recharge = node && node.data.recharge ? node.data.recharge : '无内容';
        node = bills.tree.items[row];
        while (node && !node.data.ruleText){
            node = node.parent;
        }
        let ruleText = node && node.data.ruleText ? node.data.ruleText : '无内容';

        $('#questionTab1').text('补注');
        $('#questionContent1').html(recharge);
        $('#questionContent2').html(ruleText);
        $('#questionModal').modal('show');
    }
    //节点链上含有补注或工程量计算规则数据
    //@param {Number}row(行当前行) @return {Boolean}
    function hasRechargeRuleText(row) {
        let node = bills.tree.items[row];
        if (!node) {
            return false;
        }
        while (node) {
            if (node.data.recharge || node.data.ruleText) {
                return true;
            }
            node = node.parent;
        }
        return false;
    }
    //初始化并输出树
    //@param {Object}module {Object}sheet {Object}treeSetting {Array}datas
    function initTree(module, sheet, treeSetting, datas){
        module.tree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
        module.controller = TREE_SHEET_CONTROLLER.createNew(module.tree, sheet, treeSetting, false);
        module.tree.loadDatas(datas);
        if(module === bills){
            initExpandStat();
        }
        module.controller.showTreeData();
        if(module === bills){
            module.workBook.getSheet(0).options.rowHeaderVisible = true;
            setBillsHint(bills.tree.items, stdBillsJobData, stdBillsFeatureData);
            renderSheetFunc(sheet, function () {
                for(let i = 0; i < bills.tree.items.length; i++){
                    sheet.setCellType(i, 1, TREE_SHEET_HELPER.getQuestionCellType(initRechargeModal, hasRechargeRuleText));
        }
            });
        }
    }
    //项目指引表焦点控制
    //@param {Number}row @return {void}
    function guideItemInitSel(row){
        let billsNode = bills.tree.selected;
        let node = null;
        if(billsNode && billsNode.guidance.tree){
            node = billsNode.guidance.tree.items[row];
            if(node){
                billsNode.guidance.tree.selected = node;
            }
        }
    }
    //清单精灵表焦点控制
    //@param {Number}row @return {void}
    function elfItemInitSel(row){
        let billsNode = bills.tree.selected;
        let node = null;
        if(billsNode && billsNode.elf.tree){
            node = billsNode.elf.tree.items[row];
            if(node){
                billsNode.elf.tree.selected = node;
            }
        }
    }
    //根据项目指引的类型设置单元格类型，定额类型的项目指引为复选框
    //@param {Array}nodes @return {void}
    function setItemCellType(nodes){
        //设置单元格类型
        const base = new GC.Spread.Sheets.CellTypes.Base();
        const checkBox = new GC.Spread.Sheets.CellTypes.CheckBox();
        const sheet = guideItem.workBook.getActiveSheet();
        renderSheetFunc(sheet, function(){
            for(let node of nodes){
                sheet.setCellType(node.serialNo(), 0, node.data.type === itemType.ration ?  checkBox : base);
            }
        });
    }
    //初始化清单的工作内容和项目特征
    //@param {Number}billsLibId {Function}callback @return {void}
    function initJobAndCharacter(billsLibId, callback){
        CommonAjax.post('/stdBillsEditor/getJobContent', {userId: userID, billsLibId: billsLibId}, function (datas) {
            stdBillsJobData = datas;
            CommonAjax.post('/stdBillsEditor/getItemCharacter', {userId: userID, billsLibId: billsLibId}, function (datas) {
                stdBillsFeatureData = datas;
                if(callback){
                    callback();
                }
            });
        });
    }
    //初始化清单展开收起状态
    //@return {void}
    function initExpandStat(){
        //读取展开收起状态
        let currentExpState = sessionStorage.getItem('stdBillsGuidanceExpState');
        if(currentExpState){
            bills.tree.setExpandedByState(bills.tree.items, currentExpState);
        }
        //非叶子节点默认收起
        else{
            bills.tree.setRootExpanded(bills.tree.roots, false);
        }
    }
    //设置tag以悬浮提示
    function setTagForHint(nodes){
        let sheet = bills.workBook.getActiveSheet();
        renderSheetFunc(sheet, function () {
            for(let node of nodes){
                sheet.setTag(node.serialNo(), 2, node.data.ruleText ? node.data.ruleText : '');
            }
        });
    }
    //根据编码定位至清单精灵库中
    //@param {String}code @return {void}
    function locateAtBills(code) {
        let nineCode = code.substring(0, 9);
        let items = bills.tree.items;
        let locateBills = _.find(items, function(item){
            return item.data.code === nineCode;
        });
        if(locateBills){
            expandSearchNodes([locateBills]);
            sessionStorage.setItem('stdBillsGuidanceExpState', bills.tree.getExpState(bills.tree.items));
        }
        let sheet = bills.workBook.getActiveSheet();
        let locateRow = locateBills ? locateBills.serialNo() : 0;
        sheet.setActiveCell(locateRow, 0);
        sheet.showRow(locateRow, GC.Spread.Sheets.VerticalPosition.center);
    }
    //清单设置悬浮提示信息
    //@param {Array}billsNodes(清单节点) {Array}jobs(总的工作内容数据) {Array}items(总的项目特征数据)
    function setBillsHint(billsNodes, jobs, items) {
        let jobsMapping = {},
            itemsMapping = {};
        for(let job of jobs){
            jobsMapping[job.id] = job;
        }
        for(let item of items){
            itemsMapping[item.id] = item;
        }
        let tagInfo = [];
        for(let billsNode of billsNodes){
            let hintArr = [];
            let billsItems = billsNode.data.items;
            if(billsItems.length > 0){
                //项目特征
                hintArr.push('项目特征:');
            }
            let itemCount = 1,
                jobCount = 1;
            for(let billsItem of billsItems){
                let itemData = itemsMapping[billsItem.id];
                if(itemData){
                    //特征值
                    let eigens = [];
                    for(let eigen of itemData.itemValue){
                        eigens.push(eigen.value);
                    }
                    eigens = eigens.join(';');
                    hintArr.push(`${itemCount}.${itemData.content}${eigens === '' ? '' : ': ' + eigens}`);
                    itemCount ++;
                }
            }
            //工作内容
            let billsJobs = billsNode.data.jobs;
            if(billsJobs.length > 0){
                hintArr.push('工作内容:');
            }
            for(let billsJob of billsJobs){
                let jobData = jobsMapping[billsJob.id];
                if(jobData){
                    hintArr.push(`${jobCount}.${jobData.content}`);
                    jobCount ++;
                }
            }
            /*if(billsNode.data.ruleText && billsNode.data.ruleText !== ''){
                hintArr.push('工程量计算规则:');
                hintArr.push(billsNode.data.ruleText);
            }
            if(billsNode.data.recharge && billsNode.data.recharge !== ''){
                hintArr.push('补注:');
                hintArr.push(billsNode.data.recharge);
            }*/
            if(hintArr.length > 0){
                tagInfo.push({row: billsNode.serialNo(), value: hintArr.join('\n')});
            }
        }
        let sheet = bills.workBook.getActiveSheet();
        renderSheetFunc(sheet, function () {
            for(let tagI of tagInfo){
                sheet.setTag(tagI.row, 0, tagI.value);
            }
        });
    }
    //初始选择标准清单
    //@param {Number}libID @return {void}
    function libInitSel(libID){
        //获取清单
        $.bootstrapLoading.start();
        CommonAjax.post('/billsGuidance/api/getLibWithBills', {libID: libID, isGuidanceLib: false}, function(rstData){
            if(guideItem.workBook){
                guideItem.workBook.destroy();
                guideItem.workBook = null;
            }
            if(elfItem.workBook){
                elfItem.workBook.destroy();
                elfItem.workBook = null;
            }
            initViews();
            let callback = function () {
                initTree(bills, bills.workBook.getActiveSheet(), bills.treeSetting, rstData.bills);
                if(doAfterLoadGuidance){
                    doAfterLoadGuidance();
                }
                $.bootstrapLoading.end();
            };
            //获取清单库中的工作内容和项目特征
            initJobAndCharacter(libID, callback);
        }, function () {
            $.bootstrapLoading.end();
        });
    }
    //初始化清单指引库
    //@param {Array}libDats @return {void}
    function initLibs(libDatas){
        libSel.empty();
        if(!libDatas){
            return;
        }
        let selectedLib = sessionStorage.getItem('stdBillsGuidance');
        for(let libData of libDatas){
            let opt = $('<option>').val(libData.id).text(libData.name);
            if(selectedLib && libData.id == selectedLib){
                opt.attr('selected', 'selected');
            }
            libSel.append(opt);
        }
        //初始默认选择
        libInitSel(libSel.select().val());
    }
    //初始化视图
    //@param {void} @return {void}
    function initViews(){
        //赋初始高度
        if($('#billsGuidance_bills').height() === 0 || $('#billsGuidance_items').height() === 0){
            let height = $(window).height()-$(".header").height()-$(".toolsbar").height()-$(".tools-bar-height-z").height();
            $('#billsGuidance_bills').height(height / 2);
            $('#billsGuidance_items').height(height / 2);
        }
        let modules = [bills];
        modules.push(elfItem);
        initWorkBooks(modules);

    }
    //展开至搜索出来点的节点
    //@param {Array}nodes @return {void}
    function expandSearchNodes(nodes){
        let that = this;
        let billsSheet = bills.workBook.getActiveSheet();
        renderSheetFunc(billsSheet, function () {
            function expParentNode(node){
                if(node.parent){
                    if (!node.parent.expanded) {
                        node.parent.setExpanded(true);
                    }
                    expParentNode(node.parent);
                }
            }
            for(let node of nodes){
                expParentNode(node);
            }
            TREE_SHEET_HELPER.refreshTreeNodeData(bills.treeSetting, billsSheet, bills.tree.roots, true);
            TREE_SHEET_HELPER.refreshNodesVisible(bills.tree.roots, billsSheet, true);
        });
    }
    //各按钮监听事件
    //@return {void}
    function bindBtn(){
        //打开清单指引库
        $('#stdBillsGuidanceTab').click(function () {
            if(libSel.children().length === 0 && !$(this).hasClass('disabled')){
                initLibs(projectObj.project.projectInfo.engineeringInfo.bill_lib);
            }
        });
        //更改清单指引库
        $('#stdBillsGuidanceLibSelect').change(function () {
            //关闭搜索窗口
            $('#billsGuidanceSearchResult').hide();
            billsLibObj.clearHighLight(bills.workBook);
            libInitSel($(this).select().val());
            //记住选项
            sessionStorage.setItem('stdBillsGuidance', $(this).select().val());
            //清除展开收起状态sessionStorage
            sessionStorage.removeItem('stdBillsGuidanceExpState');
        });
        //搜索
        $('#stdBillsGuidanceSearch>div>button').click(function () {
            if(!bills.tree){
                return;
            }
            let billsSheet = bills.workBook.getActiveSheet();
            billsLibObj.clearHighLight(bills.workBook);
            let keyword = $('#stdBillsGuidanceSearch>input').val();
            if (!keyword || keyword === '') {
                $('#billsGuidanceSearchResult').hide();
                return;
            }

            let result = bills.tree.items.filter(function (item) {
                let codeIs = item.data.code ? item.data.code.indexOf(keyword) !== -1 : false;
                let nameIs = item.data.name ? item.data.name.indexOf(keyword) !== -1 : false;
                return codeIs || nameIs;
            });
            result.sort(function (x, y) {
                return x.serialNo() - y.serialNo();
            });
            if (result.length !== 0) {
                //展开搜索出来的节点
                expandSearchNodes(result);
                //设置记住展开
                sessionStorage.setItem('stdBillsGuidanceExpState', bills.tree.getExpState(bills.tree.items));

                let sel = billsSheet.getSelections();
                renderSheetFunc(billsSheet, function () {
                    bills.controller.setTreeSelected(result[0]);
                    billsSheet.setSelection(result[0].serialNo(), sel[0].col, 1, 1);
                    for (let node of result) {
                        billsSheet.getRange(node.serialNo(), -1, 1, -1).backColor('lemonChiffon');
                    }
                });
                //搜索初始定位
                billsSheet.showRow(result[0].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
                //查找下一条
                $('#nextBillsGuidance').show();
                $('#nextBillsGuidance').unbind('click');
                $('#nextBillsGuidance').bind('click', function () {
                    let cur = bills.tree.selected, resultIndex = result.indexOf(cur), sel = billsSheet.getSelections();
                    if (resultIndex === result.length - 1) {
                        bills.controller.setTreeSelected(result[0]);
                        billsSheet.setSelection(result[0].serialNo(), sel[0].col, 1, 1);
                        billsSheet.showRow(result[0].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
                    } else {
                        bills.controller.setTreeSelected(result[resultIndex + 1]);
                        billsSheet.setSelection(result[resultIndex + 1].serialNo(), sel[0].col, 1, 1);
                        billsSheet.showRow(result[resultIndex + 1].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
                    }
                });
                //查找上一条
                $('#preBillsGuidance').show();
                $('#preBillsGuidance').unbind('click');
                $('#preBillsGuidance').bind('click', function () {
                    let cur = bills.tree.selected, resultIndex = result.indexOf(cur), sel = billsSheet.getSelections();
                    if (resultIndex === 0) {
                        bills.controller.setTreeSelected(result[result.length - 1]);
                        billsSheet.setSelection(result[result.length - 1].serialNo(), sel[0].col, 1, 1);
                        billsSheet.showRow(result[result.length - 1].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
                    } else {
                        bills.controller.setTreeSelected(result[resultIndex - 1]);
                        billsSheet.setSelection(result[resultIndex - 1].serialNo(), sel[0].col, 1, 1);
                        billsSheet.showRow(result[resultIndex - 1].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
                    }
                });
            } else {
                billsLibObj.clearHighLight(bills.workBook);
                $('#nextBillsGuidance').hide();
                $('#preBillsGuidance').hide();
            }
            $('#billsGuidanceSearchResultCount').text('搜索结果：' + result.length);
            $('#billsGuidanceSearchResult').show();
            autoFlashHeight();
            refreshWorkBook();
        });
        //搜索框回车
        $('#stdBillsGuidanceSearch>input').bind('keypress', function (event) {
            if(event.keyCode === 13){
                $(this).blur();
                $('#stdBillsGuidanceSearch>div>button').click();
            }
        });
        // 关闭搜索结果
        $('#closeSearchBillsGuidance').click(function () {
            $('#billsGuidanceSearchResult').hide();
            billsLibObj.clearHighLight(bills.workBook);
            autoFlashHeight();
            refreshWorkBook();
        });
    }
    //刷新表
    //@return {void}
    function refreshWorkBook(){
        if(bills.workBook){
            bills.workBook.refresh();
        }
        if(guideItem.workBook){
            guideItem.workBook.refresh();
        }
        if(elfItem.workBook){
            elfItem.workBook.refresh();
        }
    }

    return {initViews, bindBtn, refreshWorkBook, setColumnWidthByRate, locateAtBills, bills, elfItem, overwrite};
})();

$(document).ready(function(){
    billsGuidance.bindBtn();
});