# coding=utf-8


class Status(object):
    """
    To handle several data in one struct.

    Could be replaced by named tuple, but don't want to depend on python 2.6
    """
    node2com = {}
    total_weight = 0
    internals = {}
    degrees = {}
    gdegrees = {}
    nodelists = {}

    def __init__(self):
        self.node2com = dict([])
        self.total_weight = 0
        self.degrees = dict([])
        self.com2node = dict([])
        self.gdegrees = dict([])
        self.internals = dict([])
        self.loops = dict([])

    def __str__(self):
        return ("node2com : " + str(self.node2com) + " degrees : "
                + str(self.degrees) + " internals : " + str(self.internals)
                + " total_weight : " + str(self.total_weight) + "com2node:" + str(self.com2node))

    def copy(self):
        """Perform a deep copy of status"""
        new_status = Status()
        new_status.node2com = self.node2com.copy()
        new_status.internals = self.internals.copy()
        new_status.degrees = self.degrees.copy()
        new_status.gdegrees = self.gdegrees.copy()
        new_status.total_weight = self.total_weight
        new_status.com2node = self.com2node.copy()

    def init(self, graph, weight, part=None):
        """Initialize the status of a graph with every node in one community"""
        count = 0
        self.node2com = dict([])
        self.total_weight = 0
        self.degrees = dict([])
        self.gdegrees = dict([])
        self.internals = dict([])
        self.com2node = dict([])
        self.total_weight = graph.size(weight=weight)
        
        if part is None:
            for node in graph.nodes():
                self.node2com[node] = count
                self.com2node.setdefault(count, list()).append(node)
                
                deg = float(graph.degree(node, weight=weight))
                if deg < 0:
                    error = "Bad node degree ({})".format(deg)
                    raise ValueError(error)
                # self.degrees[count] = deg
                
                self.gdegrees[node] = deg
                edge_data = graph.get_edge_data(node, node, default={weight: 0})
                self.loops[node] = float(edge_data.get(weight, 1))
                self.internals[count] = self.loops[node]
                
                self.degrees[count] = [float(d) for d in list(zip(*graph.degree(self.com2node[count], weight=weight)))[1]]
                count += 1

        
        else:
            
            for node in graph.nodes():
                com = part[node]
                self.node2com[node] = com
                self.com2node.get(count, list()).append(node)

                deg = float(graph.degree(node, weight=weight))
                # self.degrees[com] = self.degrees.get(com, 0) + deg
                self.gdegrees[node] = deg
                
                inc = 0.
                for neighbor, datas in graph[node].items():
                    edge_weight = datas.get(weight, 1)
                    if edge_weight <= 0:
                        error = "Bad graph type ({})".format(type(graph))
                        raise ValueError(error)
                    if part[neighbor] == com:
                        if neighbor == node:
                            inc += float(edge_weight)
                        else:
                            inc += float(edge_weight) / 2.
                self.internals[com] = self.internals.get(com, 0) + inc
                self.degrees[count] = [float(d) for d in list(zip(*graph.degree(self.com2node[count], weight=weight)))[1]]
