package org.algorithm.core;

import org.algorithm.core.cnn.entity.Lemma;
import org.algorithm.core.cnn.entity.Triad;

import java.util.*;

/**
 * 关系树工具类
 *
 * @Author: bijl
 * @Date: 2019/9/5 15:16
 * @Description:
 */
public class RelationTreeUtils {

    /**
     * 同名实体(这里也叫词项)归并
     * 规则：
     * 1- 直接替代为位置最前面的一个
     *
     * @param triads 实体对列表
     */
    public static void sameTextLemmaMerge(List<Triad> triads) {

        Map<String, Lemma> lemmaMap = new HashMap<>();
        for (Triad triad : triads) {
            Lemma l1 = triad.getL_1();
            Lemma l2 = triad.getL_2();

            if (lemmaMap.get(l1.getText()) == null)
                lemmaMap.put(l1.getText(), l1);
            else {
                Lemma l1Pre = lemmaMap.get(l1.getText());
                if (l1Pre.getStartPosition() > l1.getStartPosition())
                    triad.setL_1(l1);  // 取靠前的
            }

            if (lemmaMap.get(l2.getText()) == null)
                lemmaMap.put(l2.getText(), l2);
            else {
                Lemma l2Pre = lemmaMap.get(l2.getText());
                if (l2Pre.getStartPosition() > l2.getStartPosition())
                    triad.setL_2(l2);  // 取靠前的
            }
        }
        for (Triad triad : triads) {
            Lemma l1 = triad.getL_1();
            Lemma l2 = triad.getL_2();
            triad.setL_1(lemmaMap.get(l1.getText()));  // 用前面的同名实体(这里也叫词项)替代后面的
            triad.setL_2(lemmaMap.get(l2.getText()));  // 用前面的同名实体(这里也叫词项)替代后面的
        }
    }

    /**
     * 构建关系树
     * 基本规则：
     * 1- 两个有关系的实体，前面的为父节点，后面的为子节点
     *
     * @param triads 有关系的三元组列表
     */
    public static void buildRelationTree(List<Triad> triads) {
        for (Triad triad : triads) {
            Lemma l1 = triad.getL_1();
            Lemma l2 = triad.getL_2();
            if (l1.getStartPosition() < l2.getStartPosition()) {  // 在前者为父节点
                l1.setHaveChildren(true);
                l2.setParent(l1);
            } else {
                l2.setHaveChildren(true);
                l1.setParent(l2);
            }
        }
    }

    /**
     * 获取关系树的分枝
     *
     * @param triads 有关系，并且设置了父子节点关系的三元组
     */
    public static List<List<String>> getRelationTreeBranches(List<Triad> triads) {
        Map<Lemma, Integer> leafNodeLemmas = new HashMap<>();

        for (Triad triad : triads) {
            if (!triad.getL_1().isHaveChildren())
                leafNodeLemmas.putIfAbsent(triad.getL_1(), 1);

            if (!triad.getL_2().isHaveChildren())
                leafNodeLemmas.putIfAbsent(triad.getL_2(), 1);
        }

        List<List<String>> branches = new ArrayList<>();
        for (Lemma lemma : leafNodeLemmas.keySet()) {
            List<Lemma> aBranch = new ArrayList<>();
            while (lemma != null) {
                aBranch.add(lemma);
                lemma = lemma.getParent();
            }
            aBranch.sort(Comparator.naturalOrder());  // 按位置排序
            branches.addAll(handleBranch(aBranch));
        }


        return branches;
    }

    /**
     * 处理分枝，要求组合非阴性词，阴性词必须包含
     * 操作：
     * 1- 分离阴性词和非阴性词
     * 2- 组合非阴性词
     * 3- 添加阴性词到组合结果中
     *
     * @param aBranch
     * @return
     */
    private static List<List<String>> handleBranch(List<Lemma> aBranch) {
        List<Lemma> nonNegativeLemmas = new ArrayList<>();
        List<Lemma> negativeLemmas = new ArrayList<>();
        for (Lemma lemma : aBranch) {
            if ("反意或虚拟".equals(lemma.getProperty()))
                negativeLemmas.add(lemma);
            else
                nonNegativeLemmas.add(lemma);
        }
        List<List<Lemma>> nonNegativeLemmaCombinations = new ArrayList<>();
        if (nonNegativeLemmas.size() > 0) {
            for (int i = 1; i <= nonNegativeLemmas.size(); i++) {
                combinerSelect(nonNegativeLemmas, new ArrayList<>(), nonNegativeLemmaCombinations,
                        nonNegativeLemmas.size(), i);
            }
        }
        List<List<String>> result = new ArrayList<>();
        for (List<Lemma> lemmaCombination : nonNegativeLemmaCombinations) {
            List<String> lemmaNames = new ArrayList<>();
            lemmaCombination.addAll(negativeLemmas);  // 阴性词加入到组合中
            lemmaCombination.sort(Comparator.naturalOrder());  // 按位置排序
            for (Lemma lemma : lemmaCombination)  // 取出名称
                lemmaNames.add(lemma.getText());
            if (lemmaNames.size() >= 2)
                result.add(lemmaNames);
        }

        return result;

    }

