package org.algorithm.core;

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

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * 规则检查机器
 *
 * @Author: bijl
 * @Date: 2019/9/6 10:32
 * @Description:
 */
public class RuleCheckMachine {
    private final List<FilterRule> filterRules = new ArrayList<>();
    private Map<String, Map<String, Set<Integer>>> key_1_map = null;
    private Map<String, Map<String, Set<Integer>>> key_2_map = null;
    private Map<String, String> punctuations = new HashMap<>();
    private Map<String, Set<Integer>> despiteMap = null;  // 实体名：[规则uuid列表]
    private Map<String, Set<Integer>> despiteInsideMap = null; // 实体名：[规则uuid列表]
    private Map<String, Map<String, Set<Integer>>> insideMap = null;


    public RuleCheckMachine() {
        this.loadRules();
        this.makeKey1Map();
        this.makeKey2Map();
        this.makeInsideMap();
        this.makeDespiteMap();
        this.makeDespiteInsideMap();
    }


    /**
     * 加载规则
     */
    public void loadRules() {
        /**
         * 连接数据库
         */
        String url = "jdbc:mysql://192.168.2.235/test_case?user=root&password=diagbot@20180822";
        MysqlConnector connector = new MysqlConnector(url);
        String querySql =
                "SELECT rr.key_1, rr.type_1, rr.key_2, rr.type_2, rr.inside, rr.inside_type, " +
                        "rr.despite, rr.despite_inside " +
                        "FROM relation_neg_rules AS rr " +
                        "WHERE rr.`status` = 1";

        ResultSet rs = connector.query(querySql);
        Integer uuid = 0;
        try {
            while (rs.next()) {
                String key_1 = rs.getString("key_1");
                String type_1 = rs.getString("type_1");

                String key_2 = rs.getString("key_2");
                String type_2 = rs.getString("type_2");

                String inside = rs.getString("inside");
                String inside_type = rs.getString("inside_type");

                String despite = rs.getString("despite");
                String despite_inside = rs.getString("despite_inside");

                String[] despiteSplit = despite.split("，");
                String[] despiteInsideSplit = despite_inside.split("，");
                for (int j = 0; j < despiteSplit.length; j++) {
                    for (int k = 0; k < despiteInsideSplit.length; k++) {
                        Map<String, String> variableMap = new HashMap<>();
                        variableMap.put("key_1", key_1);
                        variableMap.put("type_1", type_1);

                        variableMap.put("key_2", key_2);
                        variableMap.put("type_2", type_2);

                        variableMap.put("inside", inside);
                        variableMap.put("inside_type", inside_type);

                        variableMap.put("despite", despiteSplit[j]);
                        variableMap.put("despite_inside", despiteInsideSplit[k]);

                        FilterRule filterRule = new FilterRule(variableMap);
                        filterRule.setUuid(uuid);
                        this.filterRules.add(filterRule);

//                            System.out.println(filterRule);

                        uuid += 1;
                    }
                }

            }

        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("加载规则字典失败");
        } finally {
            connector.close();
        }
    }

    /**
     * 制作实体1相关信息字典
     */
    private void makeKey1Map() {
        Map<String, Map<String, Set<Integer>>> key_1_map_ = new HashMap<>();
        Map<String, Set<Integer>> emptyMap = new HashMap<>();
        Map<String, Set<Integer>> typeMap = new HashMap<>();
        Map<String, Set<Integer>> wordMap = new HashMap<>();
        key_1_map_.put("", emptyMap);
        key_1_map_.put("type", typeMap);
        key_1_map_.put("word", wordMap);

        for (FilterRule rule : this.filterRules) {
            String key_1 = rule.getKey_1();
            String type_1 = rule.getType_1();
            Integer uuid = rule.getUuid();

            this.inputMaps(key_1, type_1, uuid, emptyMap, typeMap, wordMap, null);
        }
        this.key_1_map = key_1_map_;
    }


    /**
     * 制作实体2相关信息字典
     */
    private void makeKey2Map() {
        Map<String, Map<String, Set<Integer>>> key_2_map_ = new HashMap<>();
        Map<String, Set<Integer>> emptyMap = new HashMap<>();
        Map<String, Set<Integer>> typeMap = new HashMap<>();
        Map<String, Set<Integer>> wordMap = new HashMap<>();
        key_2_map_.put("", emptyMap);
        key_2_map_.put("type", typeMap);
        key_2_map_.put("word", wordMap);

        for (FilterRule rule : this.filterRules) {
            String key_2 = rule.getKey_2();
            String type_2 = rule.getType_2();
            Integer uuid = rule.getUuid();

            this.inputMaps(key_2, type_2, uuid, emptyMap, typeMap, wordMap, null);
        }
        this.key_2_map = key_2_map_;
    }

    /**
     * 制作内部实体相关信息字典
     */
    private void makeInsideMap() {
        Map<String, Map<String, Set<Integer>>> insideMap_ = new HashMap<>();
        Map<String, Set<Integer>> punctuationMap = new HashMap<>();
        Map<String, Set<Integer>> typeMap = new HashMap<>();
        Map<String, Set<Integer>> typePunctuationMap = new HashMap<>();
        Map<String, Set<Integer>> wordMap = new HashMap<>();
        insideMap_.put("punc", punctuationMap);
        insideMap_.put("type", typeMap);
        insideMap_.put("typePunctuation", typePunctuationMap);
        insideMap_.put("word", wordMap);

        for (FilterRule rule : this.filterRules) {
            String inside = rule.getInside();
            String insideType = rule.getInsideType();
            Integer uuid = rule.getUuid();
            if (insideType.equals("punc"))
                this.punctuations.put(inside, inside);

            if (",".equals(inside.substring(0, 1)))
                this.inputMaps(inside, insideType, uuid, null, typePunctuationMap, wordMap, punctuationMap);
            else
                this.inputMaps(inside, insideType, uuid, null, typeMap, wordMap, punctuationMap);
        }
        this.insideMap = insideMap_;
    }

    /**
     * maps输入
     *
     * @param key
     * @param type
     * @param uuid
     * @param emptyMap
     * @param typeMap
     * @param wordMap
     */
    private void inputMaps(String key, String type, Integer uuid, Map<String, Set<Integer>> emptyMap,
                           Map<String, Set<Integer>> typeMap, Map<String, Set<Integer>> wordMap,
                           Map<String, Set<Integer>> punctuationMap) {

        if ("".equals(type)) {
            if (emptyMap.get(key) == null)
                emptyMap.put(key, new HashSet<>());
            emptyMap.get(key).add(uuid);
        } else if ("type".equals(type)) {
            if (typeMap.get(key) == null)
                typeMap.put(key, new HashSet<>());
            typeMap.get(key).add(uuid);
        } else if ("word".equals(type)) {
            if (wordMap.get(key) == null)
                wordMap.put(key, new HashSet<>());
            wordMap.get(key).add(uuid);
        } else if ("punc".equals(type)) {
            if (punctuationMap.get(key) == null)
                punctuationMap.put(key, new HashSet<>());
            punctuationMap.get(key).add(uuid);
        } else {
            throw new RuntimeException("出现了位置新type");
        }

    }


    /**
     * 制作例外字典
     */
    private void makeDespiteMap() {
        Map<String, Set<Integer>> despiteMap = new HashMap<>();
        for (FilterRule rule : this.filterRules) {
            String despite = rule.getDespite();
            if (!despite.equals("")) {  // 空白不收录
                if (despiteMap.get(despite) == null) {
                    despiteMap.put(despite, new HashSet<>());
                }
                despiteMap.get(despite).add(rule.getUuid());  //
            }
        }
        this.despiteMap = despiteMap;
    }


    /**
     * 制作例外_内部字典
     */
    private void makeDespiteInsideMap() {
        Map<String, Set<Integer>> despiteInsideMap = new HashMap<>();
        for (FilterRule rule : this.filterRules) {
            String despiteInside = rule.getDespiteInside();
            if (!despiteInside.equals("")) {  // 空白不收录
                if (despiteInsideMap.get(despiteInside) == null) {
                    despiteInsideMap.put(despiteInside, new HashSet<>());
                }
                despiteInsideMap.get(despiteInside).add(rule.getUuid());  //
            }
        }
        this.despiteInsideMap = despiteInsideMap;
    }

