
export class Checker {
  constructor (root) {
    this.root = root || {};
  }

  init (nodes, func) {
    var isFunc = typeof func === 'function';
    var walk = this.walker(function (node) {
      isFunc && func(node);
    });
    if (Array.isArray(nodes) && nodes.length > 0) {
      let i = 0;
      let len = nodes.length;
      while (i < len) {
        walk(nodes[i++]);
      }
      let node = this.cloneNode(nodes[0]);
      this.root = node;
      this.root.id = node.pid;
      this.root.pid = null;
      this.root.checked = nodes.every(function (node) {
        return node.checked;
      });
      this.root.children = nodes;
    } else {
      this.root = ('id' in nodes && nodes) || this.root;
      walk(this.root);
    }
  }

  /**
   * 根据节点id查找节点并返回，如果没找到，返回null
   * @param  {String | Number} id 节点id
   * @return {Object}
   */
  find (id) {
    var ret = null;
    var walk = this.walker(function (node) {
      if (node.id === id) {
        ret = node;
        return true;
      }
    });
    if (this.root.id === id) {
      return this.root;
    }
    walk(this.root);
    return ret;
  }

  /**
   * 查找满足特定条件的节点并返回，如果没有满足条件的节点，最终返回
   * 空数组
   * @param  {Function} filter 过滤条件
   * @return {Array}
   */
  findAll (filter) {
    var ret = [];
    this.walker(function (node) {
      if (filter(node)) {
        node.children = null;
        ret.push(node);
      }
    }, true)(this.root);
    return ret;
  }

  walker (func, clone) {
    var stop = false;
    var _walk = function _walk (node) {
      var children = node.children;
      if (typeof func === 'function') {
        stop = func(node);
      }
      if (Array.isArray(children)) {
        let i = 0;
        let len = children.length;
        while (i < len && !stop) {
          _walk(children[i++]);
        }
      }
    }
    return function (node) {
      _walk(clone ? this.cloneNode(node) : node);
    }.bind(this);
  }

  /**
   * 遍历所有节点，对每一个节点进行指定的的操作
   * @param  {Function} func
   */
  traverse (func) {
    this.walker(function (node) {
      if (typeof func === 'function') {
        func(node);
      }
    })(this.root);
    return this;
  }

  /**
   * 在控制台以特定的格式打印所有节点，可用于方法测试
   */
  tree () {
    var counter = 0;
    var selectedNum = 0;
    console.clear();
    (function _walk (node, deep) {
      var mark = '+'.padStart(4 * deep + 1, ' ');
      console.log(mark, node.name || '', node.id, node.checked);
      var children = node.children;
      if (Array.isArray(children)) {
        let i = 0;
        let len = children.length;
        while (i < len) {
          _walk(children[i++], deep + 1);
        }
        deep++;
      }
      if (node.checked) {
        selectedNum++;
      }
      counter++;
    }(this.root, 0));
    console.log('STATIS：共', counter, '个节点，已选中', selectedNum, '个');
  }

  cloneNode (node) {
    if (!node) {
      throw new Error('参数必须是节点对象');
    }
    return JSON.parse(JSON.stringify(node));
  }

  /**
   * 递归地设置node节点的父节点checked属性值为true，直到根节点
   * @param  {Object} node
   */
  checkParent (node) {
    var pnode = this.find(node.pid);
    if (pnode) {
      if (pnode.checked) {
        return;
      }
      let children = pnode.children;
      if (Array.isArray(children) && children.length > 0) {
        let allChecked = children.every(function (node) {
          return node.checked;
        });
        if (allChecked) {
          if (typeof this.onCheck === 'function') {
            pnode.checked = true;
            this.onCheck(pnode);
          } else {
            pnode.checked = true;
          }
          this.checkParent(pnode);
        }
      }
    }
  }

  /**
   * 递归地设置指定节点的父节点的checked属性值为false，直到根节点
   * @param  {Object} node
   */
  uncheckParent (node) {
    var pnode = this.find(node.pid);
    if (pnode) {
      if (!pnode.checked) {
        return;
      }
      if (typeof this.onUncheck === 'function') {
        pnode.checked = false;
        this.onUncheck(pnode);
      } else {
        pnode.checked = false;
      }
      if (pnode.id === this.root.id) {
        return;
      }
      this.uncheckParent(pnode);
    }
  }

  /**
   * 设置指定节点及其子节点的checked属性值，如果status为真值，则
   * 节点的checked被设置为true，否则为false
   * @param  {Object} node
   * @param  {Boolean} status true or false
   */
  toggle (node, status) {
    if (node) {
      if (status) {
        if (node.checked) {
          return;
        }
        if (typeof this.onCheck === 'function') {
          node.checked = true;
          this.onCheck(node);
        } else {
          node.checked = true;
        }
      } else {
        if (!node.checked) {
          return;
        }
        if (typeof this.onUncheck === 'function') {
          node.checked = false;
          this.onUncheck(node);
        } else {
          node.checked = false;
        }
      }
    }
    if (Array.isArray(node.children)) {
      let i = 0;
      let len = node.children.length;
      while (i < len) {
        this.toggle(node.children[i++], status);
      }
    }
  }

  /**
   * 设置所有节点的checked属性值为true
   * @return {[type]} [description]
   */
  checkAll () {
    this.toggle(this.root, true);
    return this;
  }

  /**
   * 设置所有节点checked属性值为false
   * @return {[type]} [description]
   */
  uncheckAll () {
    this.toggle(this.root, false);
    return this;
  }

  /**
   * 设置指定节点的checked属性值，如果enable为真值，则checked的值为true;
   * 否则，设置为false。该方法会关联地设置指定节点的父子节点。
   * @param  {Object | String | Number} id 节点或节点的id
   * @param  {Boolean} enable
   */
  check (id, enable) {
    var node;
    if (id && typeof id === 'object' && 'id' in id) {
      node = id;
    } else {
      node = this.find(id);
    }
    if (node == null) {
      return;
    }
    if (!!enable) {
      this.toggle(node, true);
      this.checkParent(node);
    } else {
      this.toggle(node, false);
      this.uncheckParent(node);
    }
    return this;
  }

}
