import networkx as nx
import matplotlib.pyplot as plt
from random import randint
import numpy as np
import matplotlib
import matplotlib.gridspec as gridspec
from matplotlib.ticker import NullFormatter

nullfmt = NullFormatter()  # Keine Labels im Netzwerkplot

#Analytische Verteilungsfunktion des exponentiellen Netzwerkes fuer grosse N (2**(-k))
def P(kmin,kmax):
    ergebnis = []
    for k in range(kmin,kmax):
      ergebnis.append(2**(-k))
    return np.array(ergebnis)

#plot settings
params = {
    'figure.figsize'    : [5, 7.2],
    'text.usetex'       : True,
}
matplotlib.rcParams.update(params) 

#########################################################################################
#Beginn des eigentlichen Python Programms "Komplexes, zeitlich anwachsendes Netzwerk"
######################################################################

#Allgemeine Festlegungen der globalen Parameter des Netzwerks 

av_deg = [] #Erzeugung einer Liste zum Speichern der gemittelten Verteilungsfunktion

#Erzeugung des Netzwerkes zum Anfangszeitpunkt t=0
G = nx.Graph()
NKn = 2 #Zwei Knoten am Anfang
G.add_nodes_from([0,1])
G.add_edge(0,1)

#Plot-Grid
plt.figure(0)
gs = gridspec.GridSpec(2, 1, height_ratios = [1,2.2], hspace = 0.1)
ax1 = plt.subplot(gs[0])
ax2 = plt.subplot(gs[1])

######################################################################Zeitliches Anwachsen der Netzwerkknoten
#Anfang der Schleife der zeitlichen Erzeugung des Netzwerks ( Nit Iterations-Zeit-Endpunkt )
Nit = 70
for it in range(1,Nit):
    #Hinzufuegen eines weiteren Knotens zum existierenden Netzwerk
    G.add_node(NKn)

    #Erzeugung einer zufaelligen Kante vom neuen Knoten zum existierenden Netzwerk
    G.add_edge(NKn,randint(0, NKn - 1))

    #Liste der Knotengrade
    degree_sequence = []
    for n in G.nodes():
        degree_sequence.append(G.degree(n))
    degree_sequence = sorted(degree_sequence)
    maxk=np.max(degree_sequence)

    #Erzeugung des Bildes der Verteilungsfunktion der Knotengrade P(k) (analytisch,simulativ)
    ax1.plot(range(int(maxk+2)), P(0,int(maxk+2)), linewidth = 1, linestyle = '-', c = "black")
    ax1.hist(degree_sequence, bins = range(int(maxk+2)), density = 1, align = "left", histtype = 'bar', color = "blue", alpha = 0.5)

    #Achsenbeschriftung
    ax1.set_ylabel(r'$\rm P(k)$')
    ax2.yaxis.set_major_formatter(nullfmt)
    ax2.xaxis.set_major_formatter(nullfmt)
    ax1.set_xlim(1, maxk + 1)
    ax1.set_ylim(0, 0.6)

    #Erzeugung des Netzwerk-Bildes
    node_size = 150
    node_alpha = 0.3
    node_color = "red"
    edge_thickness = 0.4
    edge_alpha = 0.7
    edge_color = "blue"
    node_text_size = 9
    text_font = "sans-serif"
    graph_pos = nx.shell_layout(G)
    nx.draw_networkx_nodes(G,graph_pos,node_size=node_size,alpha=node_alpha, node_color=node_color)
    nx.draw_networkx_edges(G,graph_pos,width=edge_thickness, alpha=edge_alpha,edge_color=edge_color)
    nx.draw_networkx_labels(G, graph_pos,font_size=node_text_size,font_family=text_font)

    # Plotten der Netzwerkeigenschaften in das Bild
    textstr = r'$N='+str(NKn+1)+', d='+str(nx.diameter(G))+', k_{max}='+str(maxk)+'$'
    props = dict(boxstyle='round', facecolor='white', alpha=0.92)
    plt.text(0, -1.46, textstr, fontsize=12, verticalalignment='bottom', horizontalalignment='center', bbox=props)

    #Speicherung des Bildes als .png Datei
    saveFig="./img-" + "{:0>3d}".format(it) + ".png"
    plt.savefig(saveFig, dpi=200,bbox_inches="tight",pad_inches=0.05,format="png")
    # Die einzelnen Bilder koennen dann in eine Animation zusammengefuegt werden.
    # z.B. mit folgende Shell-Befehl: ffmpeg -framerate 3 -i './img-%03d.png' network.mp4

    #Erhoehung der Knotenanzahl um eins
    NKn = NKn + 1

    #Loeschen des Bildes
    ax1.clear()
    ax2.clear()

  #Auflistung einiger das Netzwerk bestimmender Groessen
#    print "######### number of nodes" , NKn
#    print "average_clustering:" , nx.average_clustering(G)
#    print "clustering:" , list(nx.clustering(G).values())
#    print "triangles:" , list(nx.triangles(G).values())
#    print "degree:" , list(nx.degree(G))
#    print "shortest_path from 0 to NKn-1:" , nx.shortest_path(G,0,NKn-1)
#    print "radius:" , nx.radius(G)
#    print "diameter:" , nx.diameter(G)
#    print "eccentricity:" , list(nx.eccentricity(G).values())
#    print "center:" , nx.center(G)
#    print "periphery:" , nx.periphery(G)
#    print "density:" , nx.density(G)
#    print "neighbors node 0:" , list(G.neighbors(0))
#    print "######### ######### #########"
