package geneFinder;

import java.util.TreeMap;
import java.util.Vector;

import types.*;

/**
 * class that contains methods to extract and verify introns and define the corresponding exons
 * 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 IntronExonSearch {

	/*
	 * check if all introns have enough support. If they are below the threshold, erase them and the connected rnas
	 */
	
	public static void finalIni_IntronSupportCheck(Gene cluster, Contig thisContig){
		
		Vector<Integer> keysToErase = new Vector<Integer>();
		
		for(int spliceSite : cluster.possibleIntrons.keySet()){
			
			if(cluster.possibleIntrons.get(spliceSite)[0] != null){
				for(int i = ((Vector<int[]>)cluster.possibleIntrons.get(spliceSite)[0]).size() -1; i>= 0; --i){
					if((((Vector<int[]>)cluster.possibleIntrons.get(spliceSite)[0]).get(i)[1] > cluster.stopPos)){
						// intron has not enough support, so erase and also delete rnas from cluster
						
						int[] badIntron = ((Vector<int[]>)cluster.possibleIntrons.get(spliceSite)[0]).get(i);					
						
						for(Rna rna : ((Vector<Vector<Rna>>)cluster.possibleIntrons.get(spliceSite)[1]).get(i)){
							if(cluster.moreThanOneHitRnas.contains(rna.rnaID)){
								// if it is contained here, this serves as one "back-up life" that is now destroyed
								cluster.moreThanOneHitRnas.remove(rna.rnaID);
							}else{
								cluster.idTOassociatedRnas.remove(rna.rnaID);
							}
							
							if((((Vector<int[]>)cluster.possibleIntrons.get(spliceSite)[0]).get(i)[1] > cluster.stopPos)){
								// remove direcCounter
								for(Object[] info : rna.contigsMappedOn){
									if(info[4] != null){
										if((((int[])info[4])[1] == badIntron[0]) && ((((int[])info[4])[0]) + (((int[])info[4])[1]) == badIntron[1])){
											if(info[6] != null){
												if(((String)info[6]).equals("+")){
													cluster.direcCounter[0]--;
												}else{
													cluster.direcCounter[1]--;
												}
											}
											
											rna.contigsMappedOn.removeElement(info);
											rna.assignedNum = rna.assignedNum -1;
											
											break;
										}
									}
								}
								
							}

						}
						((Vector<int[]>)cluster.possibleIntrons.get(spliceSite)[0]).removeElementAt(i);
						((Vector<Vector<Rna>>)cluster.possibleIntrons.get(spliceSite)[1]).removeElementAt(i);
						

						if(( (Vector<int[]>) cluster.possibleIntrons.get(spliceSite)[0]).size() == 0){
							if((badIntron[1] > cluster.stopPos) && (spliceSite < cluster.stopPos)){
								//checkSplitRemovalSituation(cluster, spliceSite,keysToErase);
								searchLastIntronEndBeforeCurrentPos_closeGene(cluster, spliceSite,keysToErase);
							}
						}


					}else{
						
						// update idTOassociatedRnas, saves time later
						
						for(Rna rna : ((Vector<Vector<Rna>>)cluster.possibleIntrons.get(spliceSite)[1]).get(i)){
							cluster.idTOassociatedRnas.get(rna.rnaID)[1] = new int[] {spliceSite,((Vector<int[]>) cluster.possibleIntrons.get(spliceSite)[0]).get(i)[1]};
						}
					}
									
				}
				
				if(( (Vector<int[]>) cluster.possibleIntrons.get(spliceSite)[0]).size() == 0){
					keysToErase.add(spliceSite);								
				}else{
					// check if fussy indicated and if it is suitable (we only accept a fussy end if it is followed by an intron or if it marks a transcript end)
					
					if(((Integer)cluster.possibleIntrons.get(spliceSite)[2]).intValue() > 0){
						if((((Integer)cluster.possibleIntrons.get(spliceSite)[2]).intValue() != -2) && (((Integer)cluster.possibleIntrons.get(spliceSite)[2]).intValue() != 0)){
							// remove fussy
							
							cluster.possibleIntrons.get(spliceSite)[2] = -1;
							cluster.possibleIntrons.get(spliceSite)[3] = -1;
							
							// remove from fussy list
							if(cluster.possibleFussyExons.containsKey(spliceSite)){
								for(Rna rna : cluster.possibleFussyExons.get(spliceSite)){
									
									if(cluster.moreThanOneHitRnas.contains(rna.rnaID)){
										// if it is contained here, this serves as one "back-up life" that is now destroyed
										cluster.moreThanOneHitRnas.remove(rna.rnaID);
										
										if(cluster.idTOassociatedRnas.containsKey(rna.rnaID)){
											if(((Integer)cluster.idTOassociatedRnas.get(rna.rnaID)[2]).intValue() != -1){
												cluster.idTOassociatedRnas.get(rna.rnaID)[2] = -1;
											}
											
										}
									}else{
										if(cluster.idTOassociatedRnas.containsKey(rna.rnaID)){
											cluster.idTOassociatedRnas.remove(rna.rnaID);
											rna.assignedNum = rna.assignedNum -1;
										}
									}
									
								}
								
								cluster.possibleFussyExons.remove(spliceSite);
							}
						}
					}
				}
				
			}
		
		}
		
		for(int key : keysToErase){
			cluster.possibleIntrons.remove(key);
		}
	}
	
	
	/*
	 * if we are in currentCompete but between currentPos and nextPos is a large gap, search for a supporting split
	 * if none is found, erase the intron and all other ones, which end beyond the current position and close candidate gene at currentPos 
	 */
	
	public static int[] checkIfIntronSupported(Gene gene, int currentPos, int nextPos, TreeMap<Integer,Vector<Integer>> posiCovMap, int inTranscriptMode, int searchTranscriptStart, int endToPassForclosedGenes, int inNormalTranscript){
		
		int foundSupport = -1;
		int foundCurrentPosSupport = -1;
		for(int split : gene.possibleIntrons.keySet()){
			if(gene.possibleIntrons.get(split)[0] != null){
				for(int[] intron : ( (Vector<int[]>) gene.possibleIntrons.get(split)[0])){
					if(currentPos<=intron[0]){ 
						foundSupport = 0;
						foundCurrentPosSupport = 0;
						break;
					}else{
						if((Math.abs(nextPos - intron[1]) <= GeneFinder.readLength)){ // && nextPos <= (intron[1] + GeneFinder.readLength)
							foundSupport = 0;
						}
						
						int fussyPossible = (Integer)(gene.possibleIntrons.get(split)[2]);
						if(currentPos <= fussyPossible){
							foundCurrentPosSupport = 0;
						}
					}
						
				}
			}
			if((foundCurrentPosSupport == 0) && (foundSupport == 0)){
				break;
			}
		}
		
		if(foundSupport == -1){
			
			if(inTranscriptMode == -1){
				// close gene here
				if((searchTranscriptStart == -1)){// && (inNormalTranscript == -1)){	
					
					foundSupport = 3;	
								
				}else{
					foundSupport = 0;
				}
				
			}else{
				//delete transcript or close it
			
				foundSupport = replaceEntryInAltTransWithUpdatedOne(gene, inTranscriptMode, currentPos, -1, nextPos);   // -1 indicates that closed within an intron
		
			}
		}else{
			for(int position : posiCovMap.keySet()){
				if(currentPos < (position-GeneFinder.readLength) && nextPos > (position+GeneFinder.readLength)){						
					// had no chance to look at it, so create fake intron to also regard this exon and close
					searchCompleteIntronForEndingAndMakeNewIsoform(gene, position,posiCovMap);
					endToPassForclosedGenes = position+posiCovMap.get(position).size();
				}
			}
			
			if(foundCurrentPosSupport == -1){
				// we have no split near currentPos, so indicate this via new transcript end (define end by a fake intron starting at currentPos + read length)
				
				searchLastIntronEndBeforeCurrentPos(gene,currentPos,searchTranscriptStart,inTranscriptMode);  // this intronEnd is not allowed to be paired with the next higher key, but instead it is defined as a new exon,
														// so indicate this for ExonSearch
			}
		}
		
		return new int[] {foundSupport,endToPassForclosedGenes,foundCurrentPosSupport};
	}
	
	/*
	 * if we are in currentCompete but between currentPos and nextPos is a large gap, search for a supporting split
	 * if none is found, erase the intron and all other ones, which end beyond the current position and close candidate gene at currentPos 
	 */
	
	public static int[] checkIfIntronSupported_fakeGene(Gene gene, int currentPos, int nextPos, TreeMap<Integer,Vector<Integer>> posiCovMap, int inTranscriptMode, int searchTranscriptStart, int endToPassForclosedGenes, int inNormalTranscript){
		
		int foundSupport = -1;
		int foundCurrentPosSupport = -1;
		for(int split : gene.possibleIntrons.keySet()){
			if(gene.possibleIntrons.get(split)[0] != null){
				for(int[] intron : ( (Vector<int[]>) gene.possibleIntrons.get(split)[0])){
					if(currentPos<=intron[0]){ 
						foundSupport = 0;
						foundCurrentPosSupport = 0;
						break;
					}else{
						if(nextPos <= (intron[1] + GeneFinder.readLength) && (Math.abs(nextPos - intron[1]) <= GeneFinder.readLength)){ 
							foundSupport = 0;
						}
						
						int fussyPossible = (Integer)(gene.possibleIntrons.get(split)[2]);
						if(currentPos <= fussyPossible){
							foundCurrentPosSupport = 0;
						}
					}
						
				}
			}
			if((foundCurrentPosSupport == 0) && (foundSupport == 0)){
				break;
			}
		}
		
		if(foundSupport != -1){
			for(int position : posiCovMap.keySet()){
				if(currentPos < (position-GeneFinder.readLength) && nextPos > (position+GeneFinder.readLength)){						
					// had no chance to look at it, so create fake intron to also regard this exon and close
					searchCompleteIntronForEndingAndMakeNewIsoform(gene, position,posiCovMap);
					endToPassForclosedGenes = position+posiCovMap.get(position).size();
				}
			}
			
			if(foundCurrentPosSupport == -1){
				// we have no split near currentPos, so indicate this via new transcript end (define end by a fake intron starting at currentPos + read length)
				
				searchLastIntronEndBeforeCurrentPos(gene,currentPos,searchTranscriptStart,inTranscriptMode);  // this intronEnd is not allowed to be paired with the next higher key, but instead it is defined as a new exon,
														// so indicate this for ExonSearch
			}
		}
		
		return new int[] {foundSupport,endToPassForclosedGenes,foundCurrentPosSupport};
	}
	
	/*
	 * check each support during the candidate initialization
	 */
	
    public static void checkIfIntronSupported_forGeneIni(Gene gene, int currentPos, int nextPos, TreeMap<Integer,Vector<Integer>> posiCovMap, int inTranscriptMode, int searchTranscriptStart, int endToPassForclosedGenes){
		
		int foundSupport = -1;
		int foundCurrentPosSupport = -1;
		for(int split : gene.possibleIntrons.keySet()){
			if(gene.possibleIntrons.get(split)[0] != null){
				for(int[] intron : ( (Vector<int[]>) gene.possibleIntrons.get(split)[0])){
					if(currentPos<=intron[0]){ 
						foundSupport = 0;
						foundCurrentPosSupport = 0;
						break;
					}else{
						if(nextPos <= (intron[1] + GeneFinder.readLength) && (Math.abs(nextPos - intron[1]) <= GeneFinder.readLength)){ 
							foundSupport = 0;
						}
						
						int fussyPossible = (Integer)(gene.possibleIntrons.get(split)[2]);
						if(currentPos <= fussyPossible){
							foundCurrentPosSupport = 0;
						}
					}
						
				}
			}
			if((foundCurrentPosSupport == 0) && (foundSupport == 0)){
				break;
			}
		}
		
		if(foundSupport == -1 && foundCurrentPosSupport == -1){
			
			if(searchTranscriptStart == -1){
				for(int position : posiCovMap.keySet()){
					if(currentPos < (position-GeneFinder.readLength) && nextPos > (position+GeneFinder.readLength)){						
						// had no chance to look at it, so create fake intron to also regard this exon and close							
						searchCompleteIntronForEndingAndMakeNewIsoform(gene, position,posiCovMap);
						endToPassForclosedGenes = position+posiCovMap.get(position).size();
					}
				}
			}
					
			if(foundCurrentPosSupport == -1){
				// we have no split near currentPos, so indicate this via new transcript end (define end by a fake intron starting at currentPos + read length)
						
				searchLastIntronEndBeforeCurrentPos(gene,currentPos,searchTranscriptStart,inTranscriptMode);  // this intronEnd is not allowed to be paired with the next higher key, but instead it is defined as a new exon,
																// so indicate this for ExonSearch
			}
	
		}
		
	}
	
	/*
	 * searches the intron which has the specified position as its ending
	 * for each intron we generate a new isoform that ends here
	 */
	
	public static void searchCompleteIntronForEndingAndMakeNewIsoform(Gene gene, int ending, TreeMap<Integer,Vector<Integer>> posiCovMap){
		
		for(int split : gene.possibleIntrons.keySet()){
			if(gene.possibleIntrons.get(split)[0] != null){
				for(int[] intron : ( (Vector<int[]>) gene.possibleIntrons.get(split)[0])){
					if(ending == intron[1]){ 							
						if(testIfAlternativeIsoformIsNotContained(gene,(ending+posiCovMap.get(ending).size()),ending)){
							gene.intronEndsThatAreNotContinued.add(intron);
							Vector<int[]> vecTemp = new Vector<int[]>();
							//vecTemp.add(intron);
							gene.alternativeTranscripts.add(new Object[] {(ending+posiCovMap.get(ending).size()),Integer.MAX_VALUE,-1,-1,ending,vecTemp});
						}						
					}
				}
			}
		}
	}
	
	/*
	 * this intronEnd is not allowed to be paired with the next higher key, but instead it is defined as a new exon,
	 * so indicate this for ExonSearch
	 */
	
	public static int searchLastIntronEndBeforeCurrentPos(Gene gene, int currentPos, int searchTranscriptStart, int inTranscriptMode){
		
		int[] maxEnd = {-1,-1};
		
		for(int split : gene.possibleIntrons.keySet()){
			if(gene.possibleIntrons.get(split)[0] != null){
				for(int[] intron : ( (Vector<int[]>) gene.possibleIntrons.get(split)[0])){
					if((currentPos>= intron[1]) && (maxEnd[1] < intron[1])){ 						
						maxEnd = intron;
					}
				}
			}
		}
		
		if(maxEnd[1] != -1 && (searchTranscriptStart == -1) && (inTranscriptMode == -1)){  // && (gene.possibleIntrons.higherKey(maxEnd[0]) == null)){
			if(checkIfExtractOk(gene, maxEnd, currentPos)){				
				gene.intronEndsThatAreNotContinued.add(maxEnd);
				Vector<int[]> vecTemp = new Vector<int[]>();
				gene.alternativeTranscripts.add(new Object[] {(currentPos+GeneFinder.readLength),Integer.MAX_VALUE,-1,-1,maxEnd[1],vecTemp});
				
				return 0;
			}
				
		}
		return -1;
		
	}
	
	/*
	 * if we have identified a possible intron end, check if this end is really feasible or if there is a contradicting splice site
	 */
	
	public static boolean checkIfExtractOk(Gene gene, int[] maxEnd, int currentPos){
		
		if(gene.possibleIntrons.higherKey(maxEnd[0]) == null){
			return true;
		}else{
			
			int higherKey = maxEnd[0];
			
			do{
				higherKey = gene.possibleIntrons.higherKey(higherKey);
				
				if((higherKey > maxEnd[1]) && (higherKey < currentPos)){
					return false;
				}			
				
			}while(gene.possibleIntrons.higherKey(higherKey) != null);
		}
		
		return true;
		
	}
	
	/*
	 * replace entry in alternative transcripts with one containing a stop position
	 */
	
	public static int replaceEntryInAltTransWithUpdatedOne(Gene gene, int inTranscriptMode, int currentPos, int indicatorIfWithinIntron, int nextPos){
		
		int foundSupport = -1;
		
		for(Object[] altTrans : gene.alternativeTranscripts){
			if(((Integer) altTrans[0]) == inTranscriptMode){
				if(!((indicatorIfWithinIntron == -1) && ((nextPos - currentPos) > (2*GeneFinder.readLength)))){	
					Object[] newEntry = new Object[6];
					newEntry[0] = altTrans[0];
					newEntry[1] = altTrans[1];
					newEntry[2] = altTrans[2];
					newEntry[3] = altTrans[3];
					newEntry[4] = indicatorIfWithinIntron; 
					Vector<int[]> vecTemp = new Vector<int[]>();
					newEntry[5] = vecTemp;
					
					gene.alternativeTranscripts.setElementAt(newEntry,gene.alternativeTranscripts.indexOf(altTrans));
					foundSupport = 0;   // to include the rnas
				}
				
				if(foundSupport == -1){
					gene.alternativeTranscripts.removeElement(altTrans);
					foundSupport = 2;
				}
				
				break;
			}
		}
		
		return foundSupport;
		
	}
	
	/*
	 * replace entry in alternative transcripts with one containing a stop position, which is also the end of the transcript
	 */
	
	public static int replaceEntryInAltTransWithUpdatedOne_endTranscript(Gene gene, int inTranscriptMode, int currentPos, int indicatorIfWithinIntron){
		
		int foundSupport = -1;
		
		for(Object[] altTrans : gene.alternativeTranscripts){
			if(((Integer) altTrans[0]) == inTranscriptMode){
				
				if((gene.alternativeTranscripts.get(gene.alternativeTranscripts.indexOf(altTrans)).length < 6) || ((gene.alternativeTranscripts.get(gene.alternativeTranscripts.indexOf(altTrans)).length == 6) && (indicatorIfWithinIntron != -1))){
					Object[] newEntry = new Object[7];
					newEntry[0] = altTrans[0];
					newEntry[1] = altTrans[1];
					newEntry[2] = altTrans[2];
					newEntry[3] = altTrans[3];
					newEntry[4] = indicatorIfWithinIntron; 
					Vector<int[]> vecTemp = new Vector<int[]>();
					newEntry[5] = vecTemp;
					newEntry[6] = Integer.MAX_VALUE;
					
					gene.alternativeTranscripts.setElementAt(newEntry,gene.alternativeTranscripts.indexOf(altTrans));
					foundSupport = 0;   // to include the rnas
				}
				
				break;
			}
		}
		
		return foundSupport;
		
	}
	
	
	/*
	 * this intronEnd is not allowed to be paired with the next higher key, but instead it is defined as a new exon,
	 * so indicate this for ExonSearch
	 * note that in the close gene version we try to make sure, that a new transcript is included to avoid big exons
	 */
	
	public static int searchLastIntronEndBeforeCurrentPos_closeGene(Gene gene, int splitTooBig, Vector<Integer> keysToErase){
		
		int[] maxEnd = {-1,-1};
		
		for(int split : gene.possibleIntrons.keySet()){
			if(gene.possibleIntrons.get(split)[0] != null){
				for(int[] intron : ( (Vector<int[]>) gene.possibleIntrons.get(split)[0])){
					if((splitTooBig>= intron[1]) && (maxEnd[1] < intron[1])){ 						
						maxEnd = intron;
					}
				}
			}
		}
		
		if(maxEnd[1] != -1){
			int indicator = checkIfExtractOk_closeGene(gene, maxEnd, splitTooBig);
			
			if(indicator == 1){ // simply extract the alternative transcript

				if(testIfAlternativeIsoformIsNotContained(gene,splitTooBig,maxEnd[1])){
					Vector<int[]> vecTemp = new Vector<int[]>();
					gene.intronEndsThatAreNotContinued.add(maxEnd);
					gene.alternativeTranscripts.add(new Object[] {splitTooBig,Integer.MAX_VALUE,-1,-1,maxEnd[1],vecTemp});
				}
	
				return 1;
			}
			
		}else{
			// check if splitTooBig is the only site or the first site, if yes, assign to gene start
			boolean assignToGeneStart = true;
			
			for(int split : gene.possibleIntrons.keySet()){
				if(gene.possibleIntrons.get(split)[0] != null){				
					if(split < splitTooBig){
						assignToGeneStart = false;
						break;
					}					
				}
			}
			
			if(assignToGeneStart){
				
				if(gene.alternativeTranscripts.size() > 0){
					// search for the alternative start with the left-most position
					int start = Integer.MAX_VALUE;
					int bestPos = -1;
					for(int posTrans = 0; posTrans < gene.alternativeTranscripts.size();++posTrans){
						
						Object[] altTrans = gene.alternativeTranscripts.get(posTrans);
						int startThis = -1;
						
						if(((Integer)altTrans[1]).intValue() == Integer.MAX_VALUE){
							startThis = ((Integer)altTrans[4]).intValue();
						}else{
							startThis = ((Integer)altTrans[0]).intValue();
						}
							
						if((startThis < start) && (startThis < splitTooBig)){
							gene.startPos = startThis;
							start = startThis;
							bestPos = posTrans;
						}					
					}
					
					if(bestPos != -1){
						// defined an alternative transcript as the new start, so this transcript is regarded as normal and can be erased
						gene.alternativeTranscripts.removeElementAt(bestPos);
					}
				}

				return 0;
			}
			
		}
		
		return -1;
		
	}
	
	/*
	 * this intronEnd is not allowed to be paired with the next higher key, but instead it is defined as a new exon,
	 * so indicate this for ExonSearch
	 * note that in the close gene version we try to make sure, that a new transcript is included to avoid big exons
	 */
	
	public static int checkSplitRemovalSituation(Gene gene, int splitTooBig, Vector<Integer> keysToErase){

		boolean assignToGeneStart = true;

		for(int split : gene.possibleIntrons.keySet()){
			if(gene.possibleIntrons.get(split)[0] != null){				
				if(split < splitTooBig){
					assignToGeneStart = false;
					break;
				}					
			}
		}

		if(assignToGeneStart){

			if(gene.alternativeTranscripts.size() > 0){
				// search for the alternative start with the left-most position
				int start = Integer.MAX_VALUE;
				int bestPos = -1;
				for(int posTrans = 0; posTrans < gene.alternativeTranscripts.size();++posTrans){

					Object[] altTrans = gene.alternativeTranscripts.get(posTrans);
					int startThis = -1;

					if(((Integer)altTrans[1]).intValue() == Integer.MAX_VALUE){
						startThis = ((Integer)altTrans[4]).intValue();
					}else{
						startThis = ((Integer)altTrans[0]).intValue();
					}

					if((startThis < start) && (startThis < splitTooBig)){
						gene.startPos = startThis;
						start = startThis;
						bestPos = posTrans;
					}					
				}

				if(bestPos != -1){
					// defined an alternative transcript as the new start, so this transcript is regarded as normal and can be erased
					gene.alternativeTranscripts.removeElementAt(bestPos);
				}
			}
		}
		return 0;
	}
	
	/*
	 * check if the new isoform is already contained
	 */
	
	public static boolean testIfAlternativeIsoformIsNotContained(Gene gene, int split, int end){
		
		for(Object[] altTrans : gene.alternativeTranscripts){
			if(altTrans.length >= 6){
				int start = (Integer) altTrans[0];
				int stop = (Integer) altTrans[4];
				
				if((start == split) && (stop == end)){
					return false;
				}
			}
		}
		
		return true;
	}
	
	/*
	 * if we have identified a possible intron end, check if this end is really feasible or if there is a contradicting splice site
	 */
	
	public static int checkIfExtractOk_closeGene(Gene gene, int[] maxEnd, int currentPos){
		
		if(gene.possibleIntrons.higherKey(maxEnd[0]) == null){
			return 1;
		}else{
			
			int higherKey = maxEnd[0];
			
			do{
				higherKey = gene.possibleIntrons.higherKey(higherKey);
				
				if((higherKey > maxEnd[1]) && (higherKey < currentPos)){
					return -1;
				}else if((higherKey > currentPos) && (higherKey < gene.stopPos)){
					return -1;					
				}
				
			}while(gene.possibleIntrons.higherKey(higherKey) != null);
		}
		
		return 1;
		
	}
	
	/*
	 * check whether an intron is already included for a certain splice site, and if not, add it
	 * note: most of the times these introns only emerge due to the merging of two clusters, so their supporting rna vector is empty!
	 */
	
	public static void checkIfIntronIsIncluded_AndAdd(Gene cluster, int[] intron){
		
		
		if(cluster.possibleIntrons.containsKey(intron[0])){
			
			if((cluster.possibleIntrons.get(intron[0])[0] != null)){
				boolean foundIntron = false;
				
				for(int[] intronOld : ((Vector<int[]>)cluster.possibleIntrons.get(intron[0])[0])){
					if(intronOld[1] == intron[1]){
						foundIntron = true;
						break;
					}
				}
				
				if(!foundIntron){
					Vector<Rna> rnaTmp = new Vector<Rna>();
					( (Vector<int[]>) cluster.possibleIntrons.get(intron[0])[0]).add(intron);
					( (Vector<Vector<Rna>>) cluster.possibleIntrons.get(intron[0])[1]).add(rnaTmp);
				}
			}
				
		}else{
			
			Vector<int[]> intronTmp = new Vector<int[]>();
			intronTmp.add(intron);
			Vector<Rna> rnaTmp = new Vector<Rna>();
			Vector<Vector<Rna>> rnaVec = new Vector<Vector<Rna>>();
			rnaVec.add(rnaTmp);
			
			Object[] tmp = {intronTmp,rnaVec,-1,-1};
			cluster.possibleIntrons.put(intron[0],tmp);
		}
	}
	
	/*
	 * extract all introns supported by a sufficient number of reads
	 * posiCovMap contains positions that belong to different introns and indicates how the coverage map has to be updated at this position
	 */
	
	public static Object[] findIntrons_RespectAlternative(Gene cluster, Contig thisContig, int splitStart, int currentPos, int currentCompete, TreeMap<Integer,Vector<Integer>> posiCovMap, double limit){
		
		Vector<int[]> listOfIntrons = new Vector<int[]>();  // contains all introns supported by this splice site (note: first take all possible introns, remove introns without sufficient support later)
		Vector<Rna> rnasThatDoNotFit = new Vector<Rna>();  // store all rnas that have to be erased from this position
		Vector<Rna> rnasThatDoNotSupportAnySplit = new Vector<Rna>();  // store all rnas that are not split reads
		
		Vector<Integer> otherSpliceSites = new Vector<Integer>();  // if the currentSite does not have enough support, but others in reach, store them to regard them in coverage update
		
		boolean hadSiteBefore = false;
		
		if(cluster.possibleIntrons.containsKey(splitStart) && cluster.possibleIntrons.get(splitStart)[0] != null){
			listOfIntrons = (Vector<int[]>) cluster.possibleIntrons.get(splitStart)[0];			
			// simply add up all rna vectors if new support is found
			hadSiteBefore = true;
		}
		
		for(Rna rna : thisContig.positionTOmappingRnas.get(currentPos)){
			
			int occurenceCounter = 0;
			
			Vector<Object[]> badInfo = new Vector<Object[]>();   // if a splice site is not accepted, remove this info and remove this read from the current position
			
			for(Object[] info : rna.contigsMappedOn){
				if(((Integer) info[1] == currentPos)  && (((Contig) info[0]).equals(thisContig))){
					
					occurenceCounter++;
				
					if(info[4] != null){
						if(((int[])info[4])[1] == splitStart){
							if(thisContig.splicePositions.get(splitStart) >= limit){
								Object[] returnObject = findRightIntronAndAddRna(cluster, currentPos, ((int[])info[4])[1] , hadSiteBefore, listOfIntrons, posiCovMap, rna, info);

								posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnObject[0];
								boolean updateCompete = (Boolean) returnObject[1];
								listOfIntrons = (Vector<int[]>) returnObject[2];

								if((currentCompete == -1) || updateCompete){							
									currentCompete = Math.max(currentCompete,(((int[])info[4])[0] + splitStart));
								}
								
								if(info[6] != null){
									if(((String)info[6]).equals("+")){
										cluster.direcCounter[0]++;
									}else{
										cluster.direcCounter[1]++;
									}
								}
							}else{
								badInfo.add(info);
							}
						}else{
							if(thisContig.splicePositions.get(((int[])info[4])[1]) >= limit){  
								otherSpliceSites.add(((int[])info[4])[1]);
								Vector<int[]> listOfIntrons_alternative = new Vector<int[]>();
								
								boolean hadSiteBefore_alternative = false;
								
								if(cluster.possibleIntrons.containsKey(((int[])info[4])[1]) && cluster.possibleIntrons.get(((int[])info[4])[1])[0] != null){
									listOfIntrons_alternative = (Vector<int[]>) cluster.possibleIntrons.get(((int[])info[4])[1])[0];			
									// simply add up all rna vectors if new support is found
									hadSiteBefore_alternative = true;
								}
								
								Object[] returnObject = findRightIntronAndAddRna(cluster, currentPos, ((int[])info[4])[1] , hadSiteBefore_alternative, listOfIntrons_alternative, posiCovMap, rna, info);
								
								posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnObject[0];
								
								listOfIntrons_alternative = (Vector<int[]>) returnObject[2]; // TODO: was listOfIntrons before!
								
								if(currentCompete == -1){  // at the moment no open alternative interval, so open a new one
									
									if(cluster.possibleIntrons.containsKey(splitStart)  && cluster.possibleIntrons.get(splitStart)[0] != null && !((Vector<int[]>)cluster.possibleIntrons.get(splitStart)[0]).isEmpty()){
										currentCompete = Math.max(((Vector<int[]>) cluster.possibleIntrons.get(splitStart)[0]).get(0)[1],(((int[])info[4])[0] + ((int[])info[4])[1]));
									}else{
										currentCompete = ((int[])info[4])[0] + ((int[])info[4])[1];
									}
									
								}else{
									currentCompete = Math.max(currentCompete,(((int[])info[4])[0] + ((int[])info[4])[1]));
								}
								
								if(info[6] != null){
									if(((String)info[6]).equals("+")){
										cluster.direcCounter[0]++;
									}else{
										cluster.direcCounter[1]++;
									}
								}
								
							}else{
								// else we will not accept this site, so delete this entry for the mappings of the current read
								badInfo.add(info);
							}
							
						}
						
					}else{
						if(thisContig.splicePositions.get(splitStart) >= limit){
							if(!rnasThatDoNotSupportAnySplit.contains(rna)){
								rnasThatDoNotSupportAnySplit.add(rna);	
								HelperFunctions_GeneSearch.updatePosiCovMap(posiCovMap, splitStart, (GeneFinder.readLength - (splitStart-currentPos)));
							}
						}
									
					}
						
				}
			}
			
			if(badInfo.size() > 0){
				
				for(Object[] info : badInfo){
					rna.contigsMappedOn.removeElement(info);
					rna.assignedNum--; 
					if(rna.contigsMappedOn.size() == 0){
						rnasThatDoNotFit.add(rna);
					}
								
				}
				
				if(occurenceCounter <= badInfo.size() && !(rnasThatDoNotFit.contains(rna))){
					rnasThatDoNotFit.add(rna);
				}
								
			}
			
			if(occurenceCounter > 1){
				for(int i = (occurenceCounter - badInfo.size()); i > 1;i--){  // if there are x occurrences, we include x-1 in moreThanOnehitRnas and 1 in idTomaapingRnas
					cluster.moreThanOneHitRnas.add(rna.rnaID);
				}
			}
			
			
		}	
		
		if(rnasThatDoNotFit.size() > 0){
			thisContig.positionTOmappingRnas.get(currentPos).removeAll(rnasThatDoNotFit);
		}
		
		if(rnasThatDoNotSupportAnySplit.size() != 0){
			if(cluster.possibleIntrons.containsKey(splitStart)){
				cluster.possibleIntrons.get(splitStart)[2] = (currentPos + GeneFinder.readLength);
			}else{
				Vector<int[]> intronVec_tmp = new Vector<int[]>();
				Vector<Vector<Rna>> rnaTmp = new Vector<Vector<Rna>>();		
				Object[] objectTmp = {intronVec_tmp,rnaTmp,(currentPos + GeneFinder.readLength),-1};
				cluster.possibleIntrons.put(splitStart,objectTmp);
			}
			
			if(cluster.possibleFussyExons.containsKey(splitStart)){
				cluster.possibleFussyExons.get(splitStart).addAll(rnasThatDoNotSupportAnySplit);
			}else{
				cluster.possibleFussyExons.put(splitStart,rnasThatDoNotSupportAnySplit);
			}
			
		}
		
		Object[] returnObject ={currentCompete,posiCovMap,rnasThatDoNotSupportAnySplit,otherSpliceSites};
		
		return returnObject;
	}
	
	/*
	 * help function to search for the right intron and add the rna
	 */
	
	public static Object[] findRightIntronAndAddRna(Gene cluster, int currentPos, int splitStart, boolean hadSiteBefore, Vector<int[]> listOfIntrons, TreeMap<Integer,Vector<Integer>> posiCovMap, Rna rna, Object[] info){
		
		
		boolean foundIntron = false;
		
		for(int pos = 0; pos < listOfIntrons.size(); ++pos){
			int[] intron = listOfIntrons.get(pos);
			if(intron[1] == (((int[])info[4])[0] + splitStart)){
				boolean updateMap = true;
				// already have this intron, so add to list of supporting rnas
				if(hadSiteBefore){
					if(!(((Vector<Vector<Rna>>) cluster.possibleIntrons.get(splitStart)[1]).get(pos).contains(rna))){
						((Vector<Vector<Rna>>) cluster.possibleIntrons.get(splitStart)[1]).get(pos).add(rna);
					}else{
						updateMap = false;
					}
					
				}else{									
					updatePossibleIntronsOfCluster(cluster, splitStart, rna,((int[])info[4])[0]);
					listOfIntrons = (Vector<int[]>) cluster.possibleIntrons.get(splitStart)[0];	
					hadSiteBefore = true;
				}
				foundIntron = true;
				if(updateMap){
					HelperFunctions_GeneSearch.updatePosiCovMap(posiCovMap,(((int[])info[4])[0] + splitStart), (GeneFinder.readLength - (splitStart-currentPos)));
				}
				break;   // break to get out of this intron for-loop
			}
		}
		boolean updateCompete = false;
		
		if(!foundIntron){
			
			if(hadSiteBefore){
				int[] intron_tmp = {splitStart,((int[])info[4])[0] + splitStart};
				((Vector<int[]>) cluster.possibleIntrons.get(splitStart)[0]).add(intron_tmp);
				Vector<Rna> rnaTmp = new Vector<Rna>();
				rnaTmp.add(rna);
				((Vector<Vector<Rna>>) cluster.possibleIntrons.get(splitStart)[1]).add(rnaTmp);
				updateCompete = true;
			}else{
				updatePossibleIntronsOfCluster(cluster, splitStart, rna,((int[])info[4])[0]);									
				hadSiteBefore = true;
				updateCompete = true;
			}
			
			listOfIntrons = (Vector<int[]>) cluster.possibleIntrons.get(splitStart)[0];	
			
			HelperFunctions_GeneSearch.updatePosiCovMap(posiCovMap,(((int[])info[4])[0] + splitStart), (GeneFinder.readLength - (splitStart-currentPos)));
		}

		return new Object[] {posiCovMap,updateCompete,listOfIntrons};
	}
	
	/*
	 * function to update the possible intron feature of the cluster, needed for alternative splicing
	 */
	
	public static void updatePossibleIntronsOfCluster(Gene cluster, int splitStart, Rna rna, int sizeIntron){
		
		Object[] objectTmp = new Object[4];
		Vector<int[]> intronVec_tmp = new Vector<int[]>();
		Vector<Vector<Rna>> rnaTmp = new Vector<Vector<Rna>>();		
		Vector<Rna> firstVec = new Vector<Rna>();
		
		firstVec.add(rna);
		int[] intron_tmp = {splitStart, sizeIntron + splitStart};
		intronVec_tmp.add(intron_tmp);
		rnaTmp.add(firstVec);
		
		objectTmp[0] = intronVec_tmp;
		objectTmp[1] = rnaTmp;
		objectTmp[2] = -1;
		objectTmp[3] = -1;
		
		cluster.possibleIntrons.put(splitStart,objectTmp);
		
	}
	
	
	/*
	 * after a splice site change, check if the introns of the last splice site have enough support
	 */
	
	public static Object[] checkIntronsAfterSpliceSiteSwitch(Gene cluster, int spliceSite, int currentMaxInterval, TreeMap<Integer,Vector<Integer>> posiCovMap, int nextPos, double limit){
		
		int maxInterval = -1;
		boolean changeCurrentMaxInterval = false;
		boolean foundAtLeastOneIntron = false;	// if set to true, this indicates that this splice site can stay in possible introns
		boolean switchedFromFussyToExon = false;
		boolean notContained = false;   // indicates whether this spliceSite is contained in cluster or not
		
		if(cluster.possibleIntrons.containsKey(spliceSite)){
			for(int position = ((Vector<int[]>)cluster.possibleIntrons.get(spliceSite)[0]).size() - 1; position >= 0; --position){
				if(((Vector<Vector<Rna>>)cluster.possibleIntrons.get(spliceSite)[1]).get(position).size() < limit){

					// intron has not enough support, so erase and also delete rnas from cluster
					
					int[] badIntron = ((Vector<int[]>)cluster.possibleIntrons.get(spliceSite)[0]).get(position);
					((Vector<int[]>)cluster.possibleIntrons.get(spliceSite)[0]).removeElementAt(position);
					
					if(badIntron[1] == currentMaxInterval){
						int localMax = checkIfOtherSpliceSitesSupportCurrentmax(cluster,currentMaxInterval,nextPos);
						if(localMax != -1){
							maxInterval = localMax;
						}
						changeCurrentMaxInterval = true;											
					}
					
					for(Rna rna : ((Vector<Vector<Rna>>)cluster.possibleIntrons.get(spliceSite)[1]).get(position)){
						
						if(cluster.moreThanOneHitRnas.contains(rna.rnaID)){
							// if it is contained here, this serves as one "back-up life" that is now destroyed
							cluster.moreThanOneHitRnas.remove(rna.rnaID);
						}else{
							cluster.idTOassociatedRnas.remove(rna.rnaID);
						}
						
						for(Object[] info : rna.contigsMappedOn){
							if(info[4] != null){
								if((((int[])info[4])[1] == badIntron[0]) && ((((int[])info[4])[0]) + (((int[])info[4])[1]) == badIntron[1])){
																	
									int diffToSplitStart = (badIntron[0] - (Integer) info[1]);
									
									posiCovMap = HelperFunctions_GeneSearch.updatePosiCovMap_AfterSpliceSwitch(posiCovMap,badIntron[1],(GeneFinder.readLength-diffToSplitStart));
									
									rna.contigsMappedOn.removeElement(info);
									rna.assignedNum = rna.assignedNum - 1;
									break;
								}
							}
						}
			
					}
					
					((Vector<Vector<Rna>>)cluster.possibleIntrons.get(spliceSite)[1]).removeElementAt(position);					
				
				}else{
					maxInterval = Math.max(maxInterval,((Vector<int[]>)cluster.possibleIntrons.get(spliceSite)[0]).get(position)[1]);
					foundAtLeastOneIntron = true;
				}
			}
		}else{
			notContained = true;
		}
		
		if(changeCurrentMaxInterval){
			currentMaxInterval = maxInterval;     // if all splice sites have been erased, set to -1, but this is as desired
		}
		
		if(!foundAtLeastOneIntron && cluster.possibleIntrons.containsKey(spliceSite)){
			// also remove this splice site from possible intron map, only remember "fussy exon", if exist
			
			int fussyExon = ((Integer)cluster.possibleIntrons.get(spliceSite)[2]);
			
			if(fussyExon != -1){
				// remove from fussy exons, serves as real exon now!
				
				if(cluster.possibleFussyExons.containsKey(spliceSite)){
					cluster.possibleFussyExons.remove(spliceSite);
				}else{
					System.err.println("Splice site not in fussy exons!");
				}
				
				switchedFromFussyToExon = true;
			}
			
			cluster.possibleIntrons.remove(spliceSite);
		}
		
		Object[] returnObject = {currentMaxInterval,posiCovMap,switchedFromFussyToExon,notContained};
		
		return returnObject;
	}
	
	/*
	 * only change currentCompete interval if there are no other splice sites supporting it
	 */
	
	public static Integer checkIfOtherSpliceSitesSupportCurrentmax(Gene cluster, int currentMaxInterval, int nextPos){
		
		int localMax = -1;
		
		for(int key : cluster.possibleIntrons.keySet()){
			if(cluster.possibleIntrons.get(key)[0] != null){
				for(int[] intron : ( (Vector<int[]>) cluster.possibleIntrons.get(key)[0])){
					if(intron[1] >= nextPos){
						if(intron[1] > localMax){
							localMax = intron[1];
						}
					}
				}
			}
		}
		
		return localMax;
	}
	
	/*
	 * find all intron ends associated with the given spliceSite
	 */
	
	public static Vector<Integer> findIntronEnds(int spliceKey, int nextPos,Contig thisContig){

		Vector<Integer> intronEndings = new Vector<Integer>();

		for(Rna rna : thisContig.positionTOmappingRnas.get(nextPos)){		

			for(Object[] info : rna.contigsMappedOn){
				if(((Integer) info[1] == nextPos)  && (((Contig) info[0]).equals(thisContig))){

					if(info[4] != null){

						if(thisContig.splicePositions.get(spliceKey) >= GeneFinder.spliceLim){
							int ending = ((int[])info[4])[0] + ((int[])info[4])[1];
							if(!intronEndings.contains(ending)){
								intronEndings.add(ending);
							}

						}


					}
				}
			}
		}
		
		return intronEndings;
	}
	
	/*
	 * manages the splice analysis
	 */
	
	public static Object[] performSpliceAna(Gene cluster, Contig thisContig, int spliceKey, int currentPos, int nextPos, int currentCompete, int currentCompeteStart, TreeMap<Integer,Vector<Integer>> posiCovMap){
		
		Object[] returnFromAna = new Object[5]; // currentCompete, currentCompeteStart, posiCovMap, considerSpliceSiteForCovUpdate, rnasThatDoNotSupportSplit
		
		// find splits associated with this site, only take introns into account that are supported by a sufficient number of reads	
		
		Object[] returnObject = IntronExonSearch.findIntrons_RespectAlternative(cluster, thisContig, spliceKey, nextPos, currentCompete, posiCovMap, GeneFinder.spliceLim);

		returnFromAna[0] = (Integer) returnObject[0];
		
		if(((Integer)returnFromAna[0]).intValue() != -1){
			if(spliceKey < ((Integer)returnFromAna[0]).intValue() && (currentPos > currentCompeteStart)){
				returnFromAna[1] = spliceKey;
			}else{
				returnFromAna[1] = currentCompeteStart;
			}
		}else{
			returnFromAna[1] = -1;
		}
		
		returnFromAna[2] = (TreeMap<Integer,Vector<Integer>>) returnObject[1];
		
		returnFromAna[4] = ((Vector<Rna>) returnObject[2]);

		if(thisContig.splicePositions.get(spliceKey) >= GeneFinder.spliceLim){
			returnFromAna[3] = spliceKey;
		}else{
			Vector<Integer> otherSpliceSites = ((Vector<Integer>) returnObject[3]);
			if(!otherSpliceSites.isEmpty()){
				int minSite = Integer.MAX_VALUE;
				for(int site : otherSpliceSites){
					if(site < minSite){
						minSite = site;
					}
				}
				returnFromAna[3] = minSite;
			}else{
				returnFromAna[3] = -1;
			}
		}
		
		return returnFromAna;
	}
}
