/**
 * Js树型数据转换工具类
 * @author cls
 * @date 2019-07-01
 */

import {
  dataGroupKey
} from "@birt/funclib/DataGroup.js";

/**
 * showdoc
 * @catalog JS工具类/DataTree
 * @title 根据分组字段配置，转换成两层树型数据
 * @description 根据传入对象数组，树型配置转换成树结构数据
 * @method static
 * @url import { groupToTreeData } from '@birt/utils/DataTree.js'
 * @param datas 必选 [{},{}] 记录集对象,树分组字段值冗余
 * @param tree 必选 {groupBy:['f1','f2'],childNodeKeyField:'methodName',childrenKey:'children'} 树型配置
 * @param tree.groupBy 必选 groupBy:['f1','f2'] 树分组字段
 * @param tree.childNodeKeyField 必选 'methodName' 子节点拼接id唯一值
 * @param tree.parentLabelField 必选 'labelFieldName' 树父节点标签绑定字段
 * @param tree.childLabelField 必选 'labelFieldName' 树子节点标签绑定字段
 * @param tree.childrenKey 可选 'children' 字节点key值
 * @param tree.nodeKey 可选 'treeId' 树节点唯一标识Id字段
 * @return [{'f1':'v1','f2':'v2'}]
 * @return_param f1 string 记录字段名
 * @return_param v1 object 记录字段名对应值
 * @remark 测试用例
 *  groupToTreeData(
 *    [{"methodName":"abs","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"取绝对值","refClassImport":"com.zhc.eap.funclib"},{"methodName":"directSuperiors","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"直属上级","refClassImport":"com.zhc.eap.funclib"},{"methodName":"curtOperator","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"当前操作者","refClassImport":"com.zhc.eap.funclib"},{"methodName":"plus","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"浮点数相加","refClassImport":"com.zhc.eap.funclib"},{"methodName":"curtDate","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期","refClassImport":"com.zhc.eap.funclib"},{"methodName":"curtDateTime","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期时间","refClassImport":"com.zhc.eap.funclib"}],
 *    {groupBy:["refClassImport","className"],parentLabelField:'classNameCn',childLabelField:'methodNameCn',childrenKey:"children"}
 *  )
 *  返回==>
 *  [{"methodName":"abs","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"取绝对值","refClassImport":"com.zhc.eap.funclib","label":"数学函数","children":[{"methodName":"abs","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"取绝对值","refClassImport":"com.zhc.eap.funclib","label":"取绝对值"},{"methodName":"plus","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"浮点数相加","refClassImport":"com.zhc.eap.funclib","label":"浮点数相加"}]},{"methodName":"directSuperiors","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"直属上级","refClassImport":"com.zhc.eap.funclib","label":"组织机构函数","children":[{"methodName":"directSuperiors","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"直属上级","refClassImport":"com.zhc.eap.funclib","label":"直属上级"},{"methodName":"curtOperator","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"当前操作者","refClassImport":"com.zhc.eap.funclib","label":"当前操作者"}]},{"methodName":"curtDate","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期","refClassImport":"com.zhc.eap.funclib","label":"日期函数","children":[{"methodName":"curtDate","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期","refClassImport":"com.zhc.eap.funclib","label":"当前日期"},{"methodName":"curtDateTime","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期时间","refClassImport":"com.zhc.eap.funclib","label":"当前日期时间"}]}]
 */
export function groupToTreeData(datas, tree) {
  let nodeKey = tree.nodeKey || "treeId"
  let childrenKey = tree.childrenKey || "children";
  var groupJson = {};
  datas.forEach(data => {
    const groupKey = dataGroupKey(data, tree.groupBy, ".")
    if (!groupJson[groupKey]) {
      groupJson[groupKey] = {};
      groupJson[groupKey][childrenKey] = [];
    }

    let treeNode = {
      label: data[tree.childLabelField]
    }
    treeNode[nodeKey] = groupKey + "." + data[tree.childNodeKeyField];
    treeNode = {
      ...treeNode,
      ...data
    };
    groupJson[groupKey][childrenKey].push(treeNode)
  })

  var treeRecds = []
  for (var gKey in groupJson) {
    var groupRecd = groupJson[gKey];
    var firstChild = groupJson[gKey][childrenKey][0];
    let treeNode = {
      label: firstChild[tree.parentLabelField]
    }
    treeNode[nodeKey] = gKey;

    var recd = {
      ...firstChild,
      ...treeNode,
      ...groupRecd
    }
    treeRecds.push(recd)
  }
  return treeRecds
}

