package geneFinder;

import java.io.*;
import java.util.TreeMap;
import java.util.Vector;

import types.*;

/**
* parse the sam file derived from the read mapping,
* 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 SamParser {
	
	public static int posiNotContained;
	public static int removedHits;
	
	/*
	 * here we do the special parsing of the SAM-file, that only extracts the basic info to prepare the cluster extraction
	 * no initial scores are assigned
	 */
	
	public void samFileParser(File inFile, File inFile2, String nameRef){
		
		if(GeneFinder.minCoverage == -1){
			// estimate the threshold by using coveragePlot script
			
			GeneFinder.minCoverage = estimateCoverage();
			
			if(!GeneFinder.secondPart){
				System.out.println("Estimated minimum coverage: " + GeneFinder.minCoverage);			
				System.out.println("Estimated maximum coverage threshold: " + GeneFinder.maxCov);
				WriteOutput.writeToLogFile("Estimated minimum coverage: " + GeneFinder.minCoverage + "\n");
			}	
			
		}else if (GeneFinder.dispCov && (GeneFinder.iteration != 2)){
			estimateCoverage();
		}
		
		if(GeneFinder.useSequential){
			partParser(inFile2,nameRef);
			GeneFinder.iteration = 2;
			WriteOutput.sortReassignSamFile();		
		}
		
		if(!GeneFinder.secondPart){
			if(GeneFinder.iteration == 2){
				System.out.print("Parse SAM file second time.... ");			
				WriteOutput.writeToLogFile("Parse SAM file second time.... ");
			}else{
				System.out.print("Parse SAM file.... ");			
				WriteOutput.writeToLogFile("Parse SAM file.... ");
			}	
		}
			
		long timebef = System.currentTimeMillis();	
		
		long rnaCount = 0;
		long timeAfter = 0;
		int multiCount = 0;
		int multiTotalCount = 0;
		int totalHitCount = 0;
		
		int reassignedCount = 0;
		int notReaCount = 0;
		
		posiNotContained = 0;
		removedHits = 0;

		try{

			BufferedReader rnaIn = new BufferedReader(new FileReader(inFile));
			
			BufferedReader br = null; 
			String lineReaSam = "";
			
			if(GeneFinder.iteration == 2){
				br = new BufferedReader(new FileReader(new File(GeneFinder.pathOut+"reassignedReads_sorted.sam")));
				while((lineReaSam = br.readLine()) != null){
					String[] parts1 = lineReaSam.split("	");
					if(!parts1[0].startsWith("@") && parts1.length >=4){
						break;  // now lineReaSam is at first read
					}
				}
			}
			
			String line;

			while((line = rnaIn.readLine()) != null){
				
				String[] parts1 = line.split("	");
				
				if(!parts1[0].startsWith("@") && parts1.length >=4){
					// now go to next while loop to skip previous check
					String currentReadID = new String();	
					Rna currentRead = null;
					
					if(GeneFinder.readLength == -1){
						if(parts1[10].length() == 1){
							System.out.println("No read sequence in sam file, please specify with option -rL.");
							System.exit(0);
						}
						GeneFinder.readLength = parts1[10].length();
						if(!GeneFinder.secondPart){
							System.out.println();
							System.out.println("Read length: " + GeneFinder.readLength);
						}								
					}
					
					do{
						String[] parts = line.split("\t");
						
						String flag = Integer.toBinaryString(Integer.parseInt(parts[1]));
						String[] flagArr = flag.split("");

						// test if mapped					
					
						if(!((flagArr.length >= 3) && ((flagArr[((flagArr.length)-3)]).equals("1"))) && (!(GeneFinder.isProkaryote && parts[5].contains("N"))) && !((parts[5].contains("H")) || (parts[5].contains("P")) || (parts[5].contains("S")))){  // part 5 restriction for blat alignment....

							// get the contig

							String contigName = parts[2];
							Contig thisContig;
							if(GeneFinder.mappedContigs.containsKey(contigName)){
								// already seen this contig
								thisContig = GeneFinder.mappedContigs.get(contigName);
							}else{
								thisContig = new Contig();
								thisContig.contigName = contigName;
								GeneFinder.mappedContigs.put(contigName,thisContig);
								GeneFinder.contigTOname.put(thisContig,contigName);
							}

							totalHitCount++;
							
							if(!parts[0].equals(currentReadID)){  // now we have proceeded to a new read

								if(GeneFinder.iteration == 2 && currentRead != null && currentRead.isMulti == 1){
									
									TreeMap<Integer,String> allReassigned = new TreeMap<Integer,String>();  // will contain all lines than deal with this read
									if(lineReaSam != null){
										do{
											String[] partsReaSam = lineReaSam.split("	");										
											
											if(currentRead.rnaID.equals(partsReaSam[0])){
												allReassigned.put(Integer.parseInt(partsReaSam[3]),partsReaSam[2]);
											}else{
												break;
											}
											
										}while((lineReaSam = br.readLine()) != null);
										
										if(!allReassigned.isEmpty()){
											compareMappingsWithReaSam(currentRead,allReassigned);
											reassignedCount++;
										}else{
											notReaCount++;
										}
									}
									
								}

								currentReadID = parts[0];				

								// set up new rna node

								Rna newRna = new Rna();
								newRna.rnaID = parts[0];  		
								newRna.isMulti = 0;
								newRna.hitNum = 1;
								newRna.assignedNum = 0;

								// assign quality,

								double qualScoreRead = 0.0;
								double maxQual = Double.MIN_VALUE;
								double minQual = Double.MAX_VALUE;

								if(parts[10].length() > 1){

									for(int i = 0; i< parts[10].length();i++){
										int posQualInt = parts[10].charAt(i); 
										double baseQual = 1.0 - (Math.pow(10.0, (-((double) posQualInt/10.0))));
										if(baseQual < minQual){
											minQual = baseQual;
										}
										if(baseQual > maxQual){
											maxQual = baseQual;
										}
										qualScoreRead += baseQual;

									}

									newRna.quality = ((qualScoreRead/((double) parts[10].length())))/(maxQual-minQual+1);

								}else{
									newRna.quality = 1.0;
								}

								int thisAlignmentPosition = (Integer.parseInt(parts[3])) - 1; // TopHat and BWA refer to the 1-based start, so subtract -1 (because we refer to 0-based start)

								double thisMappingQuality = (1-(Math.pow(10.0,(-(Integer.parseInt(parts[4]))/10.0))));

								if(thisContig.positionTOmappingRnas.containsKey(thisAlignmentPosition)){
									if(!thisContig.positionTOmappingRnas.get(thisAlignmentPosition).contains(newRna)){
										thisContig.positionTOmappingRnas.get(thisAlignmentPosition).add(newRna);
									}

								}else{
									Vector<Rna> itsRnas = new Vector<Rna>();
									itsRnas.add(newRna);
									thisContig.positionTOmappingRnas.put(thisAlignmentPosition,itsRnas);
								}

								Object[] mappedContig_and_Info = new Object[7];
								mappedContig_and_Info[0] = thisContig;
								mappedContig_and_Info[1] = thisAlignmentPosition;
								mappedContig_and_Info[2] = parts[5];

								// this is for mismatch info
								
								String mdTag = "";
								if(line.contains("MD:Z:")){
									String[] splitMD = line.split("MD:Z:");
									String[] splitMD2 = splitMD[splitMD.length-1].split("\t");
									mdTag = splitMD2[0];
								}else{
									System.err.println("No MD tag!");
									System.exit(0);
								}
								mappedContig_and_Info[5] = CigarParser.extractAllAlignDiffs(parts[5],mdTag,parts[9]);

								/*if(parts[5].contains("X")){
									String[] cigarSplitX = parts[5].split("X");
									int posTmp = 0;
									Vector<int[]> missMatchInfo = new Vector<int[]>(); // entries are of form: position (within contig),baseCode (A=0;C=1;G=2;T=3)
									for(String tmp : cigarSplitX){
										// the more parts, the more mismatches; note that the last part have to be checked if it ends with another letter!
										if(!(tmp.endsWith("M") || tmp.endsWith("N"))){
											String[] tmpSplit = tmp.split("[MN]");
											for(String tmp2 : tmpSplit){
												posTmp += Integer.parseInt(tmp2);
											}
											// now we have the position of a mismatch
											int[] mmInfo = new int[2];
											mmInfo[0] = thisAlignmentPosition + posTmp;
											// now get base at this position
											String base = parts[9].substring(posTmp,posTmp+1);
											mmInfo[1] = returnBaseCode(base);
											missMatchInfo.add(mmInfo);
										}
									}

									mappedContig_and_Info[5] = missMatchInfo;
								}*/

								mappedContig_and_Info[3] = thisMappingQuality;

								if((parts[5].contains("N"))){
									mappedContig_and_Info = performSplitExtract(mappedContig_and_Info, thisAlignmentPosition,thisContig,parts[5]);									
								}

								if(line.contains("XS:A:+")){
									mappedContig_and_Info[6] = "+";
								}else if(line.contains("XS:A:-")){
									mappedContig_and_Info[6] = "-";
								}

								if(parts[5].contains("I") || parts[5].contains("D")){

									int splitSize = 0;
									if(mappedContig_and_Info[4] != null){
										splitSize = ((int[]) mappedContig_and_Info[4])[0];
									}

									countInDels(parts[5],thisAlignmentPosition,thisContig,splitSize,false);								
								}

								newRna.contigsMappedOn.add(mappedContig_and_Info);

								// note: new try to avoid out of mem: rnaList.add(newRna);
								rnaCount++;
								currentRead = newRna;

								if(!GeneFinder.useTopHat){
									// multiple hits in BWA file are indicated with XO:A:R and named in tag XA
									String[] splitPart = parts[13].split(":");
									String[] splitPartX1 = parts[14].split(":");
									if((Integer.parseInt(splitPart[2]) > 1) && ((Integer.parseInt(splitPart[2]) + Integer.parseInt(splitPartX1[2])) <= GeneFinder.maxReportedHitsBWA)){
										// parse through all other hits
										String[] multiHits = parts[19].split(";");
										for(String hit : multiHits){
											multiTotalCount++;
											if(currentRead.isMulti == 0){
												multiCount++;
												currentRead.isMulti = 1;
											}
											multiTotalCount++;
											String[] hitInfo = hit.split(",");

											// first get the contig
											Contig secondContig;
											if(GeneFinder.mappedContigs.containsKey(hitInfo[0].substring(5))){
												// already seen this contig
												secondContig = GeneFinder.mappedContigs.get(hitInfo[0].substring(5));
											}else{
												secondContig = new Contig();
												secondContig.contigName = hitInfo[0].substring(5);
												GeneFinder.mappedContigs.put(hitInfo[0].substring(5),secondContig);
											}

											// now mapping position

											int mapPos = Integer.parseInt(hitInfo[1].substring(1)) - 1;

											if(secondContig.positionTOmappingRnas.containsKey(mapPos)){
												if(!secondContig.positionTOmappingRnas.get(mapPos).contains(currentRead)){
													secondContig.positionTOmappingRnas.get(mapPos).add(currentRead);
												}												
											}else{
												Vector<Rna> itsRnas = new Vector<Rna>();
												itsRnas.add(currentRead);
												secondContig.positionTOmappingRnas.put(mapPos,itsRnas);
											}

											Object[] mappedContig_and_Info_second = new Object[7];
											mappedContig_and_Info_second[0] = secondContig;
											mappedContig_and_Info_second[1] = mapPos;
											mappedContig_and_Info_second[2] = hitInfo[2];  
											mappedContig_and_Info_second[3] = thisMappingQuality;

											// this is for mismatch info

											if(hitInfo[2].contains("X")){

												String[] cigarSplitX = hitInfo[2].split("X");
												int posTmp = 0;
												Vector<int[]> missMatchInfo = new Vector<int[]>(); // entries are of form: position (within contig),baseCode (A=0;C=1;G=2;T=3)
												for(String tmp : cigarSplitX){
													// the more parts, the more mismatches; note that the last part have to be checked if it ends with another letter!
													if(!(tmp.endsWith("M") || tmp.endsWith("N"))){
														String[] tmpSplit = tmp.split("[MN]");
														for(String tmp2 : tmpSplit){
															posTmp += Integer.parseInt(tmp2);
														}
														// now we have the position of a mismatch
														int[] mmInfo = new int[2];
														mmInfo[0] = mapPos + posTmp;
														// now get base at this position
														String base = parts[9].substring(posTmp,posTmp+1);
														mmInfo[1] = returnBaseCode(base);
														missMatchInfo.add(mmInfo);
													}
												}

												mappedContig_and_Info_second[5] = missMatchInfo;

												if(hitInfo[2].contains("I") || hitInfo[2].contains("D")){

													int splitSize = 0;
													if(mappedContig_and_Info_second[4] != null){
														splitSize = ((int[]) mappedContig_and_Info_second[4])[0];
													}

													countInDels(hitInfo[2],mapPos,secondContig,splitSize,false);								
												}
											}

											currentRead.contigsMappedOn.add(mappedContig_and_Info_second);
											currentRead.hitNum++;
										}
									}
								}

							}else{
								// still same read, but with different mapping 
								if(currentRead.isMulti == 0){
									multiCount++;
									multiTotalCount++;  // to count also the hit before this one
									currentRead.isMulti = 1;
								}

								multiTotalCount++;
								int thisAlignmentPosition = (Integer.parseInt(parts[3])) - 1;  
								double thisMappingQuality = (1-(Math.pow(10.0,(-(Integer.parseInt(parts[4]))/10.0))));

								if(thisContig.positionTOmappingRnas.containsKey(thisAlignmentPosition)){
									if(!thisContig.positionTOmappingRnas.get(thisAlignmentPosition).contains(currentRead)){
										thisContig.positionTOmappingRnas.get(thisAlignmentPosition).add(currentRead);
									}								
								}else{
									Vector<Rna> itsRnas = new Vector<Rna>();
									itsRnas.add(currentRead);
									thisContig.positionTOmappingRnas.put(thisAlignmentPosition,itsRnas);
								}

								Object[] mappedContig_and_Info = new Object[7];
								mappedContig_and_Info[0] = thisContig;
								mappedContig_and_Info[1] = thisAlignmentPosition;
								mappedContig_and_Info[2] = parts[5];
								mappedContig_and_Info[3] = thisMappingQuality;

								// this is for mismatch info
								
								String mdTag = "";
								if(line.contains("MD:Z:")){
									String[] splitMD = line.split("MD:Z:");
									String[] splitMD2 = splitMD[splitMD.length-1].split("\t");
									mdTag = splitMD2[0];
								}else{
									System.err.println("No MD tag!");
									System.exit(0);
								}
								mappedContig_and_Info[5] = CigarParser.extractAllAlignDiffs(parts[5],mdTag,parts[9]);

								/*if(parts[5].contains("X")){									
									String[] cigarSplitX = parts[5].split("X");
									int posTmp = 0;
									Vector<int[]> missMatchInfo = new Vector<int[]>(); // entries are of form: position (within contig),baseCode (A=0;C=1;G=2;T=3)
									for(String tmp : cigarSplitX){
										// the more parts, the more mismatches; note that the last part have to be checked if it ends with another letter!
										if(!(tmp.endsWith("M") || tmp.endsWith("N"))){
											String[] tmpSplit = tmp.split("[MN]");
											for(String tmp2 : tmpSplit){
												posTmp += Integer.parseInt(tmp2);
											}
											// now we have the position of a mismatch
											int[] mmInfo = new int[2];
											mmInfo[0] = thisAlignmentPosition + posTmp;
											// now get base at this position
											String base = parts[9].substring(posTmp,posTmp+1);
											mmInfo[1] = returnBaseCode(base);
											missMatchInfo.add(mmInfo);
										}
									}

									mappedContig_and_Info[5] = missMatchInfo;
								}*/

								if(parts[5].contains("N")){

									mappedContig_and_Info = performSplitExtract(mappedContig_and_Info, thisAlignmentPosition,thisContig,parts[5]);

								}

								if(line.contains("XS:A:+")){
									mappedContig_and_Info[6] = "+";
								}else if(line.contains("XS:A:-")){
									mappedContig_and_Info[6] = "-";
								}

								if(parts[5].contains("I") || parts[5].contains("D")){

									int splitSize = 0;
									if(mappedContig_and_Info[4] != null){
										splitSize = ((int[]) mappedContig_and_Info[4])[0];
									}

									countInDels(parts[5],thisAlignmentPosition,thisContig,splitSize,false);								
								}

								currentRead.contigsMappedOn.add(mappedContig_and_Info);
								currentRead.hitNum++;								
							}

						}
											
					}while((line = rnaIn.readLine()) != null);
					
					if(GeneFinder.iteration == 2 && currentRead != null && currentRead.isMulti == 1){
						
						TreeMap<Integer,String> allReassigned = new TreeMap<Integer,String>();  // will contain all lines than deal with this read
						if(lineReaSam != null){
							do{
								String[] partsReaSam = lineReaSam.split("	");
								
								if(partsReaSam[0].compareTo(currentRead.rnaID) > 0){
									break;  // we exceeded this read, so stop
								}else if(currentRead.rnaID.equals(partsReaSam[0])){
									allReassigned.put(Integer.parseInt(partsReaSam[3]),partsReaSam[2]);
								}
							}while((lineReaSam = br.readLine()) != null);
							
							if(!allReassigned.isEmpty()){
								compareMappingsWithReaSam(currentRead,allReassigned);
							}
						}
						
					}
				}else{
					if(GeneFinder.iteration == 1){
						WriteOutput.writeToOtherFile(GeneFinder.pathOut+"reassignedReads.sam",line+"\n");
					}					
				}
			}
			
			rnaIn.close();
			if(GeneFinder.iteration == 2){
				br.close();
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}  catch (IOException e) {
			e.printStackTrace();
		}

		
		if(GeneFinder.interval == -1){
			GeneFinder.interval = GeneFinder.readLength + 1;
			if(!GeneFinder.secondPart){
				System.out.println("Intron minimum: " + GeneFinder.interval);
			}
			
		}
		timeAfter = System.currentTimeMillis();	    
		
		if(!GeneFinder.secondPart){
			if(GeneFinder.iteration == 2){
				System.out.println("reassigned: " + reassignedCount);
				System.out.println("not reassigned: " + notReaCount);
			}
			System.out.println("Done.");
			System.out.println((rnaCount) + " rnas have been mapped to the reference.");
			System.out.println((multiCount) + " of them are ambiguous reads.");
			System.out.println("The total number of hits is " + (totalHitCount) + ".");		
			System.out.println("Total number of multiple hits: " + multiTotalCount + ".");
			System.out.println("Time required to parse SAM file: "+ (double) (timeAfter-timebef)/1000.0 +"s.");
			
			GeneFinder.ambiProportion = (double) ((double)multiTotalCount/(double)totalHitCount);
			System.out.println("Proportion of ambiguous reads: " + GeneFinder.ambiProportion);
			
			System.out.println();
			WriteOutput.writeToLogFile("Done.\nTime required to parse SAM file: "+ (double) (timeAfter-timebef)/1000.0 +"s.\n" + rnaCount + " rnas have been mapped to the reference. \n" + (multiCount) + " of them are ambiguous reads. \nThe total number of hits is " + (totalHitCount) + ".\nTotal number of multiple hits: " + multiTotalCount + ".\nProportion of ambiguous reads: " + GeneFinder.ambiProportion + ".\n\n");
		
		}
		
	}
	
	/*
	 * care for split extraction
	 */
	
	public static Object[] performSplitExtract(Object[] mappedContig_and_Info, int thisAlignmentPosition, Contig thisContig, String cigar){
		
		String[] cigarSplit1 = cigar.split("[DIMX]");
		int currentPos = thisAlignmentPosition;
		
		for(String cigSplit2 : cigarSplit1){			
			if(cigSplit2.contains("N")){
				
				String[] cigarSplit = cigSplit2.split("N");
				int[] spliceInfo = new int[2];          // first entry: intron size, second: start position of intron
				spliceInfo[0] = Integer.parseInt(cigarSplit[0]);
				
				// check if an insert occurred before split start, if yes, position has to be updated
				
				String[] cigarSplitN = cigar.split("N");
				
				if(cigarSplitN[0].contains("I")){
					String[] splitN_I = cigarSplitN[0].split("I");
					String[] splitN_I_All = splitN_I[0].split("[DMX]");
					currentPos = currentPos - Integer.parseInt(splitN_I_All[splitN_I_All.length-1]);  // this contains the number directly before "I"
				}
				
				
				spliceInfo[1] = currentPos;
				mappedContig_and_Info[4] = spliceInfo;
				if(thisContig.splicePositions.containsKey(currentPos)){
					thisContig.splicePositions.put(currentPos,thisContig.splicePositions.get(currentPos)+1);
					
				}else{
					thisContig.splicePositions.put(currentPos, 1);
				}
				
				break;
			}else{
				// count position before split starts
				currentPos += Integer.parseInt(cigSplit2);
			}
			
		}
		
		return mappedContig_and_Info;
	}
	
	/*
	 * returns the integer belonging to one of the 4 bases respecting baseCode (A=0;C=1;G=2;T=3)
	 */
	
	public static int returnBaseCode(String base){
		
		if(base.equals("A")){
			return 0;
		}else if(base.equals("C")){
			return 1;
		}else if(base.equals("G")){
			return 2;
		}else if(base.equals("T")){
			return 3;
		}else if(base.equals("N")){
			return 4;
		}else if(base.equals("gap")){
			return 5;
		}else{
			return 6;
		}
	}
	
	/*
	 * counts the number of insertions and deletions
	 */
	
	public static void countInDels(String cigar, int thisAlignmentPosition,Contig thisContig, int splitSize, boolean calledFromSequential){
		
		int[] indels = {0,0};
		
		if(cigar.contains("D")){
			String[] cigarSplitD = cigar.split("D");
			
			for(int i = 0; i<cigarSplitD.length -1;++i){  // "-1" because we not not want to go through last part (cigar does not end with D)
				if(cigarSplitD[i].matches(".*[a-zA-Z].*")){
					String[] parts = cigarSplitD[i].split("[a-zA-Z]");
					indels[0] += Integer.parseInt(parts[parts.length-1]);
				}else{
					indels[0] += Integer.parseInt(cigarSplitD[i]);
				}
			}
		}
		
		if(cigar.contains("I")){
			String[] cigarSplitI = cigar.split("I");
			
			for(int i = 0; i<cigarSplitI.length -1;++i){  // "-1" because we not not want to go through last part (cigar does not end with I)
				if(cigarSplitI[i].matches(".*[a-zA-Z].*")){
					String[] parts = cigarSplitI[i].split("[a-zA-Z]");
					indels[1] += Integer.parseInt(parts[parts.length-1]);
				}else{
					indels[1] += Integer.parseInt(cigarSplitI[i]);
				}
			}
		}
		
		if(calledFromSequential){
			for(int i = 1; i<=indels[0]; ++i){
				if(thisContig.positionTOdiff.keySet().contains(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize + i)){
					int value = thisContig.positionTOdiff.get(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize + i) - 1;
					thisContig.positionTOdiff.put(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize + i,value);
					if(thisContig.positionTOdiff.get(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize + i) == 0){
						thisContig.positionTOdiff.remove(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize + i);
					}
				}				
				
			}
			
			for(int i = 0; i<indels[1]; ++i){
				if(thisContig.positionTOdiff.keySet().contains(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize - i)){
					int value = thisContig.positionTOdiff.get(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize - i) + 1;
					thisContig.positionTOdiff.put(thisAlignmentPosition + splitSize + (GeneFinder.readLength-1) - i,value);
					if(thisContig.positionTOdiff.get(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize - i) == 0){
						thisContig.positionTOdiff.remove(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize - i);
					}
				}
						
			}
		}else{
			for(int i = 1; i<=indels[0]; ++i){
				if(thisContig.positionTOdiff.keySet().contains(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize + i)){
					int value = thisContig.positionTOdiff.get(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize + i) + 1;
					thisContig.positionTOdiff.put(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize + i,value);
				}else{
					thisContig.positionTOdiff.put(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize + i,1);
				}
			}
			
			for(int i = 0; i<indels[1]; ++i){
				if(thisContig.positionTOdiff.keySet().contains(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize - i)){
					int value = thisContig.positionTOdiff.get(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize - i) - 1;
					thisContig.positionTOdiff.put(thisAlignmentPosition + splitSize + (GeneFinder.readLength-1) - i,value);
				}else{
					thisContig.positionTOdiff.put(thisAlignmentPosition + (GeneFinder.readLength-1) + splitSize - i,-1);
				}
			}
		}
		
		
	}

	/*
	 * compare reassigned hits with the former extracted ones + manage update maps
	 */
	
	public static void compareMappingsWithReaSam(Rna read,TreeMap<Integer,String> allReassigned){
		
		for(int posMap = read.contigsMappedOn.size()-1;posMap>= 0;posMap--){							
			Object[] hit = read.contigsMappedOn.get(posMap);
			
			if(allReassigned.containsKey((((Integer)hit[1]).intValue()))){
				String info = allReassigned.get((((Integer)hit[1]).intValue()));
				
				if(!(info.equals(GeneFinder.contigTOname.get(((Contig)hit[0]))))){
					updateMapsForRemovedElement(hit,read);
					read.contigsMappedOn.removeElementAt(posMap);
				}
			}else{
				updateMapsForRemovedElement(hit,read);
				read.contigsMappedOn.removeElementAt(posMap);
			}

		}
		
		if(read.contigsMappedOn.size() == 1){
			read.isMulti = 0;
		}
	}
	
	/*
	 * update positionToreads and splicing maps
	 */
	
	public static void updateMapsForRemovedElement(Object[] hit, Rna read){
		
		Contig thisContig = (Contig) hit[0];
		if(thisContig.positionTOmappingRnas.containsKey((Integer)hit[1])){
			if(thisContig.positionTOmappingRnas.get((Integer)hit[1]).contains(read)){
				thisContig.positionTOmappingRnas.get((Integer)hit[1]).removeElement(read);
			}
			if(thisContig.positionTOmappingRnas.get((Integer)hit[1]).size() == 0){
				thisContig.positionTOmappingRnas.remove((Integer)hit[1]);
				removedHits++;
			}
		}else{
			//System.out.println("posi not contained for " + read.rnaID);
			posiNotContained++;
		}
			
		if(hit[4] != null){
			int[] spliceInfo = (int[]) hit[4];		
			if(thisContig.splicePositions.containsKey(spliceInfo[1])){
				thisContig.splicePositions.put(spliceInfo[1],thisContig.splicePositions.get(spliceInfo[1])-1);				
			}
			if(thisContig.splicePositions.get(spliceInfo[1]) == 0){
				thisContig.splicePositions.remove(spliceInfo[1]);			
			}
		}

	}
	
	/*
	 * use coveragePlot.py script to plot the overall coverage and estimate the needed coverage threshold
	 */
	
	public static Double estimateCoverage(){
		
		if(!GeneFinder.secondPart){
			System.out.println("Estimate coverage.... ");			
			WriteOutput.writeToLogFile("Estimate coverage.... \n");
		}
		
		long timebef= System.currentTimeMillis();
		long timeAfter = 0;
		
		double meanCov = 0.0;
		
		String nameSam = "";
		
			
			if(GeneFinder.useTopHat){
				if(GeneFinder.haveSam != null){
					nameSam = GeneFinder.haveSam;
				}else{
					nameSam = GeneFinder.pathOut+"accepted_hits.sam";
				}
				
			}else{
				if(GeneFinder.haveSam != null){
					nameSam = GeneFinder.haveSam;				
				}else{
					nameSam = GeneFinder.pathOut+"aln_BWA.sam";
				}
				
			}
			
			String nameOutfile = GeneFinder.pathOut+"covMean.txt";
			
			try {
				
				// if covMean.txt already in results directory, we do not need to estimate the coverage again
				
				BufferedReader br = new BufferedReader(new FileReader(nameOutfile));
				String line = "";
				if((line = br.readLine()) != null){
					meanCov = Double.parseDouble(line);
				} 
				if((line = br.readLine()) != null){
					GeneFinder.maxCov = Double.parseDouble(line);
				} 
				br.close();
				
			} catch (IOException e) {

				// no coverage file provided, so create a new one

				String firstExe = "python " + GeneFinder.pathToHelpFiles+"getMeanCov.py " + nameSam + " " + nameOutfile; 	
				Giira.callAndHandleOutput(firstExe,false);
				
				timeAfter = System.currentTimeMillis();
				
				if(!GeneFinder.secondPart){
					System.out.println("Done. Time needed: "+ (double) (timeAfter-timebef)/1000.0 +"s.");			
					WriteOutput.writeToLogFile("Done. Time needed: "+ (double) (timeAfter-timebef)/1000.0 +"s. \n");
				}
				
				try {
					BufferedReader br = new BufferedReader(new FileReader(nameOutfile));
					String line = "";
					if((line = br.readLine()) != null){
						meanCov = Double.parseDouble(line);
					} 
					if((line = br.readLine()) != null){
						GeneFinder.maxCov = Double.parseDouble(line);
					} 

					br.close();
				}catch (IOException e2) {
					System.err.println("File not found!");
				}

			}

			return (meanCov);	
	}
	
	
	/*
	 * parses a sam file sorted according to the chromosomes
	 * at the moment no BWA support!
	 */
	
	public void partParser(File inFile,String nameRef){

		long timebef = System.currentTimeMillis();	
		
		long rnaCount = 0;
		long timeAfter = 0;
		int multiCount = 0;
		int multiTotalCount = 0;
		int totalHitCount = 0;
		
		int reassignedCount = 0;
		int notReaCount = 0;
		
		int interChromoCount = 0;
		int interChromoTotalCount = 0;
			
		posiNotContained = 0;
		removedHits = 0;

		TreeMap<String,Vector<Object>> seenReads = new TreeMap<String,Vector<Object>>(); // first entry in Vec: ok (1) or not ok (0), second: read
		
		try{

			BufferedReader rnaIn = new BufferedReader(new FileReader(inFile));
			
			String currentName = "";

			String line;

			while((line = rnaIn.readLine()) != null){
				
				String[] parts1 = line.split("	");
				
				if(!parts1[0].startsWith("@") && parts1.length >=4){
					// now go to next while loop to skip previous check
					//String currentReadID = new String();	
					//Rna currentRead = null;
					
					if(GeneFinder.readLength == -1){
						if(parts1[10].length() == 1){
							System.out.println("No read sequence in sam file, please specify with option -rL.");
							System.exit(0);
						}
						GeneFinder.readLength = parts1[10].length();
						if(!GeneFinder.secondPart){
							System.out.println();
							System.out.println("Read length: " + GeneFinder.readLength);
						}								
					}
					
					do{
						String[] parts = line.split("\t");
						
						String flag = Integer.toBinaryString(Integer.parseInt(parts[1]));
						String[] flagArr = flag.split("");

						// test if mapped					
					
						if(!((flagArr.length >= 3) && ((flagArr[((flagArr.length)-3)]).equals("1"))) && (!(GeneFinder.isProkaryote && parts[5].contains("N"))) && !((parts[5].contains("H")) || (parts[5].contains("P")) || (parts[5].contains("S")))){  // part 5 restriction for blat alignment....

							// get the contig

							String contigName = parts[2];
							Contig thisContig;
							if(GeneFinder.mappedContigs.containsKey(contigName)){
								// already seen this contig
								thisContig = GeneFinder.mappedContigs.get(contigName);
							}else{							
								if(!GeneFinder.mappedContigs.keySet().isEmpty()){
									// start new round here!!
									
									timeAfter = System.currentTimeMillis();	    
									
									System.out.println((rnaCount) + " rnas have been mapped to the reference.");
									System.out.println((multiCount) + " of them are ambiguous reads.");
									System.out.println("The total number of hits is " + (totalHitCount) + ".");		
									System.out.println("Total number of multiple hits: " + multiTotalCount + ".");
									System.out.println("Number of interchromosomal reads (so far): " + interChromoCount + ".");
									System.out.println("Number of interchromosomal hits (so far): " + interChromoTotalCount + ".");
									System.out.println("Time for parsing chromosome: "+ (double) (timeAfter-timebef)/1000.0 +"s.");

									GeneFinder.ambiProportion = (double) ((double)multiTotalCount/(double)totalHitCount);
									System.out.println("Proportion of ambiguous reads: " + GeneFinder.ambiProportion);

									System.out.println();
									WriteOutput.writeToLogFile(rnaCount + " rnas have been mapped to the reference. \n" + (multiCount) + " of them are ambiguous reads. \nThe total number of hits is " + (totalHitCount) + ".\nTotal number of multiple hits: " + multiTotalCount + ".\nProportion of ambiguous reads: " + GeneFinder.ambiProportion + ".\n\n");					
									
									String nameOut = "_" + currentName;
									GeneFinder.geneFinder_managing(nameRef,nameOut);
								}							
								currentName = contigName;
								timebef = System.currentTimeMillis();	
								
								thisContig = new Contig();
								thisContig.contigName = contigName;
								GeneFinder.mappedContigs.clear();
								GeneFinder.contigTOname.clear();
								GeneFinder.mappedContigs.put(contigName,thisContig);
								GeneFinder.contigTOname.put(thisContig,contigName);
								rnaCount = 0;
								multiCount = 0;
								totalHitCount = 0;
								multiTotalCount = 0;
								System.out.println();
								System.out.println("Starting chromosome " + contigName + ".");
							}

							totalHitCount++;
							
							Rna read;
							
							if(seenReads.keySet().contains(parts[0])){
								
								Vector<Object> temp = seenReads.get(parts[0]);
		
								if(((Integer)temp.get(0)) != 0){
									
									//multiCount++;
									multiTotalCount++;
									
									// read ok up to now, check tag XX
									
									if(line.contains("CC:Z:")){
										if(!line.contains("CC:Z:=")){									
											
											// search for all former hits and delete read
											
											searchAndDeleteFromContig((Rna)temp.get(1),thisContig);
											
											temp.clear();
											temp.add(0);
											seenReads.put(parts[0],temp);
											
											if(totalHitCount % 100000 == 0){
		
												Runtime r=Runtime.getRuntime();
												r.gc();
												r.gc();	
												
											}
											
											interChromoTotalCount++;
											break;
										}
									}
								}else{
									interChromoTotalCount++;
									break;
								}
								read = (Rna)temp.get(1);
								read.isMulti = 1;
								read.hitNum++;
								//read.assignedNum++;
								
							}else{
								rnaCount++;
								if(line.contains("CC:Z:")){
									multiCount++;
									if(!line.contains("CC:Z:=")){
										interChromoCount++;
										interChromoTotalCount++;
										Vector<Object> temp = new Vector<Object>();
										temp.add(0);									
										seenReads.put(parts[0],temp);
										interChromoTotalCount++;
										break;
									}
	
								}
								// create new read
								read = new Rna();
								read.rnaID = parts[0];  		
								read.isMulti = 0;
								read.hitNum = 1;
								read.assignedNum = 0;
								
								// assign quality,

								double qualScoreRead = 0.0;
								double maxQual = Double.MIN_VALUE;
								double minQual = Double.MAX_VALUE;

								if(parts[10].length() > 1){

									for(int i = 0; i< parts[10].length();i++){
										int posQualInt = parts[10].charAt(i); 
										double baseQual = 1.0 - (Math.pow(10.0, (-((double) posQualInt/10.0))));
										if(baseQual < minQual){
											minQual = baseQual;
										}
										if(baseQual > maxQual){
											maxQual = baseQual;
										}
										qualScoreRead += baseQual;

									}

									read.quality = ((qualScoreRead/((double) parts[10].length())))/(maxQual-minQual+1);

								}else{
									read.quality = 1.0;
								}
								
								if(line.contains("CC:Z:")){
									Vector<Object> temp = new Vector<Object>();
									temp.add(1);
									temp.add(read);
									seenReads.put(parts[0],temp);	
								}
							}

							int thisAlignmentPosition = (Integer.parseInt(parts[3])) - 1; // TopHat and BWA refer to the 1-based start, so subtract -1 (because we refer to 0-based start)

							double thisMappingQuality = (1-(Math.pow(10.0,(-(Integer.parseInt(parts[4]))/10.0))));

							if(thisContig.positionTOmappingRnas.containsKey(thisAlignmentPosition)){
								if(!thisContig.positionTOmappingRnas.get(thisAlignmentPosition).contains(read)){
									thisContig.positionTOmappingRnas.get(thisAlignmentPosition).add(read);
								}
							}else{
								Vector<Rna> itsRnas = new Vector<Rna>();
								itsRnas.add(read);
								thisContig.positionTOmappingRnas.put(thisAlignmentPosition,itsRnas);
							}

							Object[] mappedContig_and_Info = new Object[7];
							mappedContig_and_Info[0] = thisContig;
							mappedContig_and_Info[1] = thisAlignmentPosition;
							mappedContig_and_Info[2] = parts[5];

							// this is for mismatch info

							String mdTag = "";
							if(line.contains("MD:Z:")){
								String[] splitMD = line.split("MD:Z:");
								String[] splitMD2 = splitMD[splitMD.length-1].split("\t");
								mdTag = splitMD2[0];
							}else{
								System.err.println("No MD tag!");
								System.exit(0);
							}
							mappedContig_and_Info[5] = CigarParser.extractAllAlignDiffs(parts[5],mdTag,parts[9]);
							
							/*if(parts[5].contains("X")){
								String[] cigarSplitX = parts[5].split("X");
								int posTmp = 0;
								Vector<int[]> missMatchInfo = new Vector<int[]>(); // entries are of form: position (within contig),baseCode (A=0;C=1;G=2;T=3)
								for(String tmp : cigarSplitX){
									// the more parts, the more mismatches; note that the last part have to be checked if it ends with another letter!
									if(!(tmp.endsWith("M") || tmp.endsWith("N"))){
										String[] tmpSplit = tmp.split("[MN]");
										for(String tmp2 : tmpSplit){
											posTmp += Integer.parseInt(tmp2);
										}
										// now we have the position of a mismatch
										int[] mmInfo = new int[2];
										mmInfo[0] = thisAlignmentPosition + posTmp;
										// now get base at this position
										String base = parts[9].substring(posTmp,posTmp+1);
										mmInfo[1] = returnBaseCode(base);
										missMatchInfo.add(mmInfo);
									}
								}

								mappedContig_and_Info[5] = missMatchInfo;
							}*/

							mappedContig_and_Info[3] = thisMappingQuality;

							if((parts[5].contains("N"))){
								mappedContig_and_Info = performSplitExtract(mappedContig_and_Info, thisAlignmentPosition,thisContig,parts[5]);									
							}

							if(line.contains("XS:A:+")){
								mappedContig_and_Info[6] = "+";
							}else if(line.contains("XS:A:-")){
								mappedContig_and_Info[6] = "-";
							}

							if(parts[5].contains("I") || parts[5].contains("D")){

								int splitSize = 0;
								if(mappedContig_and_Info[4] != null){
									splitSize = ((int[]) mappedContig_and_Info[4])[0];
								}

								countInDels(parts[5],thisAlignmentPosition,thisContig,splitSize,false);								
							}

							read.contigsMappedOn.add(mappedContig_and_Info);

						}
											
					}while((line = rnaIn.readLine()) != null);
										
				}else{
					if(GeneFinder.iteration == 1){
						WriteOutput.writeToOtherFile(GeneFinder.pathOut+"reassignedReads.sam",line+"\n");
					}					
				}
			}
			
			if(!GeneFinder.mappedContigs.keySet().isEmpty()){
				// start new round here!!
				
				timeAfter = System.currentTimeMillis();	    

				if(GeneFinder.iteration == 2){
					System.out.println("reassigned: " + reassignedCount);
					System.out.println("not reassigned: " + notReaCount);
				}
				
				System.out.println((rnaCount) + " rnas have been mapped to the reference.");
				System.out.println((multiCount) + " of them are ambiguous reads.");
				System.out.println("The total number of hits is " + (totalHitCount) + ".");		
				System.out.println("Total number of multiple hits: " + multiTotalCount + ".");
				System.out.println("Number of interchromosomal reads (so far): " + interChromoCount + ".");
				System.out.println("Number of interchromosomal hits (so far): " + interChromoTotalCount + ".");
				System.out.println("Time for parsing chromosome: "+ (double) (timeAfter-timebef)/1000.0 +"s.");

				GeneFinder.ambiProportion = (double) ((double)multiTotalCount/(double)totalHitCount);
				System.out.println("Proportion of ambiguous reads: " + GeneFinder.ambiProportion);

				System.out.println();
				WriteOutput.writeToLogFile(rnaCount + " rnas have been mapped to the reference. \n" + (multiCount) + " of them are ambiguous reads. \nThe total number of hits is " + (totalHitCount) + ".\nTotal number of multiple hits: " + multiTotalCount + ".\nProportion of ambiguous reads: " + GeneFinder.ambiProportion + ".\n\n");					
				
				String nameOut = currentName;
				GeneFinder.geneFinder_managing(nameRef,nameOut);
			}							
			
			rnaIn.close();
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}  catch (IOException e) {
			e.printStackTrace();
		}

		
		if(GeneFinder.interval == -1){
			GeneFinder.interval = GeneFinder.readLength + 1;
			if(!GeneFinder.secondPart){
				System.out.println("Intron minimum: " + GeneFinder.interval);
			}
			
		}
		
	}
	
	/*
	 * method to remove all mappings of this read
	 */
	
	public static void searchAndDeleteFromContig(Rna read ,Contig thisContig){
		
		Vector<Integer> mapPosis = new Vector<Integer>();
		for(Object[] temp : read.contigsMappedOn){
			// [contig, alignPos, cigarString, mapQual,spliceInfo,mismatchInfo,direcInfo]
			
			int posi = (Integer) temp[1];
			
			if(thisContig.positionTOmappingRnas.containsKey(posi)){
				if(thisContig.positionTOmappingRnas.get(posi).contains(read)){
					thisContig.positionTOmappingRnas.get(posi).remove(read);
				}else if(!mapPosis.contains(posi)){
					System.out.println("Read not contained!");
				}
				
				if(thisContig.positionTOmappingRnas.get(posi).isEmpty()){
					thisContig.positionTOmappingRnas.remove(posi);
				}
			}else if(!mapPosis.contains(posi)){
				System.out.println("Position not contained! " + read.rnaID);
			}
			
			mapPosis.add(posi);
			
			// splice positions
			
			if(temp[4] != null){
				int[] spliceInfo = (int[]) temp[4];          // first entry: intron size, second: start position of intron
				
				if(thisContig.splicePositions.containsKey(spliceInfo[1])){
					thisContig.splicePositions.put(spliceInfo[1],thisContig.splicePositions.get(spliceInfo[1])-1);				
				}else{
					System.out.println("Splice position not contained!");
				}
			}
			
			// posiDiff
			
			int splitSize = 0;
			if(temp[4] != null){
				splitSize = ((int[])temp[4])[0];
			}
			
			countInDels((String)temp[2],posi,thisContig,splitSize,true);
		}
		
		read.contigsMappedOn.clear();
		read = null;
	}
		
}
