package graph;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;

import misc.Common;
import misc.Distribution;

public class NodeWeightedTimeGraph extends Graph 
	implements ITimeGraph {
	public final float[][] wsl;
	
	public NodeWeightedTimeGraph(Graph g, float[][] sl) {
		super(g);
		wsl = sl;
		assert(g.getn()==sl[0].length);
	}

	public int gett() {
		return wsl.length;
	}

	public void aggregateByTime(int s, int e, double[] weights) {
		assert(s<=e && s >= 0 && e < gett()):"Incorrect interval" + s + " " + e;
		Arrays.fill(weights, 0.0);
		for (int i = s; i <=e; i++) {
			for(int j = 0; j < getn(); j++) {
				weights[j] += wsl[i][j];
			}
		}
	}

	public void aggregateByTime(int s, int e) {
		assert(s<=e && s >= 0 && e < gett()):"Incorrect interval" + s + " " + e;
		//Graph g = getStructure();
		Arrays.fill(wn, 0.0);
		for (int i = s; i <=e; i++) {
			for(int j = 0; j < getn(); j++) {
				wn[j] += wsl[i][j];
			}
		}
	}
	
	// computes sizes of connected components in each slice with node values
	// in [lT,hT]
	public int[][] getRootedConnectedComponents(double lT, double hT) {
		int[][] res = new int[wsl.length][wsl[0].length];
		for (int t=0; t < wsl.length; t++) {
			for (int i =0; i < getn(); i++) {
				if (wsl[t][i] >= lT && wsl[t][i] <= hT) {
					wn[i] = 1;
				} else  {
					wn[i] = -1;
				}
			}
			res[t] = Arrays.copyOf(super.getPositiveNodeConnectedComponentSizes(),res[t].length);
		}
		return res;
	}
	
	public void printEvolutionOfPositiveComponents(double lT, double hT, double percOverlap, double minsize,
													String ef, String nf) throws IOException{
		BufferedWriter edges = new BufferedWriter(new FileWriter(ef));
		BufferedWriter nodes = new BufferedWriter(new FileWriter(nf));
		int[] ccs = null;
		HashMap<Integer,BitSet> prev = new HashMap<Integer,BitSet>();
		HashMap<Integer,BitSet> curr = new HashMap<Integer,BitSet>();
		BitSet tmp = new BitSet(getn());
		for (int t=0; t < wsl.length; t++) {
			for (int i =0; i < getn(); i++) {
				if (wsl[t][i] >= lT && wsl[t][i] <= hT) {
					wn[i] = 1;
				} else  {
					wn[i] = -1;
				}
			}
			ccs = getPositiveNodeComponents();
			for (int i=0; i < getn(); i++) {
				if (wn[i] > 0) {
					if (!curr.containsKey(ccs[i])) curr.put(ccs[i], new BitSet(getn()));
					curr.get(ccs[i]).set(i);
				}
			}
			for (Integer c: curr.keySet()) {
				if (curr.get(c).cardinality() < minsize) continue;
				nodes.write(t + ":" + c + "," + curr.get(c).cardinality() + "\n");
				for (Integer p: prev.keySet()) {
					if (prev.get(p).cardinality() < minsize) continue;
					tmp.clear();
					tmp.or(curr.get(c));
					tmp.and(prev.get(p));
					if (tmp.cardinality()*1.0/
							Math.min(prev.get(p).cardinality(), curr.get(c).cardinality()) >= percOverlap) {
						edges.write((t-1) + ":" + p + "," + t + ":" + c  + "\n");
					}
				}
			}
			prev.clear();
			prev.putAll(curr);
			curr.clear();
			System.err.print("done ts:" + t + "\n");
		}
		nodes.close();
		edges.close();
	}

	public NodeWeightedTimeGraph getBFSSubgraph(int seed, int size, int s , int e) {
		Graph gg = super.getBFSSubgraph(seed, size);
		gg.buildN2I();
		float[][] nslices = new float[e-s+1][gg.getn()];
		for (int t = s; t <= e; t++) {
			for (int i = 0; i < gg.getn(); i++) {
				nslices[t-s][i] = wsl[t][getNodeIndex(gg.names[i])];
			}
		}
		NodeWeightedTimeGraph nwtg = new NodeWeightedTimeGraph(gg, nslices);
		return nwtg;
	}
	
	public WeightedTimeGraph getAugmentedEWTimeGraph() {
		Graph g = new Graph(2*getn(), getm() + 2*getn());
		float[][] ewt = new float[gett()][ getm() + 2*getn()];
		float minw = Common.min(wsl);
		
		int jj = 0;
		for (int i=0; i <getn(); i++) {
			g.names[i*2] = names[i];
			g.names[i*2+1] = names[i] + "_";
			g.wn[i*2] = wn[i];
			g.wn[i*2+1] = 0;
			g.ind[i*2+1] = g.ind[i*2] + (ind[i+1]-ind[i]) + 1;
			g.ind[i*2+2] = g.ind[i*2+1] + 1;
			for (int j=ind[i]; j < ind[i+1]; j++) {
				g.endv[jj] = endv[j]*2;
				g.we[jj] = we[j];
				for(int t = 0; t < gett(); t++) ewt[t][jj] = minw;
				jj++;
			}
			g.endv[jj] = i*2+1;
			g.we[jj] = 0;
			for(int t = 0; t < gett(); t++) ewt[t][jj] = wsl[t][i] + minw;
			jj++;
			g.endv[jj] = i*2;
			g.we[jj] = 0;
			for(int t = 0; t < gett(); t++) ewt[t][jj] = wsl[t][i] + minw;
			jj++;
		}
		return new WeightedTimeGraph(g,ewt);
	} 
	
	public float[][] getPvalues() {		
		Distribution dist = new Distribution();
		float[][] res = new float[wsl.length][wsl[0].length]; 
		
		for (int i=0; i < getn(); i++) { // nodes
			dist.clean();
			for (int t=0; t < gett(); t++) dist.add(wsl[t][i]);
			for (int t=0; t < gett(); t++) res[t][i] = dist.pvalue_gt(wsl[t][i]);
		}
		return res;
	}
	
	public Graph getGraph() {
		return this;
	}

	@Override
	public float[][] getTimeseries() {
		return wsl;
	}
}