/**
 * showdoc
 * @catalog JS工具类/DataTree
 * @title convertToTreeData 转换成树型结构数据
 * @description 根据传入数据数组，树型配置转换成树结构数据
 * @method static
 * @url import { convertToTreeData } from '@birt/utils/DataTree.js'
 * @param data 必选 [{},{}] 记录集对象
 * @param setting 必选 {id:'',parentId:'',children:''} 树型配置
 * @param setting.id 必选 string 树Id字段
 * @param setting.parentId 必选 string 树父Id字段
 * @param setting.children 可选 string 孩子节点key值，默认children
 * @return [{'f1':'v1','f2':'v2',children:[{},{}]}] 树型结构数据
 * @remark 测试用例
 *  convertToTreeData([{id:1,pid:0},{id:2,pid:0},{id:3,pid:1}],{id:"id",parentId:"pid"})
 *  返回==>
 *  [{"id":1,"pid":0,"children":[{"id":3,"pid":1}]},{"id":2,"pid":0}]
 */
export function convertToTreeData(data, setting) {
  let i, l;
  let key = setting.id;
  let parentKey = setting.parentId;
  let childKey = setting.children || "children";
  if (!key || key == "" || !data) return [];

  if (Array.isArray(data)) {
    var r = [];
    var tmpMap = [];
    for (i = 0, l = data.length; i < l; i++) {
      tmpMap[data[i][key]] = data[i];
    }
    for (i = 0, l = data.length; i < l; i++) {
      if (tmpMap[data[i][parentKey]] && data[i][key] != data[i][parentKey]) {
        if (!tmpMap[data[i][parentKey]][childKey])
          tmpMap[data[i][parentKey]][childKey] = [];
        tmpMap[data[i][parentKey]][childKey].push(data[i]);
      } else {
        r.push(data[i]);
      }
    }
    return r;
  } else {
    return [data];
  }
}

/**
 * 递归过滤节点，生成新的树结构
 * @param {Node[]} datas 要过滤的节点
 * @param {recd => boolean} filterFunc 过滤条件，符合条件的节点保留
 * @return 过滤后的节点
 */
export function filterTreeData(datas, filterFunc) {
  // 如果已经没有节点了，结束递归
  if (!(datas && datas.length)) {
    return [];
  }

  const newChildren = [];
  for (const recd of datas) {
    if (filterFunc.call(this, recd)) {
      // 如果节点符合条件，直接加入新的节点集
      newChildren.push(recd);
      recd.children = filterTreeData.call(this, recd.children, filterFunc);
    } else {
      // 如果当前节点不符合条件，递归过滤子节点，
      // 把符合条件的子节点提升上来，并入新节点集
      newChildren.push(...filterTreeData.call(this, recd.children, filterFunc));
    }
  }
  return newChildren;
}

/**
 * showdoc
 * @catalog JS工具类/DataTree
 * @title findParentTreeData 查找树父节点数据
 * @description 根据树当前节点id值,返回所有父级节点数据
 * @method static
 * @url import { findParentTreeData } from '@birt/funclib/DataTree.js'
 * @param dataMap 必选 {key1:{},key2:{}} map缓存映射
 * @param pidField 必选 string 树父id字段名
 * @param value 必选 string 查找id值
 * @param existIds 可选 Array [id1,id2,...] 已存在的记录id集合，去重添加 
 * @return [{'f1':'v1','f2':'v2'},{},...]
 * @remark 测试用例
 *  findParentTreeData(
 *    {1:{id:1,v:"a",pid:"-1"},2:{id:2,v:"b",pid:"-1"},3:{id:"3",v:"a.1",pid:"1"},4:{id:"4",v:"a.1.1",pid:"3"}},
 *    "pid",
 *    3
 *  )
 *  返回==>
 *  [{"id":1,"v":"a","pid":"-1"},{"id":"3","v":"a.1","pid":"1"}]
 */
