package exp;
import generator.DynamicModel;
import generator.GraphGenerator;
import graph.Graph;
import graph.NodeWeightedTimeGraph;
import graph.Pattern;
import graph.SmoothPattern;
import graph.TimeGraph;
import graph.WeightedTimeGraph;

import index.IntervalBounds;

import java.io.*;
import java.util.BitSet;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.TreeSet;

import misc.Common;
import misc.Distribution;
import misc.IO;

// TODO(): these invocations run only with top down
// add the possibility to invoke with ILP

public class Meden {
	// call: java -jar meden.jar file start end
	public static void main(String[] args) throws IOException {
//		if (args.length !=4 || args.length !=2) 
//			System.err.print("Usage: java -jar meden.jar -run file start end\n OR" +
//					                 "java -jar meden.jar -convert file");
		String action = args[0];

		if (action.equals("-prep_pems_orig_speed")){
			String gf = args[1];
			String stations = args[2];
			String odir = args[3];
			TimeGraph tg = IO.readTGraphPems(gf,stations);
			int tt = tg.original_nodespeeds[0].length;
			// The originalnodespeeds is transposed nodes x times!!! 
			float[][] wsl = new float[tt][tg.getn()];
			for (int t = 0; t < tt; t++) {
				for (int i = 0; i < tg.getn(); i++) {
					wsl[t][i] = (float)tg.original_nodespeeds[i][t]; 
				}
			}
			NodeWeightedTimeGraph ntg = new NodeWeightedTimeGraph(tg.getGraph(), wsl);
			ntg.buildN2I();
			IO.writeNodeWeightedTGraphQuadruples(odir + "full.speeds", ntg, true);
			NodeWeightedTimeGraph ntg100 = ntg.getBFSSubgraph(0, 100, 0, ntg.gett()-1);
			IO.writeNodeWeightedTGraphQuadruples(odir + "100.speeds", ntg100, true);
		} else if (action.equals("-preparepems")){
			// run with arguments
			// -preparepems /home/petko/data/temporal/PeMS/d07_stations_2010_08_18.graph.uniq /home/petko/data/temporal/PeMS/d07_text_station_5min_2011_04.txt.timeseries /home/petko/data/temporal/PeMS/newdata/
			String gf = args[1];
			String stations = args[2];
			String odir = args[3];
			TimeGraph tg = null;
			//TODO (petko) figure out why are there duplicate edges?
			// TRAFFIC
			tg = IO.readTGraphPems(gf,stations);
			
			tg.scoreNodesPvalue(24);
			//double nz = tg.scoreEdgesPvalueTODDOW(0.01,24);
			tg.buildN2I();
			IO.writeTGraphQuadruplesLogOdds(odir + "full.uniq_NAMESADDED", tg);
			
			WeightedTimeGraph wtg = IO.readTGraphQuadruplesWeighted(odir + "full.uniq_NAMESADDED");
			
			WeightedTimeGraph tg100 = wtg.getBFSSubgraph(0, 100, 0, wtg.gett()-1);
			IO.writeTGraphWeightedQuadruples(odir + "100nodes.uniq_NAMESADDED", tg100);
		} else if (action.equals("-preparewiki")){
			// run with arguments
			// -preparepems /home/petko/data/temporal/PeMS/d07_stations_2010_08_18.graph.uniq /home/petko/data/temporal/PeMS/d07_text_station_5min_2011_04.txt.timeseries /home/petko/data/temporal/PeMS/newdata/
			String fng = args[1];
			String fnt = args[2];
			String odir = args[3];
			NodeWeightedTimeGraph tg = IO.readNWGraph(fng, fnt);
			float[][] pv = tg.getPvalues();
			
			// set the average pvalue on the edges 
			float[][] epv = new float[tg.gett()][tg.getm()];
			for (int t = 0; t < tg.gett(); t++) {
				for (int i=0; i < tg.getn(); i++) {
					for (int j=tg.ind[i]; j < tg.ind[i+1]; j++) {
						epv[t][j] = (float)((pv[t][i] + pv[t][tg.endv[j]])/2.0);
					}
				}
			}
			WeightedTimeGraph ewtg = new WeightedTimeGraph(tg.getGraph(),epv);
			IO.writeTGraphWeighted(fng + ".ewpval", ewtg);
			IO.writeTGraphWeightedQuadruples(fng + ".ewpval.quadruples", ewtg);
			
			NodeWeightedTimeGraph nwpval = new NodeWeightedTimeGraph(tg, pv);
			IO.writeNodeWeightedTGraphQuadruples(fng + ".nwpval", nwpval, true);
		} else if (action.equals("-convert")) {
			// run as java -jar meden.jar -convert quadruples_file
			String fn = args[1];
			TimeGraph tg = IO.readTGraphQuadruples(fn);
			IO.writeTGraph(fn + ".tg", tg);
			System.err.print("converted " + fn +"\n");
		} else if (action.equals("-run")) {
			// java -jar meden.jar -run file start end d/w [mu] (if you add mu the data will be re-scored)
			// d means discrete, while w means weighted
			String fn = args[1];
			int s = Integer.parseInt(args[2]);
			int e = Integer.parseInt(args[3]);
			
			if (args[4].equals("d")) {
				// Running discrete graph
				// loading
				long st = System.currentTimeMillis();
				//TimeGraph tg = IO.readTGraph(fn);
				TimeGraph tg = IO.readTGraphQuadruples(fn);
				System.out.print("Loading took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
				// running
				st = System.currentTimeMillis();
				Pattern pt = BoundUtility.runCoarse(tg, s, e, e-s+1, 0.4, 10, new RunMesasurements(), false);
				System.out.print("Evaluation took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
				System.out.print(pt.toString());
			} else {
				// Running weighted graph
				// loading
				long st = System.currentTimeMillis();
//				WeightedTimeGraph tg = IO.readTGraphQuadruplesWeighted(fn);
				
				WeightedTimeGraph tg = null;
				if (args.length > 5) {
					float mu = Float.parseFloat(args[5]); 
					tg = IO.readTGraphQuadruplesPvalues(fn, mu);
				} else {
					tg = IO.readTGraphQuadruplesWeighted(fn);
				}
				
				System.out.print("Loading took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
//				for (int t = 0 ; t < tg.gett(); t++) {
//					System.err.print(t + "\t" + Common.numPos(tg.wsl[t]) + "\t" 
//									 + Common.sumPos(tg.wsl[t]) + "\n");
//				}
				// running
				st = System.currentTimeMillis();
				Pattern pt = BoundUtility.runCoarse(tg, s, e, e-s+1, 0.4, 10, new RunMesasurements(), false);
				System.out.print("Evaluation took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
				
				SmoothPattern sp = new SmoothPattern(tg, pt.s,pt.e,pt.edges);
//				HashMap<String,String> nnames = IO.readHashMap("/home/petko/data/seine/03_wikipedia/preprocessed/gregress/6934.nds.names.990.8");
//				sp.nnames = nnames;
//				sp.visualizeNoLabels();
//				System.out.print(pt.toString());
				System.out.print(sp.toStringNodeNames());
			}
		} else if (action.equals("-runconvert")) {
			String fn = args[1];
			int s = Integer.parseInt(args[2]);
			int e = Integer.parseInt(args[3]);
			long st = System.currentTimeMillis();
			// this is the graph with p-values in the nodes
			NodeWeightedTimeGraph tg = IO.readNWGraph(fn + ".graph", fn + ".data");
			float mu = 0.04F;
			float[][] scores =  Distribution.scoreAllPValues(tg.wsl, mu);
			NodeWeightedTimeGraph scnwtg = new NodeWeightedTimeGraph(tg, scores);
			
			// transform to node weighted, inspired by the Bioinformatics paper
			WeightedTimeGraph ewtg = scnwtg.getAugmentedEWTimeGraph();
			System.out.print("Loading and Transformation took:\t" + 
					(System.currentTimeMillis()-st) + "ms\n");
			
			for (int t = 0 ; t < ewtg.gett(); t++) {
				System.err.print(t + "\t" + Common.numPos(ewtg.wsl[t]) + "\t" 
								 + Common.sumPos(ewtg.wsl[t]) + "\n");
			}
			// running
			st = System.currentTimeMillis();
			Pattern pt = BoundUtility.runCoarse(ewtg, s, e, e-s+1, 0.4, 10, new RunMesasurements(), false);
			System.out.print("Evaluation took:\t" + 
					(System.currentTimeMillis()-st) + "ms\n");
			
			SmoothPattern sp = new SmoothPattern(ewtg, pt.s,pt.e,pt.edges);
			HashMap<String,String> nnames = IO.readHashMap("/home/petko/data/seine/03_wikipedia/preprocessed/gregress/6934.nds.names.990.8");
			sp.nnames = nnames;
			sp.visualizeNoLabels();
			System.out.print(pt.toString());
		}else if (action.equals("-runtopkmu")) {
			// ran as:
			// java -jar meden.jar -runtopk k file start end d/w
			int k = Integer.parseInt(args[1]);
			String fn = args[2];
			int s = Integer.parseInt(args[3]);
			int e = Integer.parseInt(args[4]);
			
			if (args[5].equals("d")) {
				// loading
				long st = System.currentTimeMillis();
				//TimeGraph tg = IO.readTGraph(fn);
				TimeGraph tg = IO.readTGraphQuadruples(fn);
				System.out.print("Loading took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
				// running
				st = System.currentTimeMillis();
				for (int i = 0; i < k; i++) {
					System.out.print("TOP " + (i+1) + ": #######################");
					Pattern pt = BoundUtility.runCoarse(tg, s, e, e-s+1, 0.4, 10, new RunMesasurements(), false);
					// remove bits of solution
					BitSet mask = new BitSet(tg.getm());
					mask.or(pt.edges);
					mask.flip(0,tg.getm()-1);
					for (int t = pt.s; t <= pt.e; t++) {
						tg.slices[t].and(mask);
					}
				}
				System.out.print("Evaluation took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
			} else {
				// loading
				long st = System.currentTimeMillis();
				//WeightedTimeGraph tg = IO.readTGraphWeighted(fn);
				//WeightedTimeGraph tg = IO.readTGraphQuadruplesWeighted(fn);
				float mu = 0.01F;
				if (args.length > 6) mu = Float.parseFloat(args[6]); 
				WeightedTimeGraph tg = IO.readTGraphQuadruplesPvalues(fn, mu);
				
				System.out.print("Loading took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
				// running
				HashMap<String,String> nnames = IO.readHashMap("/home/petko/data/seine/03_wikipedia/preprocessed/gregress/6934.nds.names.990.8");
				st = System.currentTimeMillis();
				for (int i = 0; i < k; i++) {
					//System.out.print("TOP " + (i+1) + ": #######################\n");
					Pattern pt = BoundUtility.runCoarse(tg, s, e, e-s+1, 0.4, 10, new RunMesasurements(), false);
					for (int j = 0; j < tg.getm(); j++) {
						assert(pt.edges.get(j) == pt.edges.get(tg.getReverseEdgeIndex(j)));
					}
					
					System.out.print(i + "\t" + pt.score + "\t" + pt.s + "-" + pt.e + "\t" + pt.edges.toString() + "\n"); 
//					if (i==20 ){
//						System.err.print("");
//						double sum = 0;
//						for (int t=3550; t<=3604;t++) sum += tg.wsl[t][99];
//						System.err.print(sum + "\n");
//					}
					SmoothPattern sp = new SmoothPattern(tg, pt.s,pt.e,pt.edges);
					sp.nnames = nnames;
					String fns = "/home/petko/data/seine/03_wikipedia/preprocessed/gregress/6934.p" + i;
					sp.saveGraphVizNoLabels(fns);
					sp.showGraphViz(fns + ".png");
					System.out.print("\t");
					for (int t = pt.s; t <= pt.e; t++) {
						for (int j = 0; j < tg.getm(); j++) {
							if (pt.edges.get(j)) { 
								//tg.wsl[t][j] = Float.NEGATIVE_INFINITY;
								tg.wsl[t][j] = 0;
							}
						}
					}
				}
				System.out.print("Evaluation took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
			}
		}else if (action.equals("-runtopk")) {
			// ran as:
			// java -jar meden.jar -runtopk k file start end d/w
			int k = Integer.parseInt(args[1]);
			String fn = args[2];
			int s = Integer.parseInt(args[3]);
			int e = Integer.parseInt(args[4]);
			
			if (args[5].equals("d")) {
				// loading
				long st = System.currentTimeMillis();
				//TimeGraph tg = IO.readTGraph(fn);
				TimeGraph tg = IO.readTGraphQuadruples(fn);
				System.out.print("Loading took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
				// running
				st = System.currentTimeMillis();
				for (int i = 0; i < k; i++) {
					System.out.print("TOP " + (i+1) + ": #######################");
					Pattern pt = BoundUtility.runCoarse(tg, s, e, e-s+1, 0.4, 10, new RunMesasurements(), false);
					// remove bits of solution
					BitSet mask = new BitSet(tg.getm());
					mask.or(pt.edges);
					mask.flip(0,tg.getm()-1);
					for (int t = pt.s; t <= pt.e; t++) {
						tg.slices[t].and(mask);
					}
				}
				System.out.print("Evaluation took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
			} else {	
				// loading
				long st = System.currentTimeMillis();
				WeightedTimeGraph tg = null;
				if (args.length > 6) {
					float mu = Float.parseFloat(args[6]); 
					tg = IO.readTGraphQuadruplesPvalues(fn, mu);
				} else {
					tg = IO.readTGraphQuadruplesWeighted(fn);
				}
				
				System.out.print("Loading took:\t" + 
						(System.currentTimeMillis()-st) + "ms\n");
				st = System.currentTimeMillis();
				for (int i = 0; i < k; i++) {
					Pattern pt = BoundUtility.runCoarse(tg, s, e, e-s+1, 0.4, 10, new RunMesasurements(), false);
					SmoothPattern sp = new SmoothPattern(tg, pt.s,pt.e,pt.edges);
					
					if (sp.score == 0) {
						System.out.print("No more strictly positiv components");
						break;
					}
					
					System.out.print("TOP " + (i+1) + ": \n" + sp.toStringNodeNames());
					
					// set the edges to 0
					for (int t = pt.s; t <= pt.e; t++) {
						for (int j = 0; j < tg.getm(); j++) {
							if (pt.edges.get(j)) { 
								//tg.wsl[t][j] = Float.NEGATIVE_INFINITY;
								tg.wsl[t][j] = 0;
							}
						}
					}
				}
			}
		} else if (action.equals("-runconverttopk")) {
			int k = Integer.parseInt(args[1]);
			String fn = args[2];
			int s = Integer.parseInt(args[3]);
			int e = Integer.parseInt(args[4]);
			
			long st = System.currentTimeMillis();
			// this is the graph with p-values in the nodes
			NodeWeightedTimeGraph ntg = IO.readNWGraph(fn + ".graph", fn + ".data");
			float mu = 0.04F;
			float[][] scores =  Distribution.scoreAllPValues(ntg.wsl, mu);
			NodeWeightedTimeGraph scnwtg = new NodeWeightedTimeGraph(ntg, scores);
			// transform to node weighted, inspired by the Bioinformatics paper
			WeightedTimeGraph ewtg = scnwtg.getAugmentedEWTimeGraph();
			System.out.print("Loading and Transformation took:\t" + 
					(System.currentTimeMillis()-st) + "ms\n");
			// running
			HashMap<String,String> nnames = IO.readHashMap("/home/petko/data/seine/03_wikipedia/preprocessed/gregress/6934.nds.names.990.8");
			st = System.currentTimeMillis();
			for (int i = 0; i < k; i++) {
				//System.out.print("TOP " + (i+1) + ": #######################\n");
				Pattern pt = BoundUtility.runCoarse(ewtg, s, e-1, e-s+1, 0.4, 10, new RunMesasurements(), false);
				
				for (int j = 0; j < ewtg.getm(); j++) {
					assert(pt.edges.get(j) == pt.edges.get(ewtg.getReverseEdgeIndex(j)));
				}
				
				System.out.print(i + "\t" + pt.score + "\t" + pt.s + "-" + pt.e + "\t" + pt.edges.toString() + "\n"); 
//				if (i==20 ){
//					System.err.print("");
//					double sum = 0;
//					for (int t=3550; t<=3604;t++) sum += tg.wsl[t][99];
//					System.err.print(sum + "\n");
//				}
				SmoothPattern sp = new SmoothPattern(ewtg, pt.s,pt.e,pt.edges);
				sp.nnames = nnames;
				sp.visualizeNoLabels();
				System.out.print("\t");
				for (int t = pt.s; t <= pt.e; t++) {
					for (int j = 0; j < ewtg.getm(); j++) {
						if (pt.edges.get(j)) { 
							//tg.wsl[t][j] = Float.NEGATIVE_INFINITY;
							ewtg.wsl[t][j] = 0;
						}
					}
				}
			}
			System.out.print("Evaluation took:\t" + 
					(System.currentTimeMillis()-st) + "ms\n");
		} else if (action.equals("-gen")) {
			// Run as:
			// java -jar meden.jar -gen /home/petko/projects/svnrepo/AnomalyDetection/trunk/experiments/Data/Synthetic/genspec.txt
			String specfile = args[1];
			try {
				BufferedReader br = new BufferedReader(new FileReader(specfile));
				String line = null;
				while ((line = br.readLine())!= null) {
					if (line.startsWith("#") || line.trim().equals("")) continue;
					String fn = line.split(",")[0];
					char typeDynamics = line.split(",")[1].charAt(0);
					boolean isER = line.split(",")[2].equals("er");
					double percentNNZ = Double.parseDouble(line.split(",")[3]);
					double N = Double.parseDouble(line.split(",")[4]);
					double T = Double.parseDouble(line.split(",")[5]);
					double lambda = Double.parseDouble(line.split(",")[6]);
					int n = Integer.parseInt(line.split(",")[7]);
					int m = Integer.parseInt(line.split(",")[8]); 
					int t = Integer.parseInt(line.split(",")[9]);
					int avglen = -1;
					if (line.split(",").length > 10) avglen =  Integer.parseInt(line.split(",")[10]);
					
					Graph g = null;
					if (isER) {
						g = GraphGenerator.generateRandom(n, m);
					} else {
						g = GraphGenerator.generateScaleFree(n);
					}
					assert(g.isConnected());
					
					// Time graph - first generate a discrete anda then if needed weigh it
					DynamicModel dm = new DynamicModel();
					dm.PNeighbor = N;
					dm.PNextGivenCurrent = T;
					dm.POne = percentNNZ;
					dm.AvgLength = avglen;
					
					WeightedTimeGraph wtg = null;
					TimeGraph tg = null;
					switch (typeDynamics) {
					case 'd':
						tg = GraphGenerator.generateTimeRWSampling(t, dm, g);
						IO.writeTGraphQuadruples(fn, tg);
						IO.writeTGraph(fn + "_graph", tg);
						break;
					case 'w':
						wtg = GraphGenerator.generateTimeRWSamplingWeighted(t, dm, g, lambda, typeDynamics);
						IO.writeTGraphWeightedQuadruples(fn, wtg);
						IO.writeTGraphWeighted(fn + "_graph", wtg);
						break;
					case 's':
						wtg = 
							GraphGenerator.generateTimeRWSamplingWeighted(t, dm, g, lambda,typeDynamics);
						IO.writeTGraphWeightedQuadruples(fn, wtg);
						IO.writeTGraphWeighted(fn + "_graph", wtg);
						break;
					case 'c':
						wtg = 
							GraphGenerator.generateTimeRWSamplingWeighted(t, dm, g, lambda,typeDynamics);
						IO.writeTGraphWeightedQuadruples(fn, wtg);
						IO.writeTGraphWeighted(fn + "_graph", wtg);
						break;
					}
					
				}
				
			} catch (Exception e) {
				System.err.print("Wrong or missing synthetic data generation file");
				System.err.print("The format of the generation specification is: \n" +
								 "<fulpath>,{w,d},{er,sf},<pfrac:[0,1]>,"+
								 "<N:[0,1]>,<T:[0,1]>,<lambda>,<n>,<m>,<t>\n" +
								 "\t- fullpath is the name of the output file \n" +
								 "\t- w generates a graph with exponential weights, while d creates a 0/1 graph \n" +
								 "\t- er is to generate Erdos-Renyi, sf is to generate a scale free graphs\n" + 
								 "\t- pfrac is the fraction of positive time-edges, usually around 0.1 or lower\n" +
								 "\t- N is the rate of propagating to neighbors in the diffusion, usually ~ 0.3\n" + 
								 "\t- T is the rate of propagating in time, usually ~ 0.9 \n" +
								 "\t- lambda of the exponential for sampling both positive and negative weights(disregarded when discrete is set)\n" +
								 "\t- n is number of nodes in the graph\n" + 
								 "\t- m is number of edges in the graph (disregarded when scale-free generation)\n" +
								 "\t- t is number of slices\n");
				e.printStackTrace();
			}
		}
	}
}
