package com.apobates.forum.core.nlp.tag.impl.relate;

import com.apobates.forum.core.nlp.tag.AbstractTagRelateQueryExecutor;
import com.apobates.forum.core.nlp.tag.RelateWordStats;
import com.apobates.forum.core.nlp.tag.TagRelateResult;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 根据标签名称使用Jaccard方法来显示相关性 {@link https://segmentfault.com/a/1190000005270047}
 *
 * @author xiaofanku@live.cn
 * @since 20200206
 */
public final class TagRelateJaccard extends AbstractTagRelateQueryExecutor {
    private final Map<Long, Set<String>> relateTags;
    private final static Logger logger = LoggerFactory.getLogger(TagRelateJaccard.class);
    
    public TagRelateJaccard(Map<String, Integer> rawdata) {
        super(rawdata);
        this.relateTags=Collections.emptyMap();
        logger.info("[QR][JAC][INIT]param size: " + rawdata.size());
    }
    
    private TagRelateJaccard(Map<String, Integer> rawdata, Map<Long, Set<String>> result){
        super(rawdata);
        this.relateTags = Map.copyOf(result);
    }
    
    @Override
    public Stream<TagRelateResult> getResult(int showSize) {
        logger.info("[QR][JAC][RT]param Value: " + showSize);
        if (relateTags.isEmpty()) {
            return Stream.empty();
        }
        Stream<TagRelateResult> result = relateTags.entrySet().stream().map(entry->{
            long topicId = entry.getKey();
            // 交集/并集
            double similarity = calcJaccardSimilarity(entry.getValue());
            return new TagRelateResult(topicId, 1, similarity);
        }).sorted(Comparator.comparing(TagRelateResult::getSimilarity)).limit(showSize);
        return applyWithIndex(result);
    }
    
    @Override
    public TagRelateJaccard load(Collection<RelateWordStats> wordSource) {
        //分组
        Map<Long, Set<String>> data = wordSource.stream().collect(Collectors.groupingBy(RelateWordStats::getTopic, Collectors.mapping(RelateWordStats::getWord, Collectors.toSet())));
        return new TagRelateJaccard(super.getRawdata(), data);
    }
    //使用Jaccard 方法计算相似度
    private double calcJaccardSimilarity(Set<String> relate) {
        // 话题的标签名称
        final Set<String> source = super.getRawdata().keySet();
        Set<String> target = new HashSet<>();
        target.addAll(source);
        target.retainAll(relate);
        //
        int overlap = target.size();
        target.clear();
        target.addAll(source);
        target.addAll(relate);
        
        return overlap / Double.valueOf(target.size());
    }
}