    /**
     * 名称—类别—开始位置类
     */
    class NameTypeStartPosition implements Comparable<NameTypeStartPosition> {
        private String name;
        private String type;
        private int startPosition;

        public NameTypeStartPosition(String name, String type, int startPosition) {
            this.name = name;
            this.type = type;
            this.startPosition = startPosition;
        }

        @Override
        public int compareTo(NameTypeStartPosition o) {
            return this.startPosition - o.getStartPosition();
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public int getStartPosition() {
            return startPosition;
        }

        public void setStartPosition(int startPosition) {
            this.startPosition = startPosition;
        }

        @Override
        public String toString() {
            return "NameTypeStartPosition{" +
                    "name='" + name + '\'' +
                    ", type='" + type + '\'' +
                    ", startPosition=" + startPosition +
                    '}';
        }

    }

    /**
     * 获取已排序的(名称，类别，开始位置)对象
     *
     * @param triads
     * @return
     */
    public List<NameTypeStartPosition> getSortedNameTypeByPosition(List<Triad> triads) {
        List<NameTypeStartPosition> nameTypeStartPositions = new ArrayList<>();
        for (Triad triad : triads) {
            Lemma l1 = triad.getL_1();
            Lemma l2 = triad.getL_2();
            nameTypeStartPositions.add(
                    new NameTypeStartPosition(l1.getText(), l1.getProperty(), l1.getStartPosition()));
            nameTypeStartPositions.add(
                    new NameTypeStartPosition(l2.getText(), l2.getProperty(), l2.getStartPosition()));
        }
        nameTypeStartPositions.sort(Comparator.naturalOrder());

        return nameTypeStartPositions;
    }

    /**
     * 是否移除
     *
     * @param nameTypeStartPositions
     * @param startIndex
     * @param endIndex
     * @return
     */
    public boolean isRemove(List<NameTypeStartPosition> nameTypeStartPositions, int startIndex, int endIndex,
                            String sentence) {
        Set<Integer> remainUuids = new HashSet<>();  // 剩余规则的uuid
        for (FilterRule rule : this.filterRules)
            remainUuids.add(rule.getUuid());

        // 过滤实体名称触发例外条件情况
        String entity_1_name = nameTypeStartPositions.get(startIndex).getName();
        String entity_1_type = nameTypeStartPositions.get(startIndex).getType();

        String entity_2_name = nameTypeStartPositions.get(endIndex).getType();
        String entity_2_type = nameTypeStartPositions.get(endIndex).getType();

        Set<Integer> set = null;
        set = this.despiteMap.get(entity_1_name);  // 过滤有实体1名为例外情况(即，不成立)的规则(的uuid)
        this.removeAll(remainUuids, set);

        set = this.despiteMap.get(entity_2_name);  // 过滤有实体2名为例外情况(即，不成立)的规则(的uuid)
        this.removeAll(remainUuids, set);

        // 过滤中间实体的名称触发例外条件情况
        for (int i = startIndex; i <= endIndex; i++) {
            NameTypeStartPosition nameTypeStartPosition = nameTypeStartPositions.get(i);
            set = this.despiteInsideMap.get(nameTypeStartPosition.getName());
            this.removeAll(remainUuids, set);
        }

        // 三板斧过滤
        // 实体1，过滤
        set = new HashSet<>();
        this.addAll(set, this.key_1_map.get("").get(""));
        // 满足，形如("形容词", "type") 过滤条件的规则
        this.addAll(set, this.key_1_map.get("type").get(entity_1_type));
        // 满足，形如("胸痛", "word") 过滤条件的规则
        this.addAll(set, this.key_1_map.get("word").get(entity_1_name));
        this.retainAll(remainUuids, set);  // 求交集，同事满足实体1相关的过滤条件，且不不满足例外情况
        if (remainUuids.size() == 0)
            return false;

        // 实体2，过滤
        set = new HashSet<>();
        this.addAll(set, this.key_2_map.get("").get(""));
        // 满足，形如("形容词", "type") 过滤条件的规则
        this.addAll(set, this.key_2_map.get("type").get(entity_2_type));
        // 满足，形如("胸痛", "word") 过滤条件的规则
        this.addAll(set, this.key_2_map.get("word").get(entity_2_name));
        this.retainAll(remainUuids, set);  // 求交集，同事满足实体1相关的过滤条件，且不不满足例外情况
        if (remainUuids.size() == 0)
            return false;

        // 中间实体过滤
        set = new HashSet<>();
        for (int i = startIndex; i <= endIndex; i++) {
            NameTypeStartPosition nameTypeStartPosition = nameTypeStartPositions.get(i);
            // 中间实体满足，形如("胸痛", "word") 过滤条件的规则
            this.addAll(set, this.insideMap.get("word").get(nameTypeStartPosition.getName()));
            // 中间实体满足，形如(";", "punc") 过滤条件的规则
            this.addAll(set, this.insideMap.get("type").get(nameTypeStartPosition.getType()));  // 没有逗号的
        }

        int entity_1_start = nameTypeStartPositions.get(startIndex).getStartPosition();
        int entity_2_start = nameTypeStartPositions.get(endIndex).getStartPosition();

        // 标点过滤
        String aPunc = null;
        for (int i=entity_1_start; i<entity_2_start;i++){
            aPunc = sentence.substring(i, i+1);
            if (this.punctuations.get(aPunc) != null)
                this.addAll(set, this.insideMap.get("punc").get(aPunc));
        }

        // 中文和英文逗号+属性 过滤
        String[] commas = {",", "，"};
        int commaIndex = 0;
        String commaPadType = null;  // 逗号拼接上类型
        for (String comma: commas) {
            commaIndex = sentence.indexOf(comma, entity_1_start + 1);  // 逗号位置
            while (commaIndex > -1 && commaIndex < entity_2_start) {
                commaIndex = sentence.indexOf(comma, commaIndex + 1);  // 下一个逗号
                for (int i = startIndex; i <= endIndex; i++) {  // 每个逗号与后面的所有实体都匹配一次
                    NameTypeStartPosition nameTypeStartPosition = nameTypeStartPositions.get(i);
                    if (nameTypeStartPosition.getStartPosition() > commaIndex) {
                        commaPadType = "," + nameTypeStartPosition.getType();
                        this.addAll(set, this.insideMap.get("typePunctuation").get(commaPadType));
                    }

                }
            }

        }

        this.retainAll(remainUuids, set);  // 求交集，同事中间实体相关的过滤条件，且不不满足例外情况

//        for (FilterRule rule: this.filterRules) {
//            if (remainUuids.contains(rule.getUuid()))
//                System.out.println(rule);
//
//        }

        return remainUuids.size() > 0;  // 还有规则满足，则过滤

    }

