package cn.myflv.data;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 树结构助手.
 *
 * @author xin
 * @since 2023/3/29 20:54
 */
public class TreeHelper {
    
    /**
     * 判断list是否为空
     *
     * @param list   list
     * @param <Item> list类型
     * @return 是否为空
     */
    private static <Item> boolean isEmptyList(List<Item> list) {
        return list == null || list.isEmpty();
    }
    
    /**
     * 从list构建树.
     *
     * @param list         list
     * @param toTree       对象转树对象
     * @param getKey       对象key获取方法
     * @param getParentKey 对象父key获取方法
     * @param setChildren  子节点设置方法
     * @param <Tree>       树类型
     * @param <Node>       list类型
     * @param <Key>        key类型
     * @return 树
     */
    public static <Tree, Node, Key> List<Tree> fromList(List<Node> list, Function<Node, Tree> toTree,
            Function<Node, Key> getKey, Function<Node, Key> getParentKey, BiConsumer<Tree, List<Tree>> setChildren) {
        List<Key> keyList = list.stream().map(getKey).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        return fromList(list, x -> !keyList.contains(getParentKey.apply(x)), toTree, getKey, getParentKey, setChildren);
    }
    
    /**
     * 从list构建树.
     *
     * @param list         list
     * @param findParent   父节点判断
     * @param toTree       对象转树对象
     * @param getKey       对象key获取方法
     * @param getParentKey 对象父key获取方法
     * @param setChildren  子节点设置方法
     * @param <Tree>       树类型
     * @param <Node>       list类型
     * @param <Key>        key类型
     * @return 树
     */
    public static <Tree, Node, Key> List<Tree> fromList(List<Node> list, Predicate<Node> findParent,
            Function<Node, Tree> toTree, Function<Node, Key> getKey, Function<Node, Key> getParentKey,
            BiConsumer<Tree, List<Tree>> setChildren) {
        List<Tree> treeList = new ArrayList<>();
        List<Node> parentList = list.stream().filter(findParent).collect(Collectors.toList());
        if (isEmptyList(parentList)) {
            return treeList;
        }
        Map<Key, List<Node>> parentKeyMap = list.stream().filter(x -> getParentKey.apply(x) != null)
                .collect(Collectors.groupingBy(getParentKey));
        for (Node node : parentList) {
            Tree tree = toTree.apply(node);
            Key key = getKey.apply(node);
            setChildren.accept(tree, getChildren(key, parentKeyMap, toTree, getKey, setChildren));
            treeList.add(tree);
        }
        return treeList;
    }
    
    /**
     * 获取子节点.
     *
     * @param key          对象key
     * @param parentKeyMap 对象父key分组
     * @param toTree       转树方法
     * @param getKey       对象key获取方法
     * @param setChildren  设置子节点方法
     * @param <Tree>       树类型
     * @param <Node>       节点类型
     * @param <Key>        key类型
     * @return 树
     */
    private static <Tree, Node, Key> List<Tree> getChildren(Key key, Map<Key, List<Node>> parentKeyMap,
            Function<Node, Tree> toTree, Function<Node, Key> getKey, BiConsumer<Tree, List<Tree>> setChildren) {
        List<Node> childList = parentKeyMap.get(key);
        List<Tree> treeList = new LinkedList<>();
        if (isEmptyList(childList)) {
            return treeList;
        }
        for (Node node : childList) {
            Tree tree = toTree.apply(node);
            Key childKey = getKey.apply(node);
            treeList.add(tree);
            setChildren.accept(tree, getChildren(childKey, parentKeyMap, toTree, getKey, setChildren));
        }
        return treeList;
    }
    
    
    /**
     * 从树构建list.
     *
     * @param treeList    树
     * @param toNode      树转对象
     * @param generateKey 对象key获取方法
     * @param setKey      对象key设置方法
     * @param getChildren 子节点获取方法
     * @param <Tree>      树类型
     * @param <Node>      list类型
     * @param <Key>       key类型
     * @return list
     */
    public static <Tree, Node, Key> List<Node> toList(List<Tree> treeList, Function<Tree, Node> toNode,
            Function<Node, Key> generateKey, BiConsumer<Node, Key> setKey, BiConsumer<Node, Key> setParentKey,
            Function<Tree, List<Tree>> getChildren) {
        List<Node> nodeList = new LinkedList<>();
        if (isEmptyList(treeList)) {
            return nodeList;
        }
        for (Tree tree : treeList) {
            Node node = toNode.apply(tree);
            Key key = generateKey.apply(node);
            setKey.accept(node, key);
            nodeList.add(node);
            List<Tree> childList = getChildren.apply(tree);
            nodeList.addAll(getChildren(key, childList, toNode, generateKey, setKey, setParentKey, getChildren));
        }
        return nodeList;
    }
    
    /**
     * 从树构建list.
     *
     * @param treeList    树
     * @param toNode      树转对象
     * @param generateKey 对象key获取方法
     * @param setKey      对象key设置方法
     * @param getChildren 子节点获取方法
     * @param <Tree>      树类型
     * @param <Node>      list类型
     * @param <Key>       key类型
     * @return list
     */
    private static <Tree, Node, Key> List<Node> getChildren(Key parentKey, List<Tree> treeList,
            Function<Tree, Node> toNode, Function<Node, Key> generateKey, BiConsumer<Node, Key> setKey,
            BiConsumer<Node, Key> setParentKey, Function<Tree, List<Tree>> getChildren) {
        List<Node> nodeList = new LinkedList<>();
        if (isEmptyList(treeList)) {
            return nodeList;
        }
        for (Tree tree : treeList) {
            Node node = toNode.apply(tree);
            Key key = generateKey.apply(node);
            setKey.accept(node, key);
            setParentKey.accept(node, parentKey);
            nodeList.add(node);
            List<Tree> childList = getChildren.apply(tree);
            nodeList.addAll(toList(childList, toNode, generateKey, setKey, setParentKey, getChildren));
        }
        return nodeList;
    }
}