'use strict';

/**
 *
 *
 * @author Mai
 * @date
 * @version
 */
const _ = require('lodash');

class ImportBaseTree {
    /**
     * 构造函数
     * @param {Array} tempData - 清单模板数据
     */
    constructor (tempData) {
        // 常量
        this.splitChar = '-';
        // 索引
        // 以code为索引
        this.codeNodes = {};
        this.items = [];
        this.roots = [];
        this.pos = [];
        this.tempData = [];

        // 缓存
        this.finalNode = null;
        this.finalXmjNode = null;
        this.keyNodeId = 1;

        this._loadTemplateTree(tempData);
    }

    /**
     * 加载 清单模板
     * @param {Array} data - 模板数据
     * @private
     */
    _loadTemplateTree(data) {
        for (const node of data) {
            node.ledger_id = node.id;
            node.ledger_pid = node.pid;
            delete node.id;
            delete node.pid;
            if (node.code) {
                this.codeNodes[node.code] = node;
            }
            this.items.push(node);
            if (node.ledger_pid === -1) {
                this.roots.push(node);
            }
            if (node.ledger_id >= this.keyNodeId) {
                this.keyNodeId = node.ledger_id + 1;
            }
            this.tempData.push(node);
        }
        for (const node of this.items) {
            node.children = this.items.filter(function (i) {
                return i.ledger_pid === node.ledger_id;
            });
        }
    }

    /**
     * 根据 编号、名称 查找模板节点
     * @param {Object} node - 要查找的节点
     * @returns {*}
     */
    findTempData(node) {
        return this.tempData.find(function (td) {
            return td.code === node.code && td.name === node.name;
        });
    }

    /**
     * 根据 编号 查找 父项项目节
     * @param {String} code - 子项编号
     * @returns {*}
     */
    findXmjParent(code) {
        const codePath = code.split(this.splitChar);
        if (codePath.length > 1) {
            codePath.splice(codePath.length - 1, 1);
            return this.codeNodes[codePath.join(this.splitChar)];
        }
    }

    /**
     * 添加 树节点 并完善该节点的树结构
     * @param {Object} node - 添加节点
     * @param {Object} parent - 父项
     * @returns {*}
     */
    addNodeWithParent(node, parent) {
        node.ledger_id = this.keyNodeId;
        this.keyNodeId += 1;
        node.ledger_pid = parent ? parent.ledger_id : -1;
        node.level = parent ? parent.level + 1 : 1;
        node.order = parent ? parent.children.length + 1 : this.roots.length + 1;
        node.full_path = parent ? parent.full_path + '.' + node.ledger_id : '' + node.ledger_id;
        if (parent) {
            parent.children.push(node);
        } else {
            this.roots.push(node);
        }
        this.items.push(node);
        this.codeNodes[node.code] = node;
        this.defineCacheData(node);
        return node;
    }

    /**
     * 定义缓存节点(添加工程量清单、部位明细需使用缓存定位)
     * @param {Object} node - 当前添加的节点
     */
    defineCacheData(node) {
        this.finalNode = node;
        if (node.code) {
            this.finalXmjNode = node;
        }
    }

    /**
     * 添加 项目节
     * @param {Object} node - 项目节
     * @returns {*}
     */
    addXmjNode(node) {
        node.children = [];
        if (node.code.split(this.splitChar).length > 1) {
            const temp = this.findTempData(node);
            if (temp) {
                this.defineCacheData(temp);
                return temp;
            } else {
                const parent = this.findXmjParent(node.code);
                return this.addNodeWithParent(node, parent);
            }
        } else {
            const n = this.codeNodes[node.code];
            if (!n) {
                return this.addNodeWithParent(node, null);
            } else {
                this.defineCacheData(n);
                return n;
            }
        }
    }

    /**
     * 添加 工程量清单
     * @param {Object} node - 工程量清单
     */
    addGclNode(node) {
        node.pos = [];
        if (this.finalXmjNode) {
            return this.addNodeWithParent(node, this.finalXmjNode);
        }
    }

    /**
     * 添加 部位明细
     * @param {object} pos - 部位明细
     * @returns {*}
     */
    addPos (pos){
        if (this.finalNode && this.finalNode.pos) {
            this.finalNode.pos.push(pos);
            this.pos.push(pos);
            return pos;
        }
    }

    /**
     * 第一部分的子节点，顺序重排
     */
    resortFirstPartChildren () {
        const splitChar = this.splitChar;
        const firstPart = this.roots[0];
        firstPart.children.sort(function (a, b) {
            if (a.code === '') {
                return 1;
            } else if (b.code === '') {
                return -1;
            }
            const codeA = a.code.split(splitChar);
            const numA = _.toNumber(codeA[codeA.length -1]);
            const codeB = b.code.split(splitChar);
            const numB = _.toNumber(codeB[codeB.length -1]);
            return numA - numB;
        });
    }

    calculateLeafWithPos () {
        for (const node of this.items) {
            if (node.children && node.children.length > 0) { continue; }
            if (!node.pos || node.pos.length === 0) { continue; }
            node.quantity = _.round(_.sum(_.map(node.pos, 'quantity')), 6);
            if (node.quantity && node.unit_price) {
                node.total_price = _.round(node.quantity * node.unit_price, 6);
            } else {
                node.total_price = null;
            }
        }
    }
}

