package org.diagbot.common.push.naivebayes.core;

import org.algorithm.core.AlgorithmExecutor;
import org.diagbot.common.push.cache.ApplicationCacheUtil;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Description:
 * @Author: HUJING
 * @Date: 2019/10/11 14:25
 */
public class AlgorithmNaiveBayesExecutor extends AlgorithmExecutor {
    private double e = Math.E;
    private static double unknownProbWithRelevant = -2; //已知有关，但未在病历中统计出来的特征
    private static double unknownProbWithoutRelevant = -6;  //无关事件间的共现概率
    private static double denominator = 0.00;

    public Map<String, Float> execute(Map<String, Map<String, String>> inputs) {
        return softmax(probCalc(inputs));
    }

    public Map<String, Float> probCalc(Map<String, Map<String, String>> inputs) {
        Map<String, Map<String, Float>> doc_feature_naivebayes_prob_map = ApplicationCacheUtil.getDoc_feature_naivebayes_prob_map();
        Map<String, Map<String, Float>> relevant_feature_map = ApplicationCacheUtil.getRelevant_feature_map();
        Map<String, Float> naivebayesResult = new HashMap<>();
        for (Map.Entry<String, Map<String, Float>> naivebayesProb : doc_feature_naivebayes_prob_map.entrySet()) {
            float sum = 0.00f;
            int i = 1;
            for (String input : inputs.keySet()) {
                //先验概率表里有该特征，就使用该特征的先验概率
                if (naivebayesProb.getValue().containsKey(input)) {
                    sum += Math.log10(naivebayesProb.getValue().get(input));
                } else if (relevant_feature_map.get(naivebayesProb.getKey()) != null &&
                        relevant_feature_map.get(naivebayesProb.getKey()).containsKey(input)) {
                    //先验概率表里没有该特征 但 关联规则表里有该特征，则平滑处理(默认此时先验概率为10^-2)
                    sum += unknownProbWithRelevant;
                } else {
                    sum += unknownProbWithoutRelevant;
                }

                if (i == inputs.size()) {
                    sum += Math.log10(naivebayesProb.getValue().get("priorProb"));
                    naivebayesResult.put(naivebayesProb.getKey(), sum);
                }
                i++;
            }
        }
//        naivebayesResult = sortMap(naivebayesResult);
        return naivebayesResult;
    }

    private Map<String, Float> softmax(Map<String, Float> naivebayesResultMap) {
        Map<String, Float> softmaxResult = new HashMap<>();
        calaDenominator(naivebayesResultMap);

        for (Map.Entry<String, Float> naivebayesResult : naivebayesResultMap.entrySet()) {
            softmaxResult.put(naivebayesResult.getKey(), (float) (Math.pow(this.e, naivebayesResult.getValue()) / denominator));
        }

        softmaxResult = sortMap(softmaxResult);
        return softmaxResult;
    }

    private void calaDenominator(Map<String, Float> naivebayesResultMap) {
        if (denominator == 0) {
            for (Map.Entry<String, Float> naivebayesResult : naivebayesResultMap.entrySet()) {
                //计算softmax算法分母
                denominator += Math.pow(this.e, naivebayesResult.getValue());
            }
        }
    }

    public Map<String, Float> sortMap(Map<String, Float> ResultMap) {
        ArrayList<Map.Entry<String, Float>> softmaxResultList = new ArrayList<>(ResultMap.entrySet());
        softmaxResultList.sort(new Comparator<Map.Entry<String, Float>>() {
            @Override
            public int compare(Map.Entry<String, Float> o1, Map.Entry<String, Float> o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });
        ResultMap = new LinkedHashMap<>();
        for (Map.Entry<String, Float> softmaxResultMap : softmaxResultList) {
            ResultMap.put(softmaxResultMap.getKey(), softmaxResultMap.getValue());
        }
        return ResultMap;
    }
}
