package geneFinder;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import types.Gene;
import types.Rna;

/**
 * after the optimization, assign ambiguous reads to their final position + erase all genes and isoforms not any longer supported by reads
 * Copyright (c) 2013,
 * Franziska Zickmann, 
 * ZickmannF@rki.de, Robert Koch-Institute, Berlin, Germany
 * Distributed under the GNU Lesser General Public License, version 3.0
 */

public class CleanAfterAmbiOpti {
	
	/*
	 * method that parses the solution file of cplex
	 */
	
	public static void parse_solution_and_clean_CPLEX(Map<String,Object[]> multiRnas, int fVar_counter){
		
		long timeBefClean = System.currentTimeMillis();

		Gene currentCluster = null;	// to avoid searching for a candidate that we already found
		int processedNum = 0;    // for progress reporting

		String rnaKey = "";		// Initialization to avoid long run times
		int countRna = 0;
		
		Iterator<String> it =  multiRnas.keySet().iterator();
		
		if(it.hasNext()){
			rnaKey = it.next();
		}
		
		int counterVars = 0;
		try{
			BufferedReader br = new BufferedReader(new FileReader(GeneFinder.pathOut+"solutionCPLEX_it" + GeneFinder.iteration + ".sol"));
			
			System.out.println("Parsing solution of cplex and clean ambiguous reads... ");
			System.out.print("Processed: ");
			
			String line = "";

			while((line = br.readLine()) != null){    // go through file until we reach the part where we find the variables
				if(line.contains("<variables>")){
					break;
				}
			}

			while(((line = br.readLine()) != null) && (!line.contains("</variables>"))){  // make sure to stop when variable part is over

				String[] lineSplit1 = line.split(" ");   // position 1 contains name, 3 contains value
			
				counterVars++;
				if(counterVars > fVar_counter){
				
					String[] valueSplit = lineSplit1[5].split("\"");  // to extract the value		
						
					int score = 0;					

					if(!(valueSplit[1].equals("0") || valueSplit[1].equals("1"))){  // sometimes cplex does not round
						double sol = Double.parseDouble(valueSplit[1]);
						if(sol<=0.5){	// if 2 variables are both 0.5, then we cannot take both! so remove both because no real support!
							score = 0;
						}else{
							score = 1;
						}
					}else{
						score = Integer.parseInt(valueSplit[1]);  // directly grab the score, without rounding
					}

					// now search for the right gene using the list of genes associated to each ambiguous read (more complicated than storing a map, but also more memory efficient)
					
					if(countRna == (((Vector<Gene>) multiRnas.get(rnaKey)[1]).size())){
						if(it.hasNext()){
							rnaKey = it.next();
							countRna = 0;
						}else{
							System.out.println("crash at: " + line);
							System.out.println("fCount: " + fVar_counter);
							System.out.println("var Count: " + counterVars);
						}
					}
					
					if(score == 0){	// read was assigned elsewhere, so delete it from the candidate of this variable
						
						// get variable name by search in binaryVars

						if(currentCluster == null){	// to avoid exceptions in searchAndErase fkt.
							currentCluster = ((Vector<Gene>) multiRnas.get(rnaKey)[1]).get(0);
						}
						
						currentCluster = searchAndErase_CPLEX(multiRnas, rnaKey, currentCluster, countRna);	// performs the deleting task
						
						if(currentCluster == null){
							break;	// this means we did not find the candidate, so something is wrong
						}
					}
					
					countRna++;

					processedNum++;

					if(processedNum % 100000 == 0){
						System.out.print(processedNum + "; ");
					}
				}
			}

			br.close();
		} catch (IOException e) {
			System.out.println("Create solution file:");
			System.exit(0);
		}

		// log messages
		System.out.println(processedNum + ".");
		System.out.println("Done.");
		long timeAfterClean = System.currentTimeMillis();
		
		System.out.println("Time needed to parse solution and to clean up after ambiguous reads assignment: "+(double) (timeAfterClean-timeBefClean)/1000.0 +"s.");
		WriteOutput.writeToLogFile("Time needed to parse solution and to clean up after ambiguous reads assignment: "+(double) (timeAfterClean-timeBefClean)/1000.0 +"s.");
		Runtime r=Runtime.getRuntime();
		r.gc();
		r.gc();	// to keep the memory requirements down as much as possible

	}
	
	/*
	 * method that parses the solution file of glpk
	 */
	
	public static void parse_solution_and_clean_GLPK(Map<String,Object[]> multiRnas){

		long timeBefClean = System.currentTimeMillis();

		Gene currentCluster = null;	// to avoid searching twice for the same candidate
		int processedNum = 0;   // for progress reporting

		try{
			BufferedReader br = new BufferedReader(new FileReader(GeneFinder.pathOut+"solutionGLPK_out_it" + GeneFinder.iteration + ".out"));

			System.out.println("Parsing solution of glpk and clean ambiguous reads... ");
			System.out.print("Processed: ");
			
			String line = "";

			while((line = br.readLine()) != null){			// proceed through file until we reach the variable part
				if(line.contains("x__") && !line.contains("__f")){
					break;
				}
			}

			while((line != null) && line.contains("x__")){

				String[] lineSplit1 = line.split(" ");   // position 1 contains name, value is contained in next line

				if(line.contains("x__") && (!line.contains("__f"))){
					
					// we arrived at a "real" ambiguous read constraint				
					
					line = br.readLine();
	
					int score = Integer.parseInt(line.substring(line.indexOf("*")+1,line.indexOf("*")+20).trim());   // the first integer after the "*" is the score we need

					// now search for the right gene using the list of genes associated to each ambiguous read (more complicated than storing a map, but also more memory efficient)
					
					if(score == 0){	// read was assigned elsewhere, so delete it from the candidate of this variable
						String[] nameSep = lineSplit1[lineSplit1.length-1].split("__");
						String varName_read = "x__"+nameSep[1];
						
						if(currentCluster == null){	// are there any exceptions possible?
							currentCluster = ((Vector<Gene>) multiRnas.get(varName_read)[1]).get(0);
						}
						
						int idGene = Integer.parseInt(nameSep[2]);
						currentCluster = searchAndErase(multiRnas, varName_read, idGene, currentCluster);  // performs the deleting task
						
						if(currentCluster == null){
							break;	// this means we did not find the candidate, so something is wrong
						}
					}

					processedNum++;

					if(processedNum % 50000 == 0){

						System.out.print(processedNum + "; ");
						Runtime r=Runtime.getRuntime();
						r.gc();
						r.gc();	// to keep the memory requirements down as much as possible
					}
				}else if(line.contains("x__") && (line.contains("__f"))){
					line = br.readLine();
				}
				
				line = br.readLine();
			}

			br.close();
		} catch (IOException e) {
			System.out.println("Create solution file:");
			System.exit(0);
		}

		// log messages:
		
		System.out.println(processedNum + ".");
		System.out.println("Done.");
		long timeAfterClean = System.currentTimeMillis();
		
		Runtime r=Runtime.getRuntime();
		r.gc();
		r.gc();	// to keep the memory requirements down as much as possible
		System.out.println("Time needed to parse solution and to clean up after ambiguous reads assignment: "+(double) (timeAfterClean-timeBefClean)/1000.0 +"s.");
		WriteOutput.writeToLogFile("Time needed to parse solution and to clean up after ambiguous reads assignment: "+(double) (timeAfterClean-timeBefClean)/1000.0 +"s.");

	}
	
	/*
	 * search the gene this variable is assigned to and erase the rna from all lists
	 */
	
	public static Gene searchAndErase(Map<String,Object[]> multiRnas, String varName_read, int idGene, Gene currentCluster){
		
		if(idGene != currentCluster.geneID){  // otherwise we do not need the time consuming search
			// search for the next node

			currentCluster = null;


			for(Gene cluster : ((Vector<Gene>) multiRnas.get(varName_read)[1])){

				if(cluster.geneID == idGene){
					currentCluster = cluster;

					if(cluster.twinNode != null){	// the removal leads to a speed up, because we do not need to look at one candidate more than once (per read)
						((Vector<Gene>) multiRnas.get(varName_read)[1]).add(cluster.twinNode);
						((Vector<Gene>) multiRnas.get(varName_read)[1]).removeElement(cluster);
					}else{
						((Vector<Gene>) multiRnas.get(varName_read)[1]).removeElement(cluster);
					}

					break;

				}else if((cluster.twinNode != null) && (cluster.twinNode.geneID == (idGene))){    // if the candidate does not have the correct id, than maybe its twin
					currentCluster = cluster.twinNode;								
					break;
				}

			}


		}

		if(currentCluster == null){
			System.out.println("Did not find the node!");   // This would mean that something is wrong
			return null;
		}

		// this read must be deleted from the list of associated reads of this candidate

		if(currentCluster.idTOassociatedRnas.containsKey(varName_read.substring(3))){  // first remove from candidate
			
			Rna rna = ((Rna) currentCluster.idTOassociatedRnas.get(varName_read.substring(3))[0]);  // grab the necessary information from map
			int[] spliceSupport = ((int[]) currentCluster.idTOassociatedRnas.get(varName_read.substring(3))[1]);
			int fussyExonSupport = ((Integer) currentCluster.idTOassociatedRnas.get(varName_read.substring(3))[2]);
			
			if(spliceSupport[0] != -1 && (currentCluster.possibleIntrons.get(spliceSupport[0])[0] != null)){ // check if this was a read supporting a split
				// remove this rna from split, first find the right one
				for(int pos = 0; pos < ((Vector<int[]>)currentCluster.possibleIntrons.get(spliceSupport[0])[1]).size();++pos){
					
					if(((Vector<int[]>)currentCluster.possibleIntrons.get(spliceSupport[0])[0]).get(pos)[1] == spliceSupport[1]){
						// found intron, so erase rna and break;
						((Vector<Vector<Rna>>)currentCluster.possibleIntrons.get(spliceSupport[0])[1]).get(pos).removeElement(rna);
						
						// now check if intron has not enough support left, if yes, then mark as to-be-erased intron
						
						if(((Vector<Vector<Rna>>)currentCluster.possibleIntrons.get(spliceSupport[0])[1]).get(pos).size() == 0){
							
							// mark intron as intron to be erased, note that exons are only erased if no other support
							
							currentCluster.eraseIntrons_temp.add(((Vector<int[]>)currentCluster.possibleIntrons.get(spliceSupport[0])[0]).get(pos));
						
						}
												
						break;
					}
				}
			}
			
			if(fussyExonSupport != -1 && currentCluster.possibleFussyExons.containsKey(fussyExonSupport)){  // also erase read from fussyExon support list
				currentCluster.possibleFussyExons.get(fussyExonSupport).removeElement(rna);
				if(currentCluster.possibleFussyExons.get(fussyExonSupport).size() == 0){
					// erase and also update in possibleIntrons
					currentCluster.possibleFussyExons.remove(fussyExonSupport);
					
					if(currentCluster.possibleIntrons.containsKey(fussyExonSupport)){
						currentCluster.possibleIntrons.get(fussyExonSupport)[2] = -1;
					}
				}
			}
				
			currentCluster.idTOassociatedRnas.remove(varName_read.substring(3));

		}else{
			System.out.println("Did not find the read "+ varName_read.substring(3) + " in the candidate " + currentCluster.geneID + "!");
		}
		
		return currentCluster;
	}
	
	/*
	 * search the gene this variable is assigned to and erase the rna from all lists
	 */
	
	public static Gene searchAndErase_CPLEX(Map<String,Object[]> multiRnas, String varName_read, Gene currentCluster, int posInVec){
		
		currentCluster = ((Vector<Gene>) multiRnas.get(varName_read)[1]).get(posInVec);

		// this read must be deleted from the list of associated reads of this candidate

		if(currentCluster.idTOassociatedRnas.containsKey(varName_read.substring(3))){  // first remove from candidate
			
			Rna rna = ((Rna) currentCluster.idTOassociatedRnas.get(varName_read.substring(3))[0]);  // grab the necessary information from map
			int[] spliceSupport = ((int[]) currentCluster.idTOassociatedRnas.get(varName_read.substring(3))[1]);
			int fussyExonSupport = ((Integer) currentCluster.idTOassociatedRnas.get(varName_read.substring(3))[2]);
			
			if(spliceSupport[0] != -1 && (currentCluster.possibleIntrons.get(spliceSupport[0])[0] != null)){ // check if this was a read supporting a split
				// remove this rna from split, first find the right one
				for(int pos = 0; pos < ((Vector<int[]>)currentCluster.possibleIntrons.get(spliceSupport[0])[1]).size();++pos){
					
					if(((Vector<int[]>)currentCluster.possibleIntrons.get(spliceSupport[0])[0]).get(pos)[1] == spliceSupport[1]){
						// found intron, so erase rna and break;
						((Vector<Vector<Rna>>)currentCluster.possibleIntrons.get(spliceSupport[0])[1]).get(pos).removeElement(rna);
						
						// now check if intron has not enough support left, if yes, then mark as to-be-erased intron
						
						if(((Vector<Vector<Rna>>)currentCluster.possibleIntrons.get(spliceSupport[0])[1]).get(pos).size() == 0){
							
							// mark intron as intron to be erased, note that exons are only erased if no other support
							
							currentCluster.eraseIntrons_temp.add(((Vector<int[]>)currentCluster.possibleIntrons.get(spliceSupport[0])[0]).get(pos));
						
						}
												
						break;
					}
				}
			}
			
			if(fussyExonSupport != -1 && currentCluster.possibleFussyExons.containsKey(fussyExonSupport)){  // also erase read from fussyExon support list
				currentCluster.possibleFussyExons.get(fussyExonSupport).removeElement(rna);
				if(currentCluster.possibleFussyExons.get(fussyExonSupport).size() == 0){
					// erase and also update in possibleIntrons
					currentCluster.possibleFussyExons.remove(fussyExonSupport);
					
					if(currentCluster.possibleIntrons.containsKey(fussyExonSupport)){
						currentCluster.possibleIntrons.get(fussyExonSupport)[2] = -1;
					}
				}
			}
	
			currentCluster.idTOassociatedRnas.remove(varName_read.substring(3));

			
		}else{
			System.out.println("Did not find the read "+ varName_read.substring(3) + " in the candidate " + currentCluster.geneID + "!");
		}
		
		return currentCluster;
	}

}
