/**
 * 构建pathTree
 * 可动态加载子节点，要求子节点获取接口按/xxx/get-children定义
 * @param {Object} setting - 设置
 * @returns {PathTree}
 */

'use strict';

const itemsPre = 'id_';

const createNewPathTree = function (type, setting) {
    class BaseTree {
        /**
         * 构造函数
         */
        constructor (setting) {
            // 无索引
            this.datas = [];
            // 以key为索引
            this.items = {};
            // 以排序为索引
            this.nodes = [];
            // 索引
            this.children = {};
            // 树设置
            this.setting = JSON.parse(JSON.stringify(setting));
        }
        /**
         * 树结构根据显示排序
         */
        sortTreeNode (isResort) {
            const self = this;
            const addSortNodes = function (nodes) {
                if (!nodes) { return }
                for (let i = 0; i < nodes.length; i++) {
                    self.nodes.push(nodes[i]);
                    nodes[i].index = self.nodes.length - 1;
                    if (!isResort) {
                        nodes[i].children = self.getChildren(nodes[i]);
                    }
                    addSortNodes(nodes[i].children);
                }
            };
            self.nodes = [];
            addSortNodes(this.getChildren(null));
        }
        /**
         * 加载数据(初始化), 并给数据添加部分树结构必须数据
         * @param datas
         */
        loadDatas (datas) {
            // 清空旧数据
            this.items = {};
            this.nodes = [];
            // 加载全部数据
            for (const data of datas) {
                const keyName = itemsPre + data[this.setting.id];
                this.items[keyName] = JSON.parse(JSON.stringify(data));
                this.datas.push(this.items[keyName]);
            }
            this.sortTreeNode();
            for (const node of this.nodes) {
                node.expanded = node.children.length > 0;
                node.visible = true;
            }
        }

        /**
         * 根据id获取树结构节点数据
         * @param {Number} id
         * @returns {Object}
         */
        getItems (id) {
            return this.items[itemsPre + id];
        };
        /**
         * 查找node的parent
         * @param {Object} node
         * @returns {Object}
         */
        getParent (node) {
            return this.getItems(node[this.setting.pid]);
        };

        /**
         * 根据path查找完整节点
         * @param {Number} path
         */
        getFullPathNodes (path) {
            const self = this, ids = path.split('.');
            if (ids.length > 0) {
                return this.nodes.filter((x) => {
                    return ids.indexOf('' + x[self.setting.id]) >= 0;
                });
            } else {
                return [];
            }
        };
        /**
         * 查询node的已下载子节点
         * @param {Object} node
         * @returns {Array}
         */
        getChildren (node) {
            const setting = this.setting;
            const pid = node ? node[setting.id] : setting.rootId;
            const children = this.datas.filter(function (x) {
                return x[setting.pid] === pid;
            });
            children.sort(function (a, b) {
                return a.order - b.order;
            });
            return children;
        };
        /**
         * 查询node的已下载的全部后代
         * @param {Object} node
         * @returns {Array}
         */
        getPosterity (node) {
            const reg = new RegExp('^' + node.full_path + '.');
            return this.datas.filter(function (x) {
                return reg.test(x.full_path);
            })
        };
        /**
         * 查询node是否是父节点的最后一个子节点
         * @param {Object} node
         * @returns {boolean}
         */
        isLastSibling (node) {
            const siblings = this.getChildren(this.getParent(node));
            return node.order === siblings[siblings.length - 1].order;
        };
        /**
         * 刷新子节点是否可见
         * @param {Object} node
         * @private
         */
        _refreshChildrenVisible (node) {
            if (!node.children) {
                node.children = this.getChildren(node);
            }
            if (node.children && node.children.length > 0) {
                for (const child of node.children) {
                    child.visible = node.expanded && node.visible;
                    this._refreshChildrenVisible(child);
                }
            }
        };
        /**
         * 设置节点是否展开, 并控制子节点可见
         * @param {Object} node
         * @param {Boolean} expanded
         */
        setExpanded (node, expanded) {
            node.expanded = expanded;
            this._refreshChildrenVisible(node);
        };

        /**
         * 提取节点key和索引数据
         * @param {Object} node - 节点
         * @returns {key}
         */
        getNodeKeyData (node) {
            const data = {};
            for (const key of this.setting.keys) {
                data[key] = node[key];
            }
            return data;
        };
        /**
         * 得到树结构构成id
         * @param node
         * @returns {*}
         */
        getNodeKey (node) {
            return node[this.setting.id];
        };
    }

    class MeasureTree extends BaseTree {
        addData (datas) {
            const loadedData = [];
            for (const data of datas) {
                let node = this.getItems(data[this.setting.id]);
                if (node) {
                    for (const prop in node) {
                        if (data[prop] !== undefined) {
                            node[prop] = data[prop];
                        }
                    }
                    loadedData.push(node);
                } else {
                    const keyName = itemsPre + data[this.setting.id];
                    const node = JSON.parse(JSON.stringify(data));
                    this.items[keyName] = node;
                    this.datas.push(node);
                    node.expanded = false;
                    node.visible = true;
                    loadedData.push(node);
                }
            }
            this.sortTreeNode();
            for (const node of loadedData) {
                const children = node.children;
                if (!node.expanded && children.length > 0) {
                    node.expanded = true;
                    this._refreshChildrenVisible(node);
                }
            }
            return loadedData;
        }
        removeData (datas) {
            datas.sort(function (a, b) {
                return b.level - a.level;
            });
            console.log(datas);
            const removeArrayData = function (array, data) {
                const index = array.indexOf(data);
                array.splice(index, 1);
            };
            for (const data of datas) {
                const node = this.getItems(data[this.setting.id]);
                if (node && this.getChildren(node).length === 0) {
                    delete this.items[itemsPre + node[this.setting.id]];
                    if (node[this.setting.pid] !== this.setting.rootId) {
                        const parent = this.items[itemsPre + node[this.setting.pid]];
                        removeArrayData(parent.children, node);
                    }
                    removeArrayData(this.datas, node);
                    removeArrayData(this.nodes, node);
                }
            }
        };
        loadLeafData (data) {
            const datas = data instanceof Array ? data : [data];
            for (const d of datas) {
                let node = this.getItems(d[this.setting.id]);
                if (node && node.is_leaf) {
                    for (const prop in node) {
                        if (data[prop] !== undefined) {
                            node[prop] = d[prop];
                        }
                    }
                }
            }
        };
    }

    class ActiveTree extends BaseTree {

        /**
         * 加载数据(动态)，只加载不同部分
         * @param {Array} datas
         * @return {Array} 加载到树的数据
         * @privateA
         */
        _loadData (datas) {
            const loadedData = [];
            for (const data of datas) {
                let node = this.getItems(data[this.setting.id]);
                if (node) {
                    for (const prop in node) {
                        if (data[prop] !== undefined) {
                            node[prop] = data[prop];
                        }
                    }
                    loadedData.push(node);
                } else {
                    const keyName = itemsPre + data[this.setting.id];
                    const node = JSON.parse(JSON.stringify(data));
                    this.items[keyName] = node;
                    this.datas.push(node);
                    node.expanded = false;
                    node.visible = true;
                    loadedData.push(node);
                }
            }
            this.sortTreeNode();
            for (const node of loadedData) {
                const children = node.children;
                if (!node.expanded && children.length > 0) {
                    node.expanded = true;
                    this._refreshChildrenVisible(node);
                }
            }
            return loadedData;
        };

        /**
         * 以下方法需等待响应, 通过callback刷新界面
         */
        /**
         * 加载子节点
         * @param {Object} node
         * @param {function} callback
         */
        loadChildren (node, callback) {
            const self = this;
            const url = this.setting.preUrl ? this.setting.preUrl + '/get-children' : 'get-children';
            console.log(url);
            postData(url, this.getNodeKeyData(node), function (data) {
                self._loadData(data);
                callback();
            });
        };
    }

    class LedgerTree extends BaseTree {

        /**
         * 加载数据(动态)，只加载不同部分
         * @param {Array} datas
         * @return {Array} 加载到树的数据
         * @privateA
         */
        _updateData (datas) {
            const loadedData = [];
            for (const data of datas) {
                let node = this.getItems(data[this.setting.id]);
                if (node) {
                    for (const prop in node) {
                        if (data[prop] !== undefined) {
                            node[prop] = data[prop];
                        }
                    }
                    loadedData.push(node);
                }
            }
            for (const node of loadedData) {
                const children = this.getChildren(node);
                node.expanded = children.length > 0 && children[0].visible;
            }
            this.sortTreeNode(true);
            return loadedData;
        };
        /**
         * 加载数据(动态)，只加载不同部分
         * @param {Array} datas
         * @return {Array} 加载到树的数据
         * @privateA
         */
        _loadData (datas) {
            const loadedData = [], resortData = [];
            for (const data of datas) {
                let node = this.getItems(data[this.setting.id]);
                if (node) {
                    const parent = this.getItems(node[this.setting.pid]);
                    for (const prop in node) {
                        if (data[prop] !== undefined && data[prop] !== node[prop]) {
                            node[prop] = data[prop];
                            if (parent && resortData.indexOf(parent) === -1) {
                                resortData.push(parent);
                            }
                        }
                    }
                    loadedData.push(node);
                } else {
                    const keyName = itemsPre + data[this.setting.id];
                    const node = JSON.parse(JSON.stringify(data));
                    this.items[keyName] = node;
                    this.datas.push(node);
                    node.expanded = false;
                    node.visible = true;
                    loadedData.push(node);
                    if (resortData.indexOf(node) === -1) {
                        resortData.push(node);
                    }
                    const parent = this.getItems(node[this.setting.pid]);
                    if (parent && resortData.indexOf(parent) === -1) {
                        resortData.push(parent);
                    }
                }
            }
            for (const node of resortData) {
                node.children = this.getChildren(node);
            }
            this.sortTreeNode(true);
            for (const node of loadedData) {
                if (!node.expanded) {
                    this.setExpanded(node, true);
                }
            }
            return loadedData;
        };
        /**
         * 清理数据(动态)
         * @param datas
         * @private
         */
        _freeData (datas) {
            const removeArrayData = function (array, data) {
                const index = array.indexOf(data);
                array.splice(index, 1);
            };
            for (const data of datas) {
                const node = this.getItems(data[this.setting.id]);
                if (node) {
                    delete this.items[itemsPre + node[this.setting.id]];
                    if (node[this.setting.pid] !== this.setting.rootId) {
                        const parent = this.getItems(node[this.setting.pid]);
                        if (parent) {
                            removeArrayData(parent.children, node);
                        }
                    }
                    removeArrayData(this.datas, node);
                    removeArrayData(this.nodes, node);
                }
            }
        };
        /**
         * 加载需展开的数据
         * @param {Array} datas
         * @returns {Array}
         * @private
         */
        _loadExpandData (datas) {
            const loadedData = [], existData = [], expandData = [], resortData = [];
            for (const data of datas) {
                let node = this.getItems(data[this.setting.id]);
                if (node) {
                    existData.push(node);
                } else {
                    const keyName = itemsPre + data[this.setting.id];
                    const node = JSON.parse(JSON.stringify(data));
                    this.items[keyName] = node;
                    this.datas.push(node);
                    node.expanded = false;
                    node.visible = true;
                    loadedData.push(node);

                    if (resortData.indexOf(node) === -1) {
                        resortData.push(node);
                    }
                    const parent = this.getItems(node[this.setting.pid]);
                    if (parent && resortData.indexOf(parent) === -1) {
                        resortData.push(parent);
                    }
                }
            }
            for (const node of resortData) {
                node.children = this.getChildren(node);
            }
            this.sortTreeNode(true);
            for (const node of loadedData) {
                if (!node.expanded) {
                    this.setExpanded(node, true);
                }
            }
            for (const node of existData) {
                const parent = this.getItems(node[this.setting.pid]);
                if (expandData.indexOf(parent) === -1) {
                    expandData.push(parent);
                    if (!parent.expanded) {
                        this.setExpanded(parent, true);
                    }
                }
                if (!node.expanded) {
                    this.setExpanded(node, true);
                }
            }
            return [loadedData, expandData];
        };

        /**
         * 以下方法需等待响应, 通过callback刷新界面
         */
        /**
         * 加载子节点
         * @param {Object} node
         * @param {function} callback
         */
        loadChildren (node, callback) {
            const self = this;
            const url = this.setting.preUrl ? this.setting.preUrl + '/get-children' : 'get-children';
            console.log(url);
            postData(url, this.getNodeKeyData(node), function (data) {
                self._loadData(data);
                callback();
            });
        };
        /**
         * 树结构基本操作
         * @param {String} url - 请求地址
         * @param {Object} node - 操作节点
         * @param {String} type - 操作类型
         * @param {function} callback - 界面刷新
         */
        baseOperation (url, node, type, callback) {
            const self = this;
            const data = {
                id: node[this.setting.id],
                postType: type
            };
            postData(url, data, function (datas) {
                const result = {};
                if (datas.update) {
                    result.update = self._updateData(datas.update);
                }
                if (datas.create) {
                    result.create = self._loadData(datas.create);
                }
                if (datas.delete) {
                    result.delete = self._freeData(datas.delete);
                }
                callback(result);
            });
        };
        /**
         * 节点数据编辑
         * @param {String} url - 请求地址
         * @param {Array|Object} updateData - 需更新的数据
         * @param {function} callback - 界面刷新
         */
        update (url, updateData, callback) {
            const self = this;
            postData(url, updateData, function (datas) {
                const result = self._updateData(datas);
                callback(result);
            }, function () {
                if (updateData instanceof Array) {
                    const result = [];
                    for (const data of updateData) {
                        result.push(self.getItems(data[self.setting.id]));
                    }
                    callback(result)
                } else {
                    callback([self.getItems(updateData[self.setting.id])]);
                }
            });
        };
        /**
         * 复制粘贴整块（目前仅可粘贴为后项）
         * @param {String} url - 请求地址
         * @param {Object} node - 操作节点
         * @param {Array} block - 被复制整块的节点列表
         * @param {function} callback - 界面刷新
         */
        pasteBlock (url, node, block, callback) {
            const self = this;
            const data = {
                id: node[self.setting.id],
                block: block
            };
            postData(url, data, function (datas) {
                const result = {};
                if (datas.update) {
                    result.update = self._updateData(datas.update);
                }
                if (datas.create) {
                    result.create = self._loadData(datas.create);
                }
                if (datas.delete) {
                    result.delete = self._freeData(datas.delete);
                }
                callback(result);
            });
        };
        /**
         * 提交数据
         * @param {String} url - 请求地址
         * @param {Object} node - 当前选中节点
         * @param {Object} data - 提交的数据
         * @param {function} callback - 界面刷新
         */
        postData (url, node, data, callback) {
            const self = this;
            if (node) {
                data.id = node[self.setting.id];
            }
            postData(url, data, function (datas) {
                const result = {};
                console.log('childrenCount: ' + datas.expand.length);
                let time = new Date();
                if (datas.update) {
                    result.update = self._updateData(datas.update);
                }
                if (datas.create) {
                    result.create = self._loadData(datas.create);
                }
                if (datas.delete) {
                    result.delete = self._freeData(datas.delete);
                }
                if (datas.expand) {
                    const [create, update] = self._loadExpandData(datas.expand);
                    result.create = result.create ? result.create.concat(create) : create;
                    result.expand = update;
                }
                time = new Date() - time;
                console.log('analysisData: ' + time);
                callback(result);
            });
        };

        batchInsert (url, node, data, callback) {
            const self = this;
            data.id = node[self.setting.id];
            postData(url, data, function (datas) {
                const result = {};
                if (datas.update) {
                    result.update = self._updateData(datas.update);
                }
                if (datas.create) {
                    result.create = self._loadData(datas.create);
                }
                if (datas.delete) {
                    result.delete = self._freeData(datas.delete);
                }
                callback(result);
            });
        };
    }

    if (type === 'base') {
        return new BaseTree(setting);
    } else if (type === 'active') {
        return new ActiveTree(setting);
    } else if (type === 'ledger') {
        return new LedgerTree(setting);
    } else if (type === 'measure') {
        return new MeasureTree(setting);
    }
};

