package exp.twostep;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;




public class Main {
	
	/**

	 * 
	 * */
	
	public static void main(String[] args) throws InterruptedException, ExecutionException{
		// it is better to use a parser for the argument, but currently, 
		// we use order of the arguments for convenience.
		
		
		Loading l = new Loading(args);
		List<String> tfList = l.getTFList();
		ArrayList<Pair<String,String> > geneMet = l.getGeneMetCombination();
		DataRepresent met = l.getMetData();
		DataRepresent mrna = l.getmRNAData();
		DataRepresent cnv = l.getCNVData();
		int mode = l.getMode();
		int cores = l.getThread();
		
		
		//start calculation
		System.out.println("(calculation)result matrix size : " + tfList.size() + ", " + geneMet.size());
		double[][] resultCMI = new double[tfList.size()][geneMet.size()]; 
		double[][] resultMI = new double[tfList.size()][geneMet.size()];
		
		System.out.println("(calculation)Preparing Thread Pool. Thread:" + cores);
		ExecutorService threadPool = Executors.newFixedThreadPool( cores ); //NEED to BE ADJUSTABLE
		
		@SuppressWarnings("unchecked")
		Future<double[]>[] resultSet = new Future[tfList.size()];
		
		int dd = 0;
		for (String tf1 : tfList){
			Callable<double[]> c = new Worker(tf1, mrna, met, cnv, geneMet, mode);
			resultSet[dd] = threadPool.submit(c);
			dd++;
		}

		try {
			for (int i = 0; i < tfList.size(); i++){
				double[] data = resultSet[i].get();
				System.arraycopy(data, 0, resultMI[i], 0, data.length>>1);
				System.arraycopy(data, geneMet.size(), resultCMI[i], 0, data.length>>1);
			} 
			

			
			threadPool.shutdown(); // do not accept new tasks
			if (!threadPool.awaitTermination(2, TimeUnit.HOURS)) { // In theory, it should shutdown in 1 second;
			       threadPool.shutdownNow(); // Cancel currently executing  task
			}
			
		} catch (SecurityException e){
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// output the result
		
		try (FileWriter fw = new FileWriter("resultMI.txt");
				BufferedWriter bw = new BufferedWriter(fw)){
			StringBuilder sb = new StringBuilder();
			sb.append("tf");
			for (Pair<String, String> p : geneMet){
				sb.append("\t").append(p.first).append("###").append(p.second);
			}
			sb.append("\n");
			bw.write(sb.toString());
			sb.delete(0, sb.length());
			for (int i = 0; i < resultMI.length; i++){ // in fact, use the size of resultSet is better
				sb.append(tfList.get(i));
				for (int j = 0; j < resultMI[i].length; j++){
					sb.append("\t").append(resultMI[i][j]);
				}
				sb.append("\n");
				bw.write(sb.toString());
				sb.delete(0, sb.length());
			}
			
		}catch (Exception e){
			e.printStackTrace();
		}
		
		try (FileWriter fw = new FileWriter("resultCMI.txt");
				BufferedWriter bw = new BufferedWriter(fw)){
			StringBuilder sb = new StringBuilder();
			sb.append("tf");
			for (Pair<String, String> p : geneMet){
				sb.append("\t").append(p.first).append("###").append(p.second);
			}
			sb.append("\n");
			bw.write(sb.toString());
			sb.delete(0, sb.length());
			for (int i = 0; i < resultCMI.length; i++){ // in fact, use the size of resultSet is better
				sb.append(tfList.get(i));
				for (int j = 0; j < resultCMI[i].length; j++){
					sb.append("\t").append(resultCMI[i][j]);
				}
				sb.append("\n");
				bw.write(sb.toString());
				sb.delete(0, sb.length());
			}

		}catch (Exception e){
			e.printStackTrace();
		}
		
		try (FileOutputStream fos = new FileOutputStream("resultMI.bin");
				DataOutputStream dos = new DataOutputStream(fos)){
			dos.writeInt(resultMI.length);//first, row
			dos.writeInt(resultMI[0].length);// second, column
			for (int i = 0; i < resultMI.length; i++){
				for (int j = 0; j < resultMI[i].length; j++){
					dos.writeDouble(resultMI[i][j]);
				}
			}
		}catch (Exception e){
			e.printStackTrace();
		}
		
		
		try (FileOutputStream fos = new FileOutputStream("resultCMI.bin");
				DataOutputStream dos = new DataOutputStream(fos)){
			dos.writeInt(resultCMI.length);//first, row
			dos.writeInt(resultCMI[0].length);// second, column
			for (int i = 0; i < resultCMI.length; i++){
				for (int j = 0; j < resultCMI[i].length; j++){
					dos.writeDouble(resultCMI[i][j]);
				}
			}
		}catch (Exception e){
			e.printStackTrace();
		}
		
		try (FileWriter fw = new FileWriter("MetSiteInfo.txt");
				BufferedWriter bw = new BufferedWriter(fw)){
			bw.write("Gene\tMet\n");
			for (int i = 0; i < geneMet.size(); i++){
//				bw.write("\t");
				bw.write(geneMet.get(i).first + "###" + geneMet.get(i).second + "\n");
			}
//			bw.write("\n");
			
		}catch (Exception e){
			e.printStackTrace();
		}
		try (FileWriter fw = new FileWriter("tfList.txt");
				BufferedWriter bw = new BufferedWriter(fw)){
			for (int i = 0; i < tfList.size(); i++){
				bw.write(tfList.get(i) + "\n");
			}
			
		}catch (Exception e){
			e.printStackTrace();
		}
		
		
		
		//primary result output ended here
		
		//pValue Calc

		
		
		
		
	}
	
	
	
	
	
}
























//System.out.println(System.getProperty("java.library.path"));
//
//double[][] a = new double[1][800];
//double[][] b = new double[1][800];
//double[][] c = new double[1][800];
//int k = 64;
//for (int i = 0; i < 800; i++){
//	a[0][i] = Math.sin(i);
//	b[0][i] = Math.cos(i);
//	c[0][i] = Math.tan(i);
//}
//
//double d =0;
//d = MI.partialMutualInformation(a, b, c, k);
//long t1 = System.currentTimeMillis();
//for (int i = 0; i < 500; i++)
//	d = MI.partialMutualInformation(a, b, c, k);
//
//System.out.println((System.currentTimeMillis()-t1)/1000.0/500);
//System.out.println(d);