package weka.clusterers.forMetisMQI.util;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.swing.JPanel;

import org.apache.commons.collections15.Transformer;

import weka.clusterers.forMetisMQI.graph.Edge;
import weka.clusterers.forMetisMQI.graph.Node;
import weka.clusterers.forMetisMQI.graph.UndirectedGraph;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.KKLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;

public class Util {
	
	public static JPanel panelCluster(Graph<Node, Edge> g, Collection<Node> cluster) {
		Set<Node> setNodes = new HashSet<Node>();
		setNodes.addAll(cluster);
		Set<Set<Node>> clusters = new HashSet<Set<Node>>();
		clusters.add(setNodes);
		return panelClusters(g, clusters);
	}
	
	public static JPanel panelClusters(Graph<Node, Edge> g, Set<Set<Node>> clusters) {
		return panelClusters(g,clusters,new ToStringLabeller<Node>());
	}
	
	public static JPanel panelClusters(Graph<Node, Edge> g, Set<Set<Node>> clusters, Transformer<Node,String> transformer) {
		Layout<Node, Edge> layout = new KKLayout<Node, Edge>(g);
		layout.setSize(new Dimension(800, 600)); // sets the initial size of the space
		// The BasicVisualizationServer<V,E> is parameterized by the edge types
		BasicVisualizationServer<Node, Edge> vv = new BasicVisualizationServer<Node, Edge>(
				layout);

		class VertexPaintTransformer implements Transformer<Node, Paint> {
			Set<Set<Node>> clusters = null;
			Map<Set<Node>, Color> clustersColor = null;

			public Set<Node> getCluster(Node node) {
				Iterator<Set<Node>> clusterIterator = clusters.iterator();
				while (clusterIterator.hasNext()) {
					Set<Node> cluster = clusterIterator.next();
					if (cluster.contains(node))
						return cluster;
				}
				return null;
			}

			public VertexPaintTransformer(Set<Set<Node>> clusters) {
				this.clusters = clusters;
				clustersColor = new HashMap<Set<Node>, Color>(clusters.size());
				Iterator<Set<Node>> clusterIterator = clusters.iterator();
				while (clusterIterator.hasNext()) {
					Set<Node> cluster = clusterIterator.next();
					clustersColor.put(cluster, new Color(Random.instance()
							.nextInt(256), Random.instance().nextInt(256),
							Random.instance().nextInt(256)));
				}
			}

			public Paint transform(Node i) {
				Set<Node> cluster = getCluster(i);
				if (cluster == null)
					return Color.RED;
				else
					return clustersColor.get(getCluster(i));
			}
		}

		Transformer<Node, Paint> vertexPaint = new VertexPaintTransformer(
				clusters);
		vv.setPreferredSize(new Dimension(800, 600)); // Sets the viewing area
														// size
		vv.getRenderContext().setVertexLabelTransformer(transformer);
		vv.getRenderContext().setVertexFillPaintTransformer(vertexPaint);
		return vv;
	}
	
	public static JPanel panelGraph(Graph<Node, Edge> g){
		Layout<Node, Edge> layout = new FRLayout<Node, Edge>(g);
		layout.setSize(new Dimension(800,600)); // sets the initial size of the space
		// The BasicVisualizationServer<V,E> is parameterized by the edge types
		BasicVisualizationServer<Node,Edge> vv =
		new BasicVisualizationServer<Node,Edge>(layout);
		vv.setPreferredSize(new Dimension(800,600)); //Sets the viewing area size
		vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<Node>());
		vv.getRenderContext().setEdgeLabelTransformer(
				new Transformer<Edge, String>() {
					public String transform(Edge e) {
						return "";
					}
				});
		return vv;
	}
	
	public static JPanel panelFlowGraph(Graph<Node, Edge> g, Map<Edge, Number> edgeFlowMap){
		class EdgeTransformer implements Transformer<Edge,String> {
			Map<Edge,Number> edgeFlowMap = null;
			public String transform(Edge edge){
				return edgeFlowMap.get(edge) + "/" + edge.getCapacity();
			}
			public EdgeTransformer(Map<Edge,Number> edgeFlowMap) {
				this.edgeFlowMap = edgeFlowMap;
			}
		}
		Layout<Node, Edge> layout = new FRLayout<Node, Edge>(g);
		layout.setSize(new Dimension(800,600)); // sets the initial size of the space
		// The BasicVisualizationServer<V,E> is parameterized by the edge types
		BasicVisualizationServer<Node,Edge> vv =
		new BasicVisualizationServer<Node,Edge>(layout);
		vv.setPreferredSize(new Dimension(800,600)); //Sets the viewing area size
		vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<Node>());
		vv.getRenderContext().setEdgeLabelTransformer(new EdgeTransformer(edgeFlowMap));
		return vv;
	}
	
	static public JPanel panelContractedGraph(CoarserGraphElement cge, Collection<Node> cluster) {
		Set<Node> setNodes = new HashSet<Node>();
		setNodes.addAll(cluster);
		Set<Set<Node>> clusters = new HashSet<Set<Node>>();
		clusters.add(setNodes);
		return panelClusters(cge.getContracted(), clusters, new ContractedVertexTransformer(cge));
	}
	
	static public JPanel panelContractedGraph(CoarserGraphElement cge) {
		Layout<Node, Edge> layout = new KKLayout<Node, Edge>(cge.getContracted());
		layout.setSize(new Dimension(800,600)); // sets the initial size of the space
		// The BasicVisualizationServer<V,E> is parameterized by the edge types
		BasicVisualizationServer<Node,Edge> vv =
		new BasicVisualizationServer<Node,Edge>(layout);
		vv.setPreferredSize(new Dimension(800,600)); //Sets the viewing area size
		vv.getRenderContext().setVertexLabelTransformer(new ContractedVertexTransformer(cge));
		return vv;
	}

	
	/**
	 * Generates a small graph with 100 nodes and two big components.
	 * For testing purpose.
	 * @return the generated graph
	 */
	public UndirectedGraph generateGraph(){
		UndirectedGraph g = new UndirectedGraph();
		for (int i = 0; i < 50; i++) {
			g.addVertex(new Node(Integer.toString(i)));
		}
		for (int j = 0; j < 120; j++) {
			g.addEdge(new Edge(Integer.toString(j), 1, 1), new Node(Integer
					.toString(Random.instance().nextInt(50))), new Node(Integer
					.toString(Random.instance().nextInt(50))));
		}
		for (int i = 50; i < 100; i++) {
			g.addVertex(new Node(Integer.toString(i)));
		}
		for (int j = 120; j < 240; j++) {
			g.addEdge(new Edge(Integer.toString(j), 1, 1), new Node(Integer
					.toString(50 + Random.instance().nextInt(50))), new Node(
					Integer.toString(50 + Random.instance().nextInt(50))));
		}
		for (int j = 240; j < 250; j++) {
			g.addEdge(new Edge(Integer.toString(j), 1, 1), new Node(Integer
					.toString(50 + Random.instance().nextInt(50))), new Node(
					Integer.toString(Random.instance().nextInt(50))));
		}
		return g;
	}
}
