package com.rongwei.bscommon.sys.utils;


import com.rongwei.bscommon.sys.utils.bean.Tree;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 树结构工具
 *
 * @author Trj
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TreeUtil<T> {
    public List<T> sonList,fatherList;//所有子节点、所有父节点
    public Tree<T> tree;//树


    /**
     * 根据制定字段建立树和对应后代节点集合
     * @param list 所有节点
     * @param head 对象类型
     * @param fatherFiled 父节点字段
     * @param sonFiled 当前节点字段
     * @param start 起始字段的值
     */
    public void buildListAndTree(List<T> list,Class head,String fatherFiled,String sonFiled,String start) {
        //根据某个节点建树和集合
        Map<String,T> sonMap=new HashMap<>();//以自身唯一标识建map
        Map<String,List<T>> fatherMap=new HashMap<>();//以父节点唯一标识建map
        for (T temp:list) {
            try {
                Field field1 = head.getDeclaredField(sonFiled);//自身唯一标识
                field1.setAccessible(true);
                Object o1 = field1.get(temp);
                sonMap.put(o1.toString(),temp);
                Field field2 = head.getDeclaredField(fatherFiled);//父节点唯一标识
                field2.setAccessible(true);
                Object o2 = field2.get(temp);
                if (o2==null) continue;//该节点为根节点不存在父节点
                if (fatherMap.containsKey(o2.toString())) {//已经含有该父节点
                    List<T> ts = fatherMap.get(o2.toString());
                    ts.add(temp);
                }else {//不含该父节点
                    List<T> tempList=new ArrayList<>();
                    tempList.add(temp);
                    fatherMap.put(o2.toString(),tempList);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (sonMap.containsKey(start)) {
            T startNode = sonMap.get(start);//起始节点
            this.sonList = buildSonList(fatherMap,startNode,sonFiled,head);//建子节点
            this.fatherList=buildFatherList(sonMap,startNode,head,fatherFiled);//建父节点
            this.tree=buildTree(fatherMap,startNode,head,sonFiled);//建树
        }
    }

    /**
     * 根据指定节点获得该节点的所有子节点
     * @param fatherMap 父节点唯一标识
     * @param startNode 起始节点
     * @param sonFiled 当前节点唯一标识属性
     * @param head 类
     * @return
     */
    private List<T> buildSonList(Map<String,List<T>> fatherMap, T startNode, String sonFiled, Class head){
        //建集合
        List<T> result=new ArrayList<>();
        Queue<T> queue=new LinkedList<>();
        queue.add(startNode);//队列
        while (!queue.isEmpty()) {
            T curNode = queue.poll();
            try {
                Field field = head.getDeclaredField(sonFiled);
                field.setAccessible(true);
                Object o = field.get(curNode);
                if (fatherMap.containsKey(o.toString())) {
                    List<T> sons = fatherMap.get(o.toString());//当前节点所有子节点
                    queue.addAll(sons);
                }
                result.add(curNode);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 根据起始节点获得所有父节点
     * @param sonMap 节点集
     * @param startNode 起始节点
     * @param head 类
     * @param fatherFiled 父节点唯一标识属性
     * @return
     */
    private List<T> buildFatherList(Map<String,T> sonMap,T startNode,Class head,String fatherFiled){
        List<T> result=new ArrayList<>();
        try {
            Field field = head.getDeclaredField(fatherFiled);
            field.setAccessible(true);
            while (field.get(startNode)!=null) {
                result.add(startNode);
                startNode=sonMap.get(field.get(startNode));
            }
            result.add(startNode);
        } catch (Exception e) {
            e.printStackTrace();
        }
        Collections.reverse(result);
        return result;
    }

    /**
     * 根据起始节点复现树状结构
     * @param fatherMap 父节点集
     * @param startNode 起始节点
     * @param head 类
     * @param sonFiled 当前节点唯一标识属性
     * @return
     */
    private Tree<T> buildTree(Map<String,List<T>> fatherMap,T startNode, Class head,String sonFiled){//建树
        try {
            Field field = head.getDeclaredField(sonFiled);//当前节点唯一标识
            field.setAccessible(true);
            Object o = field.get(startNode);
            if (fatherMap.containsKey(o.toString())) {//存在子节点
                List<T> sons = fatherMap.get(o.toString());
                List<Tree<T>> treeSon=new ArrayList<>();
                for (T son:sons) {
                    Tree<T> temp = buildTree(fatherMap, son, head, sonFiled);
                    treeSon.add(temp);
                }
                Tree<T> root=new Tree<>(startNode,treeSon);
                return root;
            }else {//不存在子节点
                return new Tree<T>(startNode,null);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
