from typing import Dict
from typing import List
import math


def best_broaders(supers_for_all_entities: Dict,
                  per_candidate_links_and_supers: List[Dict],
                  num_best: int = 5,
                  super_counts_field: str = "broader_counts",
                  doprint=False,
                  representativeness_threshold=0.1):
    """
    Returns the best matching super for a candidate class, according to a list of supers for entities in the class
    and entities in the whole corpus. If comparing to a taxonomy, a super is a broader.

    @param super_counts_field:
    @param super_counts: a dictionary that has, for every possible entity, the supers it belongs to
    @param per_candidate_links_and_supers:  a list of dictionaries, one per candidate. Fro each, at least
    two fields are expected "entities" containing the list of entities, and that given by super_counts_field
    which is, in turn, a dictionary whose keys are supers and whose values are the number of entities in that
    candidate having this broad
    @param num_best: maximum number of best matching supers to be returned
    @return: for every candidate class, the num_best best matching supers and their log odds ratio
    """
    result = []
    global_counts = dict()
    for ent, bros in supers_for_all_entities.items():
        for bro in bros:
            global_counts[bro] = global_counts.get(bro, 0) + 1

    onlytopmost = []
    for can in per_candidate_links_and_supers:

        # For this entity, the following dictionaries have an element for every possible super
        # Using notation from the paper
        # T_cc  : The number of entities narrower to a candidate which are tagged with NER typeT
        T_cc = {x: y for x, y in can[super_counts_field].items()
                if y > representativeness_threshold * len(can["entities"])}
        if len(T_cc) == 0:
            T_cc = {x: y for x, y in can[super_counts_field].items()}
        # T_w  :  is the number of entities in the wholecorpus tagged with  T
        T_w = {y: global_counts[y] for y in T_cc.keys()}
        # w : the total number of entities in the whole corpus
        w = float(len(supers_for_all_entities))
        # cc : the total number of entities in this candidate
        cc = float(len(can["entities"]))

        # dict of the form  super : log_odds
        log_odds_per_super = {x: math.log((T_cc[x] / cc) / (T_w[x] / w))
                              for x in T_cc.keys()}

        logslist = list(log_odds_per_super.items())
        logslist.sort(key=lambda x: x[1])
        logslist.reverse()

        maxbroads = min(len(logslist), num_best)
        logodds = []
        for bi in range(maxbroads):
            logodds.append({"candidatesbroader": logslist[bi][0],
                            "loggods": logslist[bi][1]})
        can["log_odds"] = logodds
        if doprint:
            print("\t\t---", ", ".join([str(x[1]) for x in logslist[:maxbroads]]))
        if len(logslist) > 0:
            onlytopmost.append(logslist[0][1])
            can["best_match_broader"] = logslist[0][0]
        else:
            onlytopmost.append(None)
            can["best_match_broader"] = None

    return onlytopmost