package com.ruoyi.common.utils;

import cn.hutool.core.collection.CollectionUtil;
import lombok.Builder;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

// 此TreeUtils不需要外部的容器包装类, 只需要配置`id`, `parentId`的getter和`children`的setter等
public final class TreeUtils
{
    // 从列表构建树
    public static <T> List<T>/* 树 */ makeTree(List<T> list/* = 列表 */, ConfigInterface<T> config, Object...parentIdArgs)
    {
        Object parentId = parentIdArgs.length > 0 ? parentIdArgs[0] : null;
        return list.stream()
                .filter((x) -> config.idEquals(parentId, config.getParentId(x)))
                .peek((x) -> config.setChildren(x, makeTree(list, config, config.getId(x))))
                .collect(Collectors.toList());
    }

    // 获取列表的所有叶子节点
    public static <T> List<T>/* 列表 */ treeLeafs(List<T> list/* = 列表 */, ConfigInterface<T> config)
    {
        return list.stream().filter((x) -> list.stream()
                        .filter((y) -> !config.idEquals(config.getId(x), config.getId(y)))
                        .noneMatch((y) -> config.idEquals(config.getId(x), config.getParentId(y)))
                )
                .collect(Collectors.toList());
    }

    // 获取树的所有节点
    public static <T> List<T>/* 列表 */ makeList(List<T> tree/* = 树 */, ConfigInterface<T> config)
    {
        List<T> res = new ArrayList<>();
        foreach(tree, res::add, config);
        return res;
    }

    // 从上到下遍历树节点
    public static <T> void recursion(T node/* = 树节点 */, Consumer<T> func, ConfigInterface<T> config)
    {
        if(null == node)
            return;
        func.accept(node);
        foreach(config.getChildren(node), func, config);
    }

    // 从上到下遍历树节点列表
    public static <T> void foreach(List<T> tree/* = 树 */, Consumer<T> func, ConfigInterface<T> config)
    {
        if(CollectionUtil.isEmpty(tree))
            return;
        for(T child : tree)
            recursion(child, func, config);
    }

    // 从下到上遍历树节点
    public static <T> void recursionReverse(T node/* = 树节点 */, Consumer<T> func, ConfigInterface<T> config)
    {
        if(null == node)
            return;
        foreachReverse(config.getChildren(node), func, config);
        func.accept(node);
    }

    // 从下到上遍历树节点列表
    public static <T> void foreachReverse(List<T> tree/* = 树 */, Consumer<T> func, ConfigInterface<T> config)
    {
        if(CollectionUtil.isEmpty(tree))
            return;
        for(T child : tree)
            recursionReverse(child, func, config);
    }

    public static interface ConfigInterface<T>
    {
        public default boolean idEquals(Object a, Object b) // 比较ID/父ID是否想的, 默认Object::equals
        {
            if(null == a && null == b)
                return true;
            if(null == a || null == b)
                return false;
            return a.equals(b);
        }
        public Object getId(T item); // 获取T的ID
        public Object getParentId(T item); // 获取T的父ID
        public void setChildren(T item, List<T> children); // 设置T的children列表
        public List<T> getChildren(T object); // 获取T的children列表
    }

    @Data
    @Builder
    public static class Config<T> implements ConfigInterface<T> // 简单的可实例化的T的配置类
    {
        public Function<T, Object> getIdFunc; // makeTree必需, 返回I的ID
        public Function<T, Object> getParentIdFunc; // makeTree必需, 返回I的父ID
        public BiConsumer<T, List<T>> setChildrenFunc; // makeTree必需, 设置T的children
        public BiPredicate<Object, Object> idEqualsFunc; // 非必需, 比较ID/父ID是否想的, 默认使用Object::equals

        public Function<T, List<T>> getChildrenFunc; // 遍历时必需, 返回T的children列表

        public boolean idEquals(Object a, Object b)
        {
            if(null != idEqualsFunc)
                return idEqualsFunc.test(a, b);
            if(null == a && null == b)
                return true;
            if(null == a || null == b)
                return false;
            return a.equals(b);
        }

        public Object getId(T item)
        {
            return getIdFunc.apply(item);
        }

        public Object getParentId(T item)
        {
            return getParentIdFunc.apply(item);
        }

        public void setChildren(T item, List<T> children)
        {
            if(CollectionUtil.isNotEmpty(children))
                setChildrenFunc.accept(item, children);
        }

        public List<T> getChildren(T object)
        {
            return getChildrenFunc.apply(object);
        }
    }

    private TreeUtils() {}
}