    /**
     * 求差集，避免null和空集
     *
     * @param basicSet
     * @param set
     */
    private void removeAll(Set<Integer> basicSet, Set<Integer> set) {
        if (set != null && set.size() > 0)
            basicSet.removeAll(set);
    }

    /**
     * 求交集，避免null和空集
     *
     * @param basicSet
     * @param set
     */
    private void addAll(Set<Integer> basicSet, Set<Integer> set) {
        if (set != null && set.size() > 0)
            basicSet.addAll(set);
    }

    /**
     * 求并集，避免null和空集
     *
     * @param basicSet
     * @param set
     */
    private void retainAll(Set<Integer> basicSet, Set<Integer> set) {
        if (set != null && set.size() > 0)
            basicSet.retainAll(set);
    }

    /**
     * 检查并移除
     *
     * @param sentence 句子
     * @param triads 三元组列表
     */
    public void checkAndRemove(String sentence, List<Triad> triads) {
        List<NameTypeStartPosition> nameTypeStartPositions = this.getSortedNameTypeByPosition(triads);
        Map<Integer, Integer> startPositionToIndexMap = new HashMap<>();
        for (int i = 0; i < nameTypeStartPositions.size(); i++)
            startPositionToIndexMap.put(nameTypeStartPositions.get(i).getStartPosition(), i);

        Iterator<Triad> it = triads.iterator();
        while (it.hasNext()) {  // 遍历三元组，移除满足过滤规则的
            Triad triad = it.next();
            int startIndex = startPositionToIndexMap.get(triad.getL_1().getStartPosition());
            int endIndex = startPositionToIndexMap.get(triad.getL_2().getStartPosition());
            if (isRemove(nameTypeStartPositions, startIndex, endIndex, sentence)) {
                it.remove();
            }
        }
    }
}
