package com.tykj.dev.misc.utils;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author HuangXiahao
 * @version V1.0
 * @class getTreeUtils
 * @packageName com.tykj.dev.device.utils
 **/
public class GetTreeUtils {

    /**
     * 自底向上整理出树结构，并存放在Node对象中
     * 利用Map将原始List的对象的位置重新整理，并将所有的指针都设置好
     *
     * @param originList      原始待整理的列表
     * @param getId           列表里每个对象获取自身id的方法，用于避免重复计算
     * @param getParent       列表里每个对象获取父对象的方法
     * @param targetSetChild  用于最终对象设置子节点的方法，形如R.setChild(R),或者R.addChildList(R)
     * @param <V>             原始对象
     * @param <T>             主键类型
     * @return 根节点对象集合
     */
    public static <V, T extends Number> List<V> parseTreeFromDown(List<V> originList,
                                                           Function<V, T> getId,
                                                           Function<V, Optional<V>> getParent,
                                                           BiConsumer<V, V> targetSetChild) {
        //K为主键id , Value 为最终对象
        Map<T, V> map = new HashMap<>(32);
        List<T> rootIds = new ArrayList<>();

        for (V originNode : originList) {
            //对于所有节点，如果已经遍历过了则直接取已经设置过子节点的引用
            V targetNode = map.getOrDefault(getId.apply(originNode), originNode);

            Optional<V> parentNode = getParent.apply(originNode);
            //查询父节点，如果不存在父节点则证明该节点为根节点，直接放入map中
            if (parentNode.isPresent()) {
                //否则查询该父节点是否已经已经被连接过指针，如果连接过则取出连接过的继续连接，否则进行第一次连接并存入map
                V parent = parentNode.get();
                T parentId = getId.apply(parent);
                V parentInMap = map.get(parentId);
                if (parentInMap == null) {
                    targetSetChild.accept(parent, targetNode);
                    map.put(parentId, parent);
                } else {
                    targetSetChild.accept(parentInMap, targetNode);
                }
                //连接完处理之后还需要将自身这个节点存入map
                map.put(getId.apply(originNode), targetNode);
            } else {
                //root node
                map.put(getId.apply(originNode), targetNode);
                rootIds.add(getId.apply(originNode));
            }
        }
        //根据rootIds返回所有的顶层节点
        return rootIds.stream().map(map::get).collect(Collectors.toList());
    }
}
