package com.ema.common.utils;

import cn.hutool.core.collection.CollUtil;

import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * TreeUtils类提供了树形结构的构建方法，当一个列表数据需要转换成树形结构时，可以使用该类提供的方法。
 * <p>
 * 但列表类型必须实现接口{@link TreeNode}，在接口中定义了父子结构的关联属性，此处构建则是依据该关联属性进行构建。
 *
 * @author KVSonen
 * @see TreeNode
 */
public class TreeUtil {

    /**
     * 根据给定的列表构建树形结构，列表不能为空
     *
     * @param list 所有节点的集合，节点必须实现接口{@link TreeNode}
     * @param <T>  节点类型，接口{@link TreeNode}的实现类
     * @return 树形结构，在返回列表中，根节点为所有parentId为null或0的节点
     * @Param checkIds 选中的节点的id列表
     * @see TreeNode
     */
    public static <T extends TreeNode<T, ?>> List<T> buildTree(List<T> list, List<String> checkIds) {
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        // 过滤出所有没有父节点的节点作为根节点
        // 此处结果默认是经过排序的列表，排序方法为接口 TreeNode 实现类的compareTo方法
        List<T> root = list.stream()
                .filter(node -> node.getParentId() == null || node.getParentId().equals("0"))
                .sorted().collect(Collectors.toList());
        List<T> tempList = new ArrayList<>(list);
        // 减少遍历次数，在构建过程中将已选取的节点从list中移除
        tempList.removeAll(root);
        // 构建children
        root.forEach(node -> {
            node.setChecked(CollUtil.contains(checkIds, node.getId()));
            // 构建子节点
            buildChild(tempList, node, checkIds);
        });
        return root;
    }

    /**
     * 为给定节点的节点构建子节点
     *
     * @param list 所有节点的集合，节点必须实现接口{@link TreeNode}
     * @param node 当前正在构建子树的节点。
     * @param <T>  节点类型，接口{@link TreeNode}的实现类
     */
    private static <T extends TreeNode<T, ?>> void buildChild(@NotNull List<T> list, @NotNull T node, List<String> checkIds) {
        // 找到当前节点的所有子节点
        // 此处结果默认是经过排序的列表，排序方法为接口 TreeNode 的实现类的compareTo方法
        List<T> children = list.stream()
                .filter(child -> child.getParentId() != null && child.getParentId().equals(node.getId()))
                .sorted().collect(Collectors.toList());
        // 减少遍历次数，在构建过程中将已选取的节点从list中移除
        List<T> tempList = new ArrayList<>(list);
        tempList.removeAll(children);
        // 构建children的children
        children.forEach(child -> {
            // 递归构建子节点的子树
            buildChild(tempList, child, checkIds);
            //判断节点是否选中
            child.setChecked(CollUtil.contains(checkIds, child.getId()));
            // 将当前节点添加进子节点列表中
            node.addChild(child);
        });
    }
}