class AnalysisExcelTree {
    /**
     * 构造函数
     */
    constructor() {
        this.colsDef = null;
        this.colHeaderMatch = {
            code: ['项目节编号', '预算项目节'],
            b_code: ['清单子目号', '清单编号', '子目号'],
            pos: ['部位明细'],
            name: ['名称'],
            unit: ['单位'],
            quantity: ['清单数量'], // 施工图复核数量
            dgn_qty1: ['设计数量1'],
            dgn_qty2: ['设计数量2'],
            unit_price: ['单价'],
            drawing_code: ['图号'],
            memo: ['备注'],
        };
    }

    toNumber (value) {
        let num = _.toNumber(value);
        return _.isNaN(num) ? null : num;
    }

    /**
     * 读取项目节节点
     * @param {Array} row - excel行数据
     * @returns {*}
     * @private
     */
    _loadXmjNode(row) {
        const node = {};
        node.code = row[this.colsDef.code];
        node.name = row[this.colsDef.name];
        node.unit = row[this.colsDef.unit];
        node.quantity = this.toNumber(row[this.colsDef.quantity]);
        node.dgn_qty1 = this.toNumber(row[this.colsDef.dgn_qty1]);
        node.dgn_qty2 = this.toNumber(row[this.colsDef.dgn_qty2]);
        node.unit_price = this.toNumber(row[this.colsDef.unit_price]);
        node.drawing_code = row[this.colsDef.drawing_code];
        node.memo = row[this.colsDef.memo];
        if (node.quantity && node.unit_price) {
            node.total_price = _.round(node.quantity * node.unit_price, 6);
        } else {
            node.total_price = null;
        }
        return this.cacheTree.addXmjNode(node);
    }
    /**
     * 读取工程量清单数据
     * @param {Array} row - excel行数据
     * @returns {*}
     * @private
     */
    _loadGclNode(row) {
        const node = {};
        node.b_code = row[this.colsDef.b_code];
        node.name = row[this.colsDef.name];
        node.unit = row[this.colsDef.unit];
        node.quantity = this.toNumber(row[this.colsDef.quantity]);
        node.unit_price = this.toNumber(row[this.colsDef.unit_price]);
        node.drawing_code = row[this.colsDef.drawing_code];
        node.memo = row[this.colsDef.memo];
        if (node.quantity && node.unit_price) {
            node.total_price = _.round(node.quantity * node.unit_price, 6);
        } else {
            node.total_price = null;
        }
        return this.cacheTree.addGclNode(node);
    }
    /**
     * 读取部位明细数据
     * @param {Array} row - excel行数据
     * @returns {*}
     * @private
     */
    _loadPos(row) {
        const pos = {};
        pos.name = row[this.colsDef.name];
        pos.quantity = this.toNumber(row[this.colsDef.quantity]);
        pos.drawing_code = row[this.colsDef.drawing_code];
        return this.cacheTree.addPos(pos);
    }

    /**
     * 读取数据行
     * @param {Array} row - excel数据行
     * @param {Number} index - 行索引号
     */
    loadRowData(row, index) {
        let result;
        // 含code识别为项目节，含posCode识别为部位明细，其他识别为工程量清单
        if (row[this.colsDef.code]) {
            // 第三部分（编号为'3'）及之后的数据不读取
            if (row[this.colsDef.code] === '3') {
                this.loadEnd = true;
                return;
            }
            result = this._loadXmjNode(row)
        } else if (row[this.colsDef.pos]) {
            result = this._loadPos(row);
        } else {
            result = this._loadGclNode(row);
        }
        // 读取失败则写入错误数据 todo 返回前端告知用户?
        if (!result) {
            this.errorData.push({
                serialNo: index,
                data: row,
            });
        }
    }

    /**
     * 读取表头并检查
     * @param {Number} row - Excel数据行
     */
    checkColHeader(row) {
        const colsDef = {};
        for (const iCol in row) {
            const text = row[iCol];
            for (const head in this.colHeaderMatch) {
                const match = this.colHeaderMatch[head];
                if (match.indexOf(text) >= 0) {
                    colsDef[head] = iCol;
                }
            }
        }
        if (colsDef.code && colsDef.b_code && colsDef.pos) {
            this.colsDef = colsDef;
        }
    }

    /**
     * 将excel清单 平面数据 解析为 树结构数据
     * @param {object} sheet - excel清单数据
     * @param {Array} tempData - 新建项目使用的清单模板
     * @returns {ImportBaseTree}
     */
    analysisData(sheet, tempData) {
        this.colsDef = null;
        this.cacheTree = new ImportBaseTree(tempData);
        this.errorData = [];
        this.loadEnd = false;

        for (const iRow in sheet.rows) {
            const row = sheet.rows[iRow];
            if (this.colsDef && !this.loadEnd) {
                this.loadRowData(row, iRow);
            } else {
                this.checkColHeader(row);
            }
        }
        this.cacheTree.resortFirstPartChildren();
        this.cacheTree.calculateLeafWithPos();
        return this.cacheTree;
    }
}

module.exports = AnalysisExcelTree;