export function findParentTreeData(dataMap, pidField, value, existIds) {
  let parentData = [];
  if (dataMap) {
    do {
      let parentRecd = dataMap[value];
      if (parentRecd) {
        // 如果存在去重数组，则判断记录id是否有存在
        if (existIds) {
          if (existIds.indexOf(value) == -1) {
            parentData.unshift(parentRecd);
            existIds.push(value);
          }
        } else {
          parentData.unshift(parentRecd);
        }
        if (value === parentRecd[pidField]) {
          console.error("dataMap 数据映射有问题，父节点值[" + value + "]找到自身或是父ID字段名错误，出现死循环，异常退出")
          break;
        }
        value = parentRecd[pidField];
      } else {
        break;
      }
    } while (true)
  } else {
    console.error("dataMap数据键值缓存为空，找不到父节点，可能数据集未配置rowKey主键属性")
  }
  return parentData
}


/**
 * showdoc
 * @catalog JS工具类/DataTree
 * @title getTreeParentChildrenMap 父节点与孩子映射列表
 * @description 根据树型数据结构，转换成父节点与孩子映射关系数据
 * @method static
 * @url import { getTreeParentChildrenMap } from '@birt/funclib/DataTree.js'
 * @param treeData 必选 树型数据
 * @param pidField 必选 父节点字段名
 * @param mapData 可选 已映射数据
 * @param indexPath 可选 树节点位置下标
 * @return [pid1:{children:[]},pid2:{children:[]}]
 * @remark 测试用例
 */
export function getTreeParentChildrenMap(treeData, pidField, mapData, indexPath) {
  if (treeData && (treeData.length > 0)) {
    mapData = mapData || {};
    indexPath = indexPath || [];
    treeData.forEach((recd, i) => {
      let childIndexPath = indexPath.concat(i);
      if (recd.children && (recd.children.length > 0)) {
        getTreeParentChildrenMap(recd.children, pidField, mapData, childIndexPath);
        delete recd.children;
        recd.hasChildren = true;
      }
    })
    let pidVal = treeData[0][pidField];
    mapData[pidVal] = { children: JSON.parse(JSON.stringify(treeData)), _indexPath: indexPath };
    return mapData
  }
}

/**
 * showdoc
 * @catalog JS工具类/DataTree
 * @title getTreeChildrenByIndexPath 根据下标路径，获取树型节点
 * @description 根据树型层级下标路径，获取该路径下的所有孩子节点
 * @method static
 * @url import { getTreeChildrenByIndexPath } from '@birt/funclib/DataTree.js'
 * @param treeData 必选 树型数据
 * @param indexPath 必选 下标路径
 * @return {} 返回路径对应记录对象
 * @remark 测试用例
 */
export function getTreeChildrenByIndexPath(treeData, indexPath) {
  if (indexPath) {
    let expr = "";
    for (let i = 0, l = indexPath.length; i < l; i++) {
      let ti = indexPath[i];
      if (i == 0) {
        expr = "treeData[" + ti + "]";
      } else {
        expr += ".children[" + ti + "]";
      }
    }
    return eval(expr);
  }
}

/**
 * showdoc
 * @catalog JS工具类/DataTree
 * @title getTreeParentRowAndSelfIndex 获取父节点记录
 * @description 根据树型数据结构，层次遍历,获取父节点记录和当前记录在该父节点孩子节点中位置下标
 * @method static
 * @url import { getTreeParentRowAndSelfIndex } from '@birt/funclib/DataTree.js'
 * @param treeData 必选 树型数据
 * @param curtRow 可选 当前查找记录
 * @param idField 必选 节点字段名
 * @return {children:{},index:0}
 * @remark 测试用例
 */
export function getTreeParentRowAndSelfIndex(treeData, curtRow, idField) {
  if (treeData && (treeData.length > 0)) {
    for (let i = 0, l = treeData.length; i < l; i++) {
      let recd = treeData[i];
      let idValue = recd[idField];
      let curtIdValue = curtRow[idField];
      if (idValue == curtIdValue) {
        return { children: treeData, index: i };
      }
      if (recd.children && (recd.children.length > 0)) {
        let retu = getTreeParentRowAndSelfIndex(recd.children, curtRow, idField);
        if (retu) {
          return retu;
        }
      }
    }
  }
}





