package misc;

import graph.Graph;
import generator.GraphGenerator;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.TreeMap;

public class ExtToolsDriver {
	
	public static ArrayList<String> partitionGraph(Graph g, int partitions) {
		String target = Common.TMP_DIR + "g" + g.hashCode();
		ArrayList<String> parts = null;
		try {
			IO.writeGraphMetis(target, g);
			// Runtime
			Runtime run = Runtime.getRuntime() ;
			Process pr = run.exec(Common.METIS_EX + " " + target + " " + partitions) ;
			pr.waitFor();
			parts = IO.readPartitioningMetis( target + ".part." + partitions);
		} catch (Exception e) {
			System.err.print("Could not run METIS on graph\n");
			e.printStackTrace();
			System.exit(1);
		}
		return parts;
	}
	
	public static ArrayList<String> partitionGraph(Graph g, int partitions, int levels) {
		String target = Common.TMP_DIR + "g" + g.hashCode();
		ArrayList<String> parts, parts_prev = null;
		try {	
			parts_prev = new ArrayList<String>();
			parts = new ArrayList<String>();
			for(int i = 0; i < g.getn(); i++) {
				parts.add(""); 
				parts_prev.add("0");
			}
			
			ArrayList<Integer> nodes = new ArrayList<Integer>();
			ArrayList<String> pl = new ArrayList<String>();
			HashSet<String> clusts = new HashSet<String>();
			Graph sg = null;
			for (int l=0; l < levels; l++) {
				clusts.clear();
				// collect all cluster labels
				for (int i=0; i < parts_prev.size(); i++) 
					clusts.add(parts_prev.get(i));
				
				for (String clust: clusts) {
					nodes.clear();
					for(int i = 0 ; i < parts_prev.size(); i++) {
						if(parts_prev.get(i).equals(clust)) nodes.add(i);
					}
					sg = g.inducedSubgraph(nodes);
					
					IO.writeGraphMetis(target, sg);
					// Runtime
					Runtime run = Runtime.getRuntime() ;
					Process pr = run.exec(Common.METIS_EX + " " + target + " " + partitions) ;
					pr.waitFor();
					pl.clear();
					pl = IO.readPartitioningMetis( target + ".part." + partitions);
					for(int i = 0; i < nodes.size(); i++) {
						assert(parts.get(nodes.get(i)).equals("")):"assigning a node from another partition";
						parts.set(nodes.get(i),parts_prev.get(nodes.get(i)) + Common.SEP + pl.get(i));
					}
				}
				for(int i = 0; i < g.getn(); i++) {
					parts_prev.set(i, parts.get(i));
					parts.set(i,""); 
				}
			}
		} catch (Exception e) {
			System.err.print("Could not run METIS on graph\n");
			e.printStackTrace();
			System.exit(1);
		}
		return parts_prev;
	}
	public static HashMap<Integer,Integer> partitionHyperGraph(Graph g, int partitions) {
		String target = Common.TMP_DIR + "hg" + g.hashCode();
		HashMap<Integer,Integer> pp = null;
		try {
			IO.writeHyperGraphHMetis(target, g, "11");
			// Runtime
			Runtime hrun = Runtime.getRuntime() ;
			//Process hpr = hrun.exec(Common.SHMETIS_EX + " " + target + " " + partitions + " " + Common.HGRAPH_BALLANCE) ;
			Process hpr = hrun.exec(Common.HMETIS2_EX + " " + target + " " + partitions) ;
			hpr.waitFor();
			pp = IO.readPartitioningHMetis(target + ".part." + partitions, g);
		} catch (Exception e) {
			System.err.print("Error Running HMETIS:\n" + Common.HMETIS2_EX + " " + target + " " + partitions + " " + Common.HGRAPH_BALLANCE + "\n");
			e.printStackTrace();
			System.exit(1);
		}
		return pp;
	}
	