    /**
     * 从三元组列表到关系树分枝
     *
     * @param triads
     * @return
     */
    public static List<List<String>> triadsToRelationTreeBranches(List<Triad> triads) {
//        sameTextLemmaMerge(triads);
        buildRelationTree(triads);
        return getRelationTreeBranches(triads);
    }

    /**
     * 组合生成器
     *
     * @param data      原始数据
     * @param workSpace 自定义一个临时空间，用来存储每次符合条件的值
     * @param k         C(n,k)中的k
     */
    private static <E> void combinerSelect(List<E> data, List<E> workSpace, List<List<E>> result, int n, int k) {
        List<E> copyData;
        List<E> copyWorkSpace = null;

        if (workSpace.size() == k) {
//            for (E c : workSpace)
//                System.out.print(c);

            result.add(new ArrayList<>(workSpace));
//            System.out.println();
        }

        for (int i = 0; i < data.size(); i++) {
            copyData = new ArrayList<E>(data);
            copyWorkSpace = new ArrayList<E>(workSpace);

            copyWorkSpace.add(copyData.get(i));
            for (int j = i; j >= 0; j--)
                copyData.remove(j);
            combinerSelect(copyData, copyWorkSpace, result, n, k);
        }
    }

    /**
     * 全排列算法
     *
     * @param stringList 字符串列表
     * @return
     */
    public static ArrayList<ArrayList<String>> permute(List<String> stringList) {
        ArrayList<ArrayList<String>> result = new ArrayList<ArrayList<String>>();
        result.add(new ArrayList<String>());

        for (int i = 0; i < stringList.size(); i++) {
            //list of list in current iteration of the stringList num
            ArrayList<ArrayList<String>> current = new ArrayList<ArrayList<String>>();

            for (ArrayList<String> l : result) {
                // # of locations to insert is largest index + 1
                for (int j = 0; j < l.size() + 1; j++) {
                    // + add num[i] to different locations
                    l.add(j, stringList.get(i));

                    ArrayList<String> temp = new ArrayList<String>(l);
                    current.add(temp);

                    // - remove num[i] add
                    l.remove(j);
                }
            }

            result = new ArrayList<>(current);
        }

        return result;
    }


    /**
     * 测试文件
     */
    public static void test() {

        List<Triad> triads = new ArrayList<>();
        String[] arr_1 = {"子宫", "0,1", "部位"};
        String[] arr_2 = {"内膜", "2,3", "结构"};
        addTriad(arr_1, arr_2, triads);

        String[] arr_1_1 = {"不", "13,13", "反意或虚拟"};
        String[] arr_2_1 = {"出血", "10,11", "形容词"};
        addTriad(arr_1_1, arr_2_1, triads);

        String[] arr_1_2 = {"胸部", "15,16", "部位"};
        String[] arr_2_2 = {"剧烈", "17,18", "程度"};
        addTriad(arr_1_2, arr_2_2, triads);

        String[] arr_1_3 = {"疼痛", "17,18", "形容词"};
        String[] arr_2_3 = {"剧烈", "19,20", "程度"};
        addTriad(arr_1_3, arr_2_3, triads);

        String[] arr_1_4 = {"内膜", "2,3", "结构"};
        String[] arr_2_4 = {"出血", "10,11", "形容词"};
        addTriad(arr_1_4, arr_2_4, triads);

        System.out.println(triads.size());
        sameTextLemmaMerge(triads);
        buildRelationTree(triads);
        List<List<String>> info = getRelationTreeBranches(triads);

        System.out.println(info);
    }

    /**
     * 增加三元组
     */
    private static void addTriad(String[] lemma_1, String[] lemma_2, List<Triad> triads) {
        Lemma lemma1 = new Lemma();
        lemma1.setText(lemma_1[0]);
        lemma1.setPosition(lemma_1[1]);
        lemma1.setProperty(lemma_1[2]);

        Lemma lemma2 = new Lemma();
        lemma2.setText(lemma_2[0]);
        lemma2.setPosition(lemma_2[1]);
        lemma2.setProperty(lemma_2[2]);

        Triad triad = new Triad();
        triad.setL_1(lemma1);
        triad.setL_2(lemma2);

        triads.add(triad);

    }


}
