import networkx as nx
import matplotlib.pyplot as plt
from collections import Counter
import HyperGraph as hg
import warnings

# pip install networkx
# pip install matplotlib
## pip install pandas
## pip install community
## pip install mplleaflet
## pip install values

class SemanticLinking:

    def __init__(self):
        hg.classify()

    def _color_network(self, G):
        """Colors the network so that neighboring nodes all have distinct colors.

           Returns a dict keyed by color to a set of nodes with that color.
        """
        coloring = dict()  # color => set(node)
        colors = nx.coloring.greedy_color(G)
        for node, color in colors.items():
            if color in coloring:
                coloring[color].add(node)
            else:
                coloring[color] = set([node])
        return coloring


    def _labeling_complete(self, labeling, G):
        """Determines whether or not LPA is done.

           Label propagation is complete when all nodes have a label that is
           in the set of highest frequency labels amongst its neighbors.

           Nodes with no neighbors are considered complete.
        """
        return all(labeling[v] in self._most_frequent_labels(v, labeling, G)
                   for v in G if len(G[v]) > 0)


    def _most_frequent_labels(self, node, labeling, G):
        """Returns a set of all labels with maximum frequency in `labeling`.

           Input `labeling` should be a dict keyed by node to labels.
        """
        if not G[node]:
            # Nodes with no neighbors are themselves a community and are labeled
            # accordingly, hence the immediate if statement.
            return {labeling[node]}

        # Compute the frequencies of all neighbours of node
        freqs = Counter(labeling[q] for q in G[node])
        max_freq = max(freqs.values())
        return {label for label, freq in freqs.items() if freq == max_freq}


    def _update_label(self, node, labeling, G):
        """Updates the label of a node using the Prec-Max tie breaking algorithm

           The algorithm is explained in: 'Community Detection via Semi-Synchronous
           Label Propagation Algorithms' Cordasco and Gargano, 2011
        """
        high_labels = self._most_frequent_labels(node, labeling, G)
        if len(high_labels) == 1:
            labeling[node] = high_labels.pop()
        elif len(high_labels) > 1:
            # Prec-Max
            if labeling[node] not in high_labels:

             labeling[node] = max(high_labels)


    warnings.filterwarnings('ignore')




    #G = nx.DiGraph(directed=True)
    G = nx.MultiDiGraph(day="Stackoverflow")

    df_nodes = hg.clusterlabels
    destf_nodes = hg.destclusterlabel
    color_map = {1: '#f09494', 2: '#eebcbc', 3: '#72bbd0', 4: '#91f0a1', 5: '#629fff', 6: '#bcc2f2',
                 7: '#eebcbc', 8: '#f1f0c0', 9: '#d2ffe7', 10: '#caf3a6', 11: '#ffdf55', 12: '#ef77aa',
                 13: '#d6dcff', 14: '#d2f5f0'}
    i=0

    graphedge = []
    weigth = []
    sourcedestination = []
    source = []
    dest = []
    edge_width = []
    weight1 = []

    node_adjacencies = []
    labeling = {}

    def drawedges(self):

        """drawing edges in graph"""

        for drow in range(len(self.df_nodes)):
            for row in range(len(self.destf_nodes[drow])):
               self.G.add_edge(self.df_nodes[drow], self.destf_nodes[drow][row])

        for row in range(len(hg.labalvlues)):
            for row1 in range(len(hg.labalvlues)):
               self.weight1.append(self.G.number_of_edges(hg.labalvlues[row], hg.labalvlues[row1]))
               print("The number of coccurance from node ", hg.labalvlues[row],"to node ", hg.labalvlues[row1], ": ", self.weight1[row1])

        self.G.__setattr__('weight', self.weight1)

    def dolabeling(self):
        """label_propagation_communities(G) """


        coloring = self._color_network(self.G)
            # Create a unique label for each node in the graph
        labeling = {v: k for k, v in enumerate(self.G)}
        print("lable value: ", labeling.values())
        while not self._labeling_complete(labeling, self.G):
        # Update the labels of every node with the same color.
            print("lable value: ", labeling.values())
            for color, nodes in coloring.items():
              for n in nodes:
                self._update_label(n, labeling, self.G)
            for label in set(labeling.values()):
              print("lable value: ", labeling.values())
        self.labeling = labeling


    def findigneighbors(self):
        """ findig nodes' adjecencies"""
        node_text = []
        for node, adjacencies in enumerate(self.G.adjacency()):
            self.node_adjacencies.append(len(adjacencies[1]))
            node_text.append('# of connections: '+str(len(adjacencies[1])))

        self.G.color = self.node_adjacencies

    def result(self):
        plt.figure(figsize=(25, 25))
        options = {
            'with_labels': True,
            'font_weight': 'regular',
        }

        # colors = [color_map[G.node[node][1]] for node in G]
        # sizes = [G.node[node]['Timestamp'] * 10 for node in G]

        d = nx.degree_centrality(self.G)
        d_list = list(d.values())
        print("node centrality: ", d_list)
        print("node adjacencies: ", self.node_adjacencies)
        for row in range(len(self.weigth)):
            self.edge_width.append([])
            for drow in range(len(self.weigth[row])):
                self.edge_width[row].append(self.weigth[row][drow])
        node_size = [v * 80 for v in d.values()]  # setting node size based on node centrality
        edge_width = [row * 0.5 for row in self.weight1]

        print("Nodes' Degree:  ", nx.degree(self.G))
        print("Nodes' Betweeness  ", nx.edge_betweenness_centrality(self.G))
        print("Nodes' Betweeness-centrality:  ", nx.betweenness_centrality(self.G))

        """
        Using the spring layout : 
        - k controls the distance between the nodes and varies between 0 and 1
        - iterations is the number of times simulated annealing is run
        default k=0.1 and iterations=50
        """

        labels2 = {}

        for idx, edge in enumerate(self.G.edges):
            labels2[edge] = "s"

        pos_nodes = nx.spring_layout(self.G, k=0.25, iterations=50)


        nx.draw(self.G, pos_nodes, node_color=self.node_adjacencies, node_size=node_size, width=2, arrowstyle='->',
                arrowsize=10, weight=self.weight1, edge_color='gray', **options)

        edge_labels = nx.get_edge_attributes(self.G, 'weight')

        pos_attrs = {}
        for node, coords in pos_nodes.items():
            pos_attrs[node] = (coords[0], coords[1] + 0.02)
        nx.draw_networkx_edge_labels(self.G, pos_nodes, edge_labels=edge_labels, font_size=10, font_color='red')
        nx.draw_networkx_labels(self.G, pos_attrs, labels=self.labeling, font_size=10, font_color='red')

        ax = plt.gca()
        ax.collections[0].set_edgecolor("#555555")
        plt.show()


    def main(self):
        self.drawedges()
        self.dolabeling()
        self.findigneighbors()
        self.result()

linking = SemanticLinking()
linking.main()