	public static void plotHist(TreeMap<Double,Integer> h){
		try {
			IO.writeHist(h, Common.TMP_DIR + "hist");
			Runtime run = Runtime.getRuntime() ;
			if (Common.enablePlotting) {
				System.err.print(Common.PLOT_DIR + "plothist");
				Process pr = run.exec(Common.PLOT_DIR + "plothist");
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void plotVector(double[] ds) {
		plotVector(ds, "_");
	}
	
	public static void plotVector(double[] ds, String s) {
		try {
			IO.writeVector(ds, Common.TMP_DIR + "vec" + s);
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				Process pr = run.exec(Common.PLOT_DIR + "plot_vector " +  Common.TMP_DIR + "vec" + s );
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void plotVector(ArrayList<Double> ds, String s) {
		double[] d = new double[ds.size()];
		for(int i=0;i<d.length;i++) d[i] = ds.get(i);
		plotVector(d,s);
	}
	
	public static void plotDistribution(ComparableLabeledValue[] tm) {
		try {
			IO.writeDist(tm, Common.TMP_DIR + "dist");
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				Process pr = run.exec(Common.PLOT_DIR + "plot_dist " +  Common.TMP_DIR + "dist" );
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
		
	}
	
	public static void plotHists( TreeMap<String,TreeMap<Double,Integer>> hs){
		try {
			String hists = "";
			for (String s: hs.keySet()) {
				IO.writeHistNormalize(hs.get(s), Common.TMP_DIR + s);
				hists += " " + Common.TMP_DIR + s;
			}
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				Process pr = run.exec(Common.PLOT_DIR + "plotallhists " + hists);
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void plotClusts(TreeMap<String, TreeMap<Double, Integer>> hs) {
		try {
			String vals = "";
			for (String s: hs.keySet()) {
				IO.writeValues(hs.get(s), Common.TMP_DIR + s);
				vals += " " + Common.TMP_DIR + s;
			}
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				Process pr = run.exec(Common.PLOT_DIR + "plotallvals " + vals);
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void plotAllBounds(ArrayList<String> b) {
		try {
			IO.writeStrings(b, Common.TMP_DIR + "strings");
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				Process pr = run.exec(Common.PLOT_DIR + "plotallbounds " + Common.TMP_DIR + "strings");
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void plotHeuristics(ArrayList<String> quality,
			ArrayList<String> time, String fprefix) {
		try {
			String qfile = fprefix + ".q";
			String tfile = fprefix + ".t";
			IO.writeStrings(quality, Common.RES_DIR + qfile);
			IO.writeStrings(time, Common.RES_DIR + tfile);
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				Process pr = run.exec(Common.PLOT_DIR + "plot_heuristic " + Common.RES_DIR + qfile);
				pr.waitFor();
				pr = run.exec(Common.PLOT_DIR + "plot_heuristic " + Common.RES_DIR + tfile);
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
		
	}
	
	public static void plotBoundTightness(ArrayList<String> quality,
			ArrayList<String> time, String fprefix) {
		try {
			String qfile = fprefix + ".q";
			IO.writeStrings(quality, Common.RES_DIR + qfile);
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				Process pr = run.exec(Common.PLOT_DIR + "plot_boundtightness " + Common.RES_DIR + qfile);
				pr.waitFor();
			}
			if (null != time) {
				String tfile = fprefix + ".t";	
				IO.writeStrings(time, Common.RES_DIR + tfile);
				if (Common.enablePlotting) {
					Runtime run = Runtime.getRuntime() ;
					Process pr = run.exec(Common.PLOT_DIR + "plot_boundtightness " + Common.RES_DIR + tfile);
					pr.waitFor();
				}
			}
		} catch (Exception e) {
			System.err.print("Error Bound Tightness\n");
			e.printStackTrace();
			System.exit(1);
		}
		
	}
	
	public static void plotIntersvals(HashSet<String> prunedUbAl, int maxt, String suffix) {
		try {
			String intf = "intervals" + suffix;
			IO.writeIntervals(prunedUbAl, Common.RES_DIR + intf);
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				Process pr = run.exec(Common.PLOT_DIR + "plot_intervals " + Common.RES_DIR + intf + " " + maxt);
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	
	// used to plot 3d points. not so useful
	public static void plotScatter3D(TreeMap<String,Integer> h){
		try {
			IO.write2DScatter(h, Common.TMP_DIR + "2dhist");
			Runtime run = Runtime.getRuntime() ;
			if (Common.enablePlotting) {
				System.err.print(Common.PLOT_DIR + "plot2dhist");
				Process pr = run.exec(Common.PLOT_DIR + "plot2dhist");
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	// plotting frequency distributions
	
	public static void plotValuesFrequencyDistribution(int[] values, String Xlabel, String YLabel) {
		TreeMap<Double,Double> h = Common.counts(values);
		plotScatter(h,Xlabel,YLabel);
	}
	
	public static void plotValuesFrequencyDistribution(double[] values, String Xlabel, String YLabel) {
		TreeMap<Double,Double> h = Common.counts(values);
		plotScatter(h,Xlabel,YLabel);
	}
	
	public static void plotScatter(TreeMap<Double,Double> h, String XLabel, String YLabel){
		try {
			IO.writeScatterXYLabels(h, XLabel, YLabel, Common.TMP_DIR + "scatter");
			Runtime run = Runtime.getRuntime() ;
			if (Common.enablePlotting) {
				System.err.print(Common.PLOT_DIR + "plot_scatter " + Common.TMP_DIR + "scatter");
				Process pr = run.exec(Common.PLOT_DIR + "plot_scatter " + Common.TMP_DIR + "scatter");
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error plotting a scatter\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void showImage(String fn) {
		if (!Common.enablePlotting) return;
		try {
			Runtime run = Runtime.getRuntime() ;
			//System.err.print("Killing "+process + "\n");
			Process pr = run.exec("eog " + fn + " &");
		} catch (Exception e) {
			System.err.print("Error showing image " + fn + "\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void kill(String process) {
		if (!Common.enablePlotting) return;
		try {
			Runtime run = Runtime.getRuntime() ;
			//System.err.print("Killing "+process + "\n");
			Process pr = run.exec("killall -9 " + process);
		} catch (Exception e) {
			System.err.print("Error Killing " + process + "\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void plotCurves(String[] x, String[] curve_names,
			double[][] y, String xlab, String ylab, String name) {
		plotCurves(x, curve_names, y, xlab, ylab, name, Common.RES_DIR);
	}
	
	public static void plotCurves(String[] x, String[] curve_names,
			double[][] y, String xlab, String ylab, String name, String dir) {
		for (int i = 0; i < curve_names.length; i++) System.err.print("\t" + curve_names[i]);
		System.err.print("\n");
		for (int i = 0 ; i < y.length; i++) {
			System.err.print(x[i]);
			for (int j = 0 ; j < y[i].length; j++) System.err.print("\t" + y[i][j]);
			System.err.print("\n");
		}
		try {
			// print to file
			IO.writeCurves(x, curve_names, y, xlab, ylab, dir + name);
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				System.err.print(Common.PLOT_DIR + "plot_traces " +  dir + name );
				Process pr = run.exec(Common.PLOT_DIR + "plot_traces " +  dir + name);
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			//System.exit(1);
		}
	}
	
	public static void plotStacks(String[] x, String[] comp_names,
			double[][] y, String xlab, String ylab, String name) {
		plotStacks(x, comp_names, y, xlab, ylab, name, false);
	}
	
	public static void plotStacks(String[] x, String[] comp_names,
			double[][] y, String xlab, String ylab, String name, boolean normalize) {
		for (int i = 0; i < comp_names.length; i++) System.err.print("\t" + comp_names[i]);
		System.err.print("\n");
		for (int i = 0 ; i < y.length; i++) {
			System.err.print(x[i]);
			for (int j = 0 ; j < y[i].length; j++) System.err.print("\t" + y[i][j]);
			System.err.print("\n");
		}
		try {
			// print to file
			IO.writeCurves(x, comp_names, y, xlab, ylab, Common.RES_DIR + name, true);
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				//System.err.print(Common.PLOT_DIR + "plot_stacked " +  Common.RES_DIR + name );
				Process pr = run.exec(Common.PLOT_DIR + "plot_stacked " +  Common.RES_DIR + name);
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
		
	}

	
	public static void plotMatrix(double[][] matrix, double norm, 
										double tresh, int yupto) {	
		try {
			// print to file
			IO.writeMatrix(matrix, norm, tresh, Common.TMP_DIR + "mat", yupto);
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				//System.err.print(Common.PLOT_DIR + "plot_matrix " +  Common.TMP_DIR + "mat " + (matrix.length+0.5) );
				Process pr = run.exec(Common.PLOT_DIR + "plot_matrix " +  Common.TMP_DIR + "mat " +  (matrix.length+0.5) + " " +(yupto+1.5));
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void showInGoogleEarth(String[] points) {
		try {
			// print to file
			IO.writeStrings(points,Common.TMP_DIR + "points");
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				//System.err.print(Common.PLOT_DIR + "plot_matrix " +  Common.TMP_DIR + "mat " + (matrix.length+0.5) );
				Process pr = run.exec(Common.PLOT_DIR + "plot_points_google " +  Common.TMP_DIR + "points");
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error Plotting a histogram\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void saveAndShowGraphViz(String graphrep) {
		try {
			// print to file
			IO.write(graphrep,Common.TMP_DIR + "graph");
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				//System.err.print(Common.PLOT_DIR + "plot_matrix " +  Common.TMP_DIR + "mat " + (matrix.length+0.5) );
				Process pr = run.exec(Common.PLOT_DIR + "graphviz " +  Common.TMP_DIR + "graph");
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error showing graphviz graph\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void saveGraphVizPng(String graphrep, String fn) {
		try {
			// print to file
			IO.write(graphrep,fn);
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				//System.err.print(Common.PLOT_DIR + "plot_matrix " +  Common.TMP_DIR + "mat " + (matrix.length+0.5) );
				Process pr = run.exec(Common.PLOT_DIR + "graphviz " +  fn);
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error showing graphviz graph\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void showGraphViz() {
		showGraphViz(Common.TMP_DIR, "graph");
	}
	
	public static void showGraphViz(String fn) {
		showImage(fn);
	}
	
	public static void showGraphViz(String dir, String fn) {
		showImage(dir + fn + ".png");
	}
	
	public static void saveGraphViz(String graphrep) {
		saveGraphViz(graphrep, Common.TMP_DIR + "graph");
	}
	
	public static void saveGraphViz(String graphrep, String fn) {
		try {
			// print to file
			IO.write(graphrep, fn);
			if (Common.enablePlotting) {
				Runtime run = Runtime.getRuntime() ;
				//System.err.print(Common.PLOT_DIR + "plot_matrix " +  Common.TMP_DIR + "mat " + (matrix.length+0.5) );
				Process pr = run.exec(Common.PLOT_DIR + "savegraphviz " +  fn);
				pr.waitFor();
			}
		} catch (Exception e) {
			System.err.print("Error saving graphviz graph\n");
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public static void main(String[] argv) {
		
		Graph g = GraphGenerator.generateScaleFree(100);
		
		//g = GraphGenerator.generateRandom(1000, 20000);
		
		g.aggDegreeInWn();
		plotValuesFrequencyDistribution(g.wn, "Degree", "Frequency");
		
		// first probability is persist, second seed
//		g = GraphGenerator.getTestGraphWithStarFurtherMerge();
//		ArrayList<String> s1 = partitionGraph(g,2,1);
//		ArrayList<String> s2 = partitionGraph(g,2,2);
//		System.err.print(s1.toString() + "\n" + s2.toString() + "\n");
//		HashMap<Integer,Integer> s3 = partitionHyperGraph(g, 3);
//		System.err.print(s3.toString());
	}
}
