## high level interface for community detection algorithms
from .community_louvain import *
import os
try:
    from .NoRC import *
except:
    pass


def run_infomap(infile,
                multiplex=True,
                overlapping=False,
                binary="./infomap",
                verbose=True,
                iterations=1000):

    from subprocess import call
    if verbose:
        if multiplex:
            call([
                binary, infile, "out/", "-i multiplex",
                "-N " + str(iterations), "-z"
            ])
        else:
            if overlapping == True:
                call([
                    binary, infile, "out/", "-N " + str(iterations),
                    "--overlapping", "-z"
                ])
            else:
                call([binary, infile, "out/", "-N " + str(iterations), "-z"])
    else:
        if multiplex:
            call([
                binary, infile, "out/", "-i multiplex",
                "-N " + str(iterations), "-z", "--silent"
            ])
        else:
            if overlapping == True:
                call([
                    binary, infile, "out/", "-N " + str(iterations),
                    "--overlapping", "-z", "--silent"
                ])
            else:
                call([
                    binary, infile, "out/", "-N " + str(iterations), "-z",
                    "--silent"
                ])


def infomap_communities(graph,
                        binary="./infomap",
                        edgelist_file="./tmp/tmpedgelist.txt",
                        multiplex=False,
                        verbose=False,
                        overlapping=False,
                        iterations=200,
                        output="mapping"):

    ## check type of the network
    print("INFO: Infomap community detection in progress..")
    outstruct = []

    ### go through individual nodes first and enumerate them., also layers
    inverse_node_map = graph.serialize_to_edgelist(edgelist_file=edgelist_file,
                                                   multiplex=multiplex)
    ## run infomap
    run_infomap(edgelist_file,
                multiplex=multiplex,
                binary=binary,
                verbose=verbose,
                overlapping=overlapping,
                iterations=iterations)

    partition = parse_infomap("out/" +
                              edgelist_file.split("/")[-1].split(".")[0] +
                              ".tree")
    partition = {inverse_node_map[k]: v for k, v in partition.items()}
    non_mapped = set(list(graph.get_nodes())).difference(partition.keys())

    for x in non_mapped:
        partition[x] = 1

    import shutil
    shutil.rmtree("out", ignore_errors=False, onerror=None)
    shutil.rmtree("tmp", ignore_errors=False, onerror=None)

    if output == "mapping":
        return partition
    else:
        dx_hc = defaultdict(list)
        for a, b in partition.items():
            dx_hc[b].append(a)
        return dx_hc

    return partition


def parse_infomap(outfile):

    outmap = {}
    with open(outfile) as of:
        for line in of:
            parts = line.strip().split()
            try:
                module = parts[0].split(":")[0]
                node = parts[3]
                outmap[int(node)] = int(module)
            except:
                pass

    return outmap


def louvain_communities(network, output="mapping"):

    try:
        G = nx.Graph()
        for edge in network.core_network.edges():
            G.add_edge(edge[0], edge[1])
        network = G

    except Exception as es:
        print(es)

    partition = best_partition(network)
    if output == "partition":
        dx_hc = defaultdict(list)
        for a, b in partition.items():
            dx_hc[b].append(a)
        return dx_hc
    return partition


def NoRC_communities(
        network,
        verbose=True,
        clustering_scheme="kmeans",
        output="mapping",
        prob_threshold=0.001,
        parallel_step=8,
        community_range=[1, 3, 5, 7, 11, 20, 40, 50, 100, 200, 300],
        fine_range=3):

    try:
        network = network.core_network
    except:
        pass

    partition = NoRC_communities_main(network,
                                      verbose=True,
                                      clustering_scheme=clustering_scheme,
                                      prob_threshold=prob_threshold,
                                      parallel_step=parallel_step,
                                      community_range=community_range,
                                      fine_range=fine_range)

    if output == "mapping":
        ## todo
        return None
    else:
        return partition
