package cc.smtweb.system.bpm.util;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * 树结构数据创建器
 *
 * @author xkliu
 */
public class TreeDataUtil {
    private TreeDataUtil() {
    }

    public static boolean longEquals(Long l1, Long l2) {
        if (l1 != null) {
            return l1.equals(l2);
        }

        return l2 == null;
    }

    private static <T> boolean findAndAdd(T parent, T item, ITreeDataHandler<T> handler) {
        if (longEquals(handler.getId(parent), handler.getParentId(item))) {
            addChild(parent, item, handler);
            return true;
        } else {
            List<T> children = handler.getChildren(parent);

            if (children != null) {
                for (T child : children) {
                    if (findAndAdd(child, item, handler)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    private static <T> boolean findAndAddLevel(T parent, T item, ITreeDataLevelHandler<T> handler) {
        if (longEquals(handler.getId(parent), handler.getParentId(item))) {
            addChildLevel(parent, item, handler);
            return true;
        } else {
            List<T> children = handler.getChildren(parent);

            if (children != null) {
                for (T child : children) {
                    if (findAndAddLevel(child, item, handler)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    private static <T> void addChild(T parent, T item, ITreeDataHandler<T> handler) {
        List<T> children = handler.getChildren(parent);
        if (children == null) {
            children = new ArrayList<>();
            handler.setChildren(parent, children);
        }

        children.add(item);
    }

    private static <T> void addChildLevel(T parent, T item, ITreeDataLevelHandler<T> handler) {
        List<T> children = handler.getChildren(parent);
        if (children == null) {
            children = new ArrayList<>();
            handler.setChildren(parent, children);
        }

        updateLevel(item, handler.getLevel(parent) + 1, handler);
        children.add(item);
    }

    private static <T> void updateLevel(T parent, int parentLevel, ITreeDataLevelHandler<T> handler) {
        handler.setLevel(parent, parentLevel);
        List<T> children = handler.getChildren(parent);
        if (children != null) {
            int level = parentLevel + 1;
            for (T t : children) {
                handler.setLevel(t, level);
                updateLevel(t, level, handler);
            }
        }
    }

    // 读取树结构
    public static <T> List<T> buildTree(T root, List<T> list, ITreeDataHandler<T> handler) {
        if (list != null) {
            for (T item : list) {
                if (!findAndAdd(root, item, handler)) {
                    for (T it : list) {
                        if (longEquals(handler.getId(it), handler.getParentId(item))) {
                            addChild(it, item, handler);
                            item = null;
                            break;
                        }
                    }

                    // not find item parent
                    if (item != null && handler.forceAdd(item)) {
                        addChild(root, item, handler);
                    }
                }
            }
        }

        return handler.getChildren(root);
    }

    // 读取树结构
    public static <T> List<T> buildLevelTree(T root, List<T> list, ITreeDataLevelHandler<T> handler) {
        if (list != null) {
            handler.setLevel(root, 0);
            for (T item : list) {
                if (!findAndAddLevel(root, item, handler)) {
                    for (T it : list) {
                        if (longEquals(handler.getId(it), handler.getParentId(item))) {
                            addChildLevel(it, item, handler);
                            item = null;
                            break;
                        }
                    }

                    // not find item parent
                    if (item != null && handler.forceAdd(item)) {
                        addChildLevel(root, item, handler);
                    }
                }
            }
        }

        return handler.getChildren(root);
    }

    public static <T> T findParent(T root, Long id, int treeLevel, ITreeDataLevelHandler<T> handler) {
        T result = findParentInLevel(root, id, treeLevel, handler);
        if (result != null && handler.getLevel(result) != treeLevel) {
            result = null;
        }

        return result;
    }

    private static <T> T findParentInLevel(T parent, Long id, int treeLevel, ITreeDataLevelHandler<T> handler) {
        List<T> children = handler.getChildren(parent);
        if (children != null) {
            for (T child : children) {
                if (id.equals(handler.getId(child))) {
                    return parent;
                } else {
                    T find = findParentInLevel(child, id, treeLevel - 1, handler);
                    if (find != null) {
                        if (treeLevel > 0) {
                            return find;
                        } else {
                            return parent;
                        }
                    }
                }
            }
        }

        return null;
    }

    public static <T> T findParent(T root, Long id, ITreeDataHandler<T> handler) {
        List<T> children = handler.getChildren(root);
        if (children != null) {
            for (T child : children) {
                if (id.equals(handler.getId(child))) {
                    return root;
                } else {
                    T find = findParent(child, id, handler);
                    if (find != null) {
                        return find;
                    }
                }
            }
        }

        return null;
    }

    public static <T> T find(T root, Long id, ITreeDataHandler<T> handler) {
        List<T> children = handler.getChildren(root);
        if (children != null) {
            for (T child : children) {
                if (id.equals(handler.getId(child))) {
                    return child;
                } else {
                    T find = find(child, id, handler);
                    if (find != null) {
                        return find;
                    }
                }
            }
        }

        return null;
    }

    public static <T> void forEach(T node, ITreeDataHandler<T> handler, Consumer<T> action) {
        List<T> children = handler.getChildren(node);
        if (children != null) {
            for (T child : children) {
                action.accept(child);
            }
        }
    }
}