const treeCalc = {
    getMaxLevel: function (tree) {
        return Math.max.apply(Math, tree.datas.map(function(o) {return o.level}));
    },
    calculateNode: function (tree, node, calcFields) {
        const children = tree.getChildren(node);
        if (children.length > 0) {
            const gather = children.reduce(function (rst, x) {
                const result = {};
                const fieldCalc = function (field) {
                    if (rst[field]) {
                        result[field] = x[field] ? rst[field] + x[field] : rst[field];
                    } else {
                        result[field] = x[field] ? x[field] : undefined;
                    }
                }
                for (const cf of calcFields) {
                    fieldCalc(cf);
                }
                return result;
            });
            for (const cf of calcFields) {
                if (gather[cf]) {
                    node[cf] = gather[cf];
                }
            }
        }
    },
    calculateLevelNode: function (tree, level, calcFields) {
        const nodes = tree.datas.filter((n) => { return n.level === level });
        for (const node of nodes) {
            this.calculateNode(tree, node, calcFields);
        }
    },
    calculateAll: function (tree, calcFields) {
        for (let i = this.getMaxLevel(tree); i >= 0; i--) {
            this.calculateLevelNode(tree, i, calcFields);
        }
    },
    calculateParent: function (tree, node, calcFields) {
        const nodes = tree.getFullPathNodes(node.full_path);
        nodes.sort((a, b) => {
            return b.level - a.level;
        });
        for (const n of nodes) {
            this.calculateNode(tree, n, calcFields);
        }
        return nodes;
    }
}