import matplotlib.pyplot as plt
import networkx as nx
import mpld3

from clickinfo import ClickInfo
from highlight import Highlight

## Creates a networkx graph from a graph dictionary
# @param	data 				A dictionary representing data generated by @link <directed_graph> or @link <undreicted_graph>
# @param	host 				A hostname to prepend to each URL
# @param	minimum_weight		The minimum weight for an edge to be added to the graph
# @param    directed            If True, the returned graph will be a networkx.DiGraph()
# @return						A networkx graph object
def create_graph(data, host="https://en.wikipedia.org", minimum_weight=1, directed=False):
	if directed:
		graph = nx.DiGraph()
	else:
		graph = nx.Graph()
	for article, article_info in data.items():
		graph.add_node(article, url=(host + article_info["url"]))

	for article, article_info in data.items():
		for link, num_links in article_info["edges"].items():
			if num_links >= minimum_weight:
				graph.add_edge(article, link, weight=num_links)

	return graph

## Creates an inline mpld3 interactive networkx graph in Jupyter Notebook from a graph
# @param	graph 				The networkx graph to display
# @param	pos					An optional layout for graph nodes. If not specified, spring_layout will be used
# @param	cmap 				A colormap for graph nodes. Nodes are color mapped to node degree
# @param	edge_cmap			A colormap for graph edges. Edges are color mapped to edge weights
# @param	node_size_factor	A node size factor. Node sizes are a product of the size factor and the node's degree
# @return                       An HTML string of the resulting ineteractive graph
def make_interactive_graph(graph, pos=None, cmap=plt.cm.viridis, edge_cmap=plt.cm.Reds, node_size_factor=5):
	fig = plt.figure(1, figsize=(8, 6), dpi=100)
	ax = plt.Axes(fig, [0., 0., 1., 1.])
	ax.set_axis_off()
	fig.add_axes(ax)

	weights = [ d["weight"] for (u, v, d) in graph.edges(data=True)]
	degrees = nx.degree(graph)
	sizes = [ degrees[name] * node_size_factor for name in graph.nodes() ]
	urls = [ d["url"] for u, d in graph.nodes(data=True) ]

	if pos is None:
		pos = nx.spring_layout(graph)

	nodes = nx.draw_networkx_nodes(graph, pos=pos, node_size=sizes, node_color=sizes, cmap=cmap, alpha=0.3)
	edges = nx.draw_networkx_edges(graph, pos=pos, edge_color=weights, edge_cmap=edge_cmap, arrows=False, alpha=0.3)

	tip_div = u"<div style='background: #FFF; border-style: solid; border-width: 1px; padding: 2px;'>{}</div>"

	html_labels = list()
	for node in graph.nodes():
		neighbors = graph[node]
		neighbor_list = sorted([(neighbor, graph[node][neighbor]["weight"]) for neighbor in graph[node]], key=lambda tup: tup[1], reverse=True)[:3]
		list_label = u"<br />".join([u"{} ({})".format(neighbor[0], neighbor[1]) for neighbor in neighbor_list])
		html_labels.append( tip_div.format(u"<strong>{}</strong><br/><i>{}</i>".format(node, list_label)))

	line_labels = [ tip_div.format(u"<strong>{}</strong> and <strong>{}</strong> <i>({})</i>".format(u, v, d["weight"])) for (u, v, d) in graph.edges(data=True) ]

	tooltips = mpld3.plugins.PointHTMLTooltip(nodes, labels=html_labels, voffset=-10, hoffset=10)
	linetips = mpld3.plugins.PointHTMLTooltip(edges, labels=line_labels, voffset=-5, hoffset=10)
	mpld3.plugins.connect(fig, ClickInfo(nodes, urls))
	mpld3.plugins.connect(fig, Highlight(nodes))
	mpld3.plugins.connect(fig, Highlight(edges))
	mpld3.plugins.connect(fig, tooltips)
	mpld3.plugins.connect(fig, linetips)
	mpld3.enable_notebook()
	return mpld3.fig_to_html(fig)

## Saves a high resolution non-interactive graph, similar to @link <make_interactive_graph>
# @param	graph 				The networkx graph to display
# @param	pos					An optional layout for graph nodes. If not specified, spring_layout will be used
# @param	cmap 				A colormap for graph nodes. Nodes are color mapped to node degree
# @param	edge_cmap			A colormap for graph edges. Edges are color mapped to edge weights
# @param    width               Image width, in inches (default is 8)
# @param    height              Image height, in inches (default is 6)
# @param	node_size_factor	A node size factor. Node sizes are a product of the size factor and the node's degree
# @param	dpi 				Linear DPI of the graph. The graph is 8 inches wide and 6 inches tall
# @param	font_size			A font size for node labels
# @param	output_file			A filename for the output image
def save_big_graph(graph, pos=None, cmap=plt.cm.viridis, edge_cmap=plt.cm.Reds, width=8, height=6, dpi=600, font_size=1, node_size_factor=5, output_file="network.png"):
	import matplotlib
	weights = [ d["weight"] for (u, v, d) in graph.edges(data=True)]
	degrees = nx.degree(graph)
	sizes = [ degrees[name] * node_size_factor for name in graph.nodes() ]
	urls = [ d["url"] for u, d in graph.nodes(data=True) ]

	print "Rendering hi-resolution image..."

	fig = plt.figure(1, figsize=(height, width), dpi=dpi)
	ax = plt.Axes(fig, [0., 0., 1., 1.])
	ax.set_axis_off()
	fig.add_axes(ax)

	if pos is None:
		pos = nx.spring_layout(graph)

	nx.draw_networkx(graph, \
					 pos=pos, \
					 arrows=False, \
					 node_size=sizes, \
					 node_color=sizes, \
					 edge_color=weights, \
					 cmap=plt.cm.viridis, \
					 edge_cmap=plt.cm.Reds, \
					 font_size=font_size, \
					 alpha=0.3)

	print "Saving hi-resolution image to " + output_file + "..."

	fig.savefig(output_file)
