#!/usr/bin/python

__author__		= "Sander Granneman"
__copyright__	= "Copyright 2018"
__version__		= "1.5.8"
__credits__		= ["Sander Granneman"]
__maintainer__	= "Sander Granneman"
__email__		= "sgrannem@staffmail.ed.ac.uk"
__status__		= "Production"

##################################################################################
#
#	ParseAlignments.py
#
#
#	Copyright (c) Sander Granneman 2018
#	
#	Permission is hereby granted, free of charge, to any person obtaining a copy
#	of this software and associated documentation files (the "Software"), to deal
#	in the Software without restriction, including without limitation the rights
#	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#	copies of the Software, and to permit persons to whom the Software is
#	furnished to do so, subject to the following conditions:
#	
#	The above copyright notice and this permission notice shall be included in
#	all copies or substantial portions of the Software.
#	
#	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#	THE SOFTWARE.
#
##################################################################################

import re
import sys
import six
import numpy as np
from collections import defaultdict
from pyCRAC.Classes.PairedReads import *
from pyCRAC.Methods import makeBinaryIndexFile,readFileIndex

#----------------------------------------------------------------- PARSECOUNTERSOUTPUT CLASS ------------------------------------------------------------------#

class ParseCountersOutput():
	"""This class parses the gtf output file generated by pyReadCounters and pyMotif"""
	def __init__(self,input_file,index=False):
		"""Initialises the class attributes"""
		if isinstance(input_file,str):					# This allows you to pipe data from the standard input. the input file would be sys.stdin
			if index:									# you can index the GTF file and do a seek.
				makeBinaryIndexFile(input_file)
				self.__fileindex = readFileIndex(input_file)
			self.input_file = open(input_file,"r")
		elif hasattr(input_file,'__iter__'):   
			self.input_file = input_file				# if it is a file object, standard input or a generator
		else:
			raise TypeError("\nCould not determine the file type\n")
		self.count			   = defaultdict(lambda: defaultdict(int))
		self.readsareclustered = False
		self.duplicatesremoved = False
		self.file_name		   = input_file
		self.read_start		   = int()
		self.read_end		   = int()
		self.genes			   = list()
		self.substitutions	   = tuple()
		self.deletions		   = tuple()
		self.chromosome		   = str()
		self.number_of_reads   = 1.0
		self.strand			   = str()
		self.attributes		   = str()
		self.mapped_reads	   = int()
		self.total_reads	   = int()
		self.single_reads	   = int()
		self.paired_reads	   = int()
		self.line			   = str()
		self.gff_type		   = str()
		self.file_header	   = list()	   # this list contains all the lines that start with a bracket. Having the headers in output files is essential.
		self.total_cDNAs	   = int()
		self.gtfdatatype	   = "reads"
		self.__clusters_regex  = re.compile("[0-9]+S\d+.\d+D\d+.\d+|[0-9]+S\d+.\d+|[0-9]+D\d+.\d+")
		self.__dels_regex	   = re.compile("([0-9]+)D")
		self.__subs_regex	   = re.compile("([0-9]+)S")
		self.__gene_regex	   = re.compile("\"(.*)\"")

	def __checkHeaderInfo(self,line):
		"""looks at header info and checks whether the data are clustered or had blocks removed"""
		if re.search("##gff-version 2",line):
			self.gff_type = "GTF"
		if re.search("clusters",line):
			self.gtfdatatype = "clusters"
			self.readsareclustered = True
			self.duplicatesremoved = False
		elif re.search("blocks",line):
			self.gtfdatatype = "cDNAs"
			self.readsareclustered = False
			self.duplicatesremoved = True
		elif re.search("peaks",line):
			self.gtfdatatype = "peaks"
			self.readsareclustered = False
			self.duplicatesremoved = True
		if re.search("total",line):
			self.file_header.append(line)
		if re.search("total number of reads",line):
			self.total_reads  = int(line.split("\t")[1])
		elif re.search("total number of mapped",line):
			self.mapped_reads = int(line.split("\t")[1])
		elif re.search("total number of single",line):
			self.single_reads = int(line.split("\t")[1])
		elif re.search("total number of paired",line):
			self.paired_reads = int(line.split("\t")[1])

	def __checkDataType(self):
		"""In case the header has been removed from the pyReadCounters or pyMotif GTF file, then this method is used to \
		read the second column (source) to see if the coordinates are for clusters, cDNAs or just reads"""
		if self.source:
			if re.search("cluster",self.source,re.I):
				self.gtfdatatype = "clusters"
				self.readsareclustered = True
				self.duplicatesremoved = False
			elif re.search("cDNA",self.source,re.I):
				self.gtfdatatype = "cDNAs"
				self.readsareclustered = False
				self.duplicatesremoved = True
			elif re.search("peak",self.source,re.I):
				self.gtfdatatype = "peaks"
				self.readsareclustered = False
				self.duplicatesremoved = True
			else:
				self.readsareclustered = False
				self.duplicatesremoved = False	

	def intervalIterator(self):
		"""Returns a generator for read or cluster positions"""
		for i in range(self.read_start, self.read_end):
			yield i

	def getGenes(self,attribute="gene_name",returnall=False):
		"""Looks for the gene_name in attributes and returns associated gene names, if any"""
		attribute_list = self.attributes.split(";")
		for i in attribute_list:
			if re.search(attribute,i):
				genes = self.__gene_regex.findall(i)
				if genes:
					return genes[0].split(",")
				else:
					return []

	def seek(self,linenumber):
		""" jumps to position in file at linenumber using an index file and index array. 
		Then it uses the next() function to start at the beginning of the line. """
		try:
			self.input_file.seek(self.__fileindex[linenumber])
			six.next(self.input_file)
		except IndexError:
			sys.stderr.write("IndexError at line:hg\t%s\nwhilst processing line:\n%s\n" % (linenumber,self.line.strip()))

	def getMutations(self,mutsfrequency=0.0):
		"""Looks for mutation information attributes and substitutions and deletions in separate tuples, if any.
		From pyReadCounters:
			if strand	== "F" : corrpos = muts - start + 1 # 1-based coordinate
			elif strand == "R" : corrpos = end	- muts	
		"""		  
		if not self.attributes:
			return [],[]
		deletions	  = list()
		substitutions = list()
		mutsfrequency = float(mutsfrequency)
		if self.gtfdatatype == "clusters":
			mutations = self.__clusters_regex.findall(self.comments)
			for muts in mutations:
				dels = list()
				subs = list()
				pos	 = re.split("[DS]",muts)
				try:
					if "D" in muts: dels = [int(pos[0]),float(pos[-1])]
					if "S" in muts: subs = [int(pos[0]),float(pos[1])]
					if dels and dels[1] >= mutsfrequency: deletions.append(dels[0]-1)			# convert them to zero based coordinates
					if subs and subs[1] >= mutsfrequency: substitutions.append(subs[0]-1) 
				except ValueError:
					sys.stderr.write("ValueError at line:\t%s. Could not process the mutations\n" % self.line.strip())
		else:
			dels = self.__dels_regex.findall(self.comments)
			subs = self.__subs_regex.findall(self.comments)
			if dels: deletions	   = [int(i)-1 for i in dels]
			if subs: substitutions = [int(i)-1 for i in subs]
		return substitutions,deletions

	def processAttributes(self,mutsfrequency,numpy=False,tuples=False,collectmutations=False):
		""" Processes the attributes in column 9 """
		self.comments = str()
		if "#" in self.attributes:
			self.comments = self.attributes.split("#")[-1]
			self.comments = self.comments.strip()
			self.comments = self.comments.strip(";")
		self.genes		= self.getGenes(attribute="gene_name")
		self.gene_ids	= self.getGenes(attribute="gene_id")
		if collectmutations:
			self.substitutions,self.deletions = self.getMutations(mutsfrequency)
			if numpy:
				self.substitutions = np.array(self.substitutions)
				self.deletions	   = np.array(self.deletions)
			elif tuples:
				self.substitutions = tuple(self.substitutions)
				self.deletions	   = tuple(self.deletions)

	def dataToDict(self,mutsfrequency=0,collectmutations=True):
		"""Opens the file and loads the information into a dictionary"""
		while self.readLineByLine(numpy=False,tuples=False,mutsseqpos=False,mutsfrequency=mutsfrequency,collectmutations=collectmutations,getattributes=True):
			interval = (self.chromosome,self.strand,self.read_start,self.read_end,tuple(self.substitutions),tuple(self.deletions))
			self.count[interval]["coverage"] = self.number_of_reads
			self.count[interval]["features"] = self.genes

	def readLineByLine(self,numpy=False,tuples=False,mutsseqpos=False,mutsfrequency=0,collectmutations=True,getattributes=True):
		"""Takes the input line, splits it and assigns values to the class attributes.
		If mutsseqpos is False then the 1-based coordinates for substitutions and deletions will be returned"""
		line_count = 0
		while True:
			self.number_of_reads = 1
			try:
				self.line = six.next(self.input_file) 
			except StopIteration:
				self.input_file.close()
				return False
			if self.line.startswith("#"):
				self.__checkHeaderInfo(self.line)
				#assert self.gff_type, "\n\nThe first line of the input file must be: ##gff-version 2\nPlease verify your input file.\n"
				continue
			#print "\t%s" % self.line.strip()
			Fld =  self.line.strip("\n").split("\t")
			self.chromosome = Fld[0]
			self.source		= Fld[1]
			self.type		= Fld[2]
			self.read_start = int(Fld[3]) - 1	# GTF annotation files contain 1-based coordinates, so 1 has to be subtracted from each value
			self.read_end	= int(Fld[4])
			self.score		= Fld[5]
			self.strand		= Fld[6]
			self.frame		= Fld[7]
			if getattributes:
				try:
					self.attributes = Fld[8]
					self.processAttributes(mutsfrequency,numpy=numpy,tuples=tuples,collectmutations=collectmutations)
				except IndexError:
					sys.stderr.write("IndexError at line %s\nAttributes are missing\n" % self.line)
			line_count += 1
			if line_count == 1: self.__checkDataType()
			if not self.readsareclustered or not self.duplicatesremoved:
				try:
					self.number_of_reads = int(self.score)
				except ValueError:
					try:
						self.number_of_reads = float(self.score)
					except ValueError:
						self.number_of_reads = 1.0
			return True

#----------------------------------------------------------------- PARSEALIGNMENTSFILE CLASS ------------------------------------------------------------------#

class ParseAlignmentFile():
	""" This class can be used to process Novoalign and SAM single and paired-end output formats
	It uses separate Novoalign and SAM modules (Novoalign.py and SAM.py) to parse each line in the alignment file"""
	def __init__(self,input_file,file_type="novo",base_quality=30,debug=None):
		self.count			   = defaultdict(lambda: defaultdict(int))
		self.file_name		   = input_file
		self.file_type		   = file_type
		self.total_reads	   = 0
		self.total_cDNAs	   = 0
		self.mapped_reads	   = 0
		self.paired_reads	   = 0
		self.single_reads	   = 0
		self.hasmutation	   = False
		self.readsareclustered = False
		self.duplicatesremoved = False
		self.readinterval	   = None
		self.line			   = str()
		self.__base_quality	   = base_quality
		self.__paired_reads	   = PairedReadCollector()
		self.__readtrackerdict = defaultdict(set)
		self.__discarded	   = None
		if debug: 
			self.__discarded = open(debug, "w")
		if re.search("novo",file_type,re.I):
			from pyCRAC.Parsers import Novoalign
			self.readinterval = Novoalign.Process_Novo(input_file,base_quality) 
		elif re.search("sam",file_type,re.I):
			from pyCRAC.Parsers import SAM
			self.readinterval = SAM.Process_Sam(input_file,base_quality)
		else:
			pass

	def printToDiscardedFile(self,line):
		""" To tell the program to print to the debug output file """
		if self.__discarded: 
			self.__discarded.write(line)

	def readisDuplicate(self,interval,muts=True):
		""" Takes a set() list and checks whether tuples with read_coordinates are already present
		in the set. If not, it will add the coordinates to the list, else it will return False.
		The reason for using sets is because membership tests are much faster than regular python lists and 
		the list doesn't need to be ordered."""
		chromosome = interval[0]
		input	   = str(interval)	# making a string out of the input coordinates makes comparison a lot easier/faster
		if not muts:
			input = str(interval[:4])
		if input in self.__readtrackerdict[chromosome]:
			return True
		else:
			self.__readtrackerdict[chromosome].add(input)
			return False

#--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#
											##### COUNT ALL READS #####
#--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#

	def countReads(self,align_quality=0,align_score=0,length=1000,maximum=float("infinity"),blocks=False,force_single_end=False,unique=False,distance=1000,muts_filter=None,properpair=False,correct_seq=True):
		"""Counts the number of times reads were mapped to the same chromosomal coordinates. Before running any other counting method you have to run this one first. 
		The dictionary created by this program collects the hits for reads with the same start and end mapping position using the
		chromosome position, strand, read_start, read_end, substitutions and deletions variables (in exactly that order). For example: self.count[chromosome,strand,read_start,read_end] += 1.
		Many variables can be set: read length(i.e. length=18), how many mapped reads you want to have analyzed (maximum=10000),
		whether you only want reads with a single alignment location included (unique=True) and you can also set alignment scores and alignment qualities (align_quality=50, align_score=60).
		This method can handle both Novoalign native and BAM/SAM single and paired-end output formats"""
		self.duplicatesremoved = blocks		
		ispaired = None

		while self.readinterval.readline(correct_seq=correct_seq):
			interval = tuple()
			if properpair:
				if self.paired_reads >= maximum or not self.readinterval():
					return False
			else:
				if self.mapped_reads >= maximum or not self.readinterval():
					return False
			self.total_reads += 1

#---------------------------------------------------------------- Processing all the read filtering options ------------------------------------------------------------------#

			# checks if:
			# the read is mapped
			# the alignquality is acceptable
			# the alignscore is acceptable
			# the read has the right length
			# the read is uniquely mapped
			# the read has the desired mutations

			if not self.readinterval.ismapped()												: self.printToDiscardedFile(self.readinterval()); continue
			if align_quality and not self.readinterval.alignQualityThreshold(align_quality) : self.printToDiscardedFile(self.readinterval()); continue
			if align_score	 and not self.readinterval.alignScoreThreshold(align_score)		: self.printToDiscardedFile(self.readinterval()); continue
			if length		 and not self.readinterval.lengthThreshold(length)				: self.printToDiscardedFile(self.readinterval()); continue
			if unique		 and not self.readinterval.uniqueAlnLocation()					: self.printToDiscardedFile(self.readinterval()); continue
			if muts_filter:	 self.hasmutation = self.readinterval.filterMutations(muts_filter)		# allows filtering of mutations. If the read has the desired mutation(s) then the filterMutations method will return True, else False.

#---------------------------------------------------------------- For processing sequencing data with single end reads ------------------------------------------------#

			if force_single_end:				# in case you do not want pyReadCounters to automatically connect the paired reads.
				ispaired = False
			else:
				ispaired = self.readinterval.read_start_paired()
			if not ispaired and not properpair:
				self.line	  = self.readinterval()
				substitutions = tuple(self.readinterval.substitutions(chromosome_location=True))
				deletions	  = tuple(self.readinterval.deletions(chromosome_location=True))
				interval	  = (self.readinterval.chr_ID(),self.readinterval.strand(),self.readinterval.read_start(),self.readinterval.read_end(),substitutions,deletions)
				if blocks:
					self.count[interval]['coverage']  = 1 
				else: 
					self.count[interval]['coverage'] += 1
				self.single_reads += 1
				self.mapped_reads += 1
				continue

#--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#

										##### countReads #####

#---------------------------------------------------------------- For processing paired-end data -----------------------------------------------------------------------------#

			elif ispaired and self.readinterval.paired_sequence(distance=distance):
				self.__paired_reads.addRead(self.readinterval)
				if self.__paired_reads.numberOfReadsinList() < 2:												# to make sure that two reads have been processed before continuing.
					self.line = self.readinterval()
					continue
				if self.__paired_reads.ispaired():																# if two reads are proper pairs, then analyze them.
					self.line = "%s%s" % (self.line,self.readinterval())
					if self.__paired_reads.cDNALength() <= length:												# This bit will determine whether the read is proper length and proper align quality and/or align score
						chromosome	  = self.__paired_reads.chr_ID(read="L")
						strand		  = self.__paired_reads.strand(read="L")
						start,end	  = self.__paired_reads.coordinates(read="both")
						substitutions = tuple(set(self.__paired_reads.substitutions(read="both")))
						deletions	  = tuple(set(self.__paired_reads.deletions(read="both")))					# substitutions in the same position have to be ignored!
						interval	  = (chromosome,strand,start,end,substitutions,deletions)					# I used to have an Interval class with __slots__ but using tuples here rather than a class instance made the program 2-3 fold faster. 
						if blocks:	  # The results are stored in the self.count dictionary
							self.count[interval]['coverage']  = 1
						else:		
							self.count[interval]['coverage'] += 1
						self.paired_reads += 1
						self.mapped_reads += 2
						self.__paired_reads.reset()
					else:
						self.printToDiscardedFile(self.line)
						self.__paired_reads.reset()
				elif properpair:
					self.__paired_reads.reset()
					continue
				else:
					chromosome	  = self.__paired_reads.chr_ID(read="L")
					strand		  = self.__paired_reads.strand(read="L")
					start		  = self.__paired_reads.coordinates(read="L")[0]
					end			  = self.__paired_reads.coordinates(read="L")[1]
					substitutions = tuple(self.__paired_reads.substitutions(read="L"))
					deletions	  = tuple(self.__paired_reads.deletions(read="L"))
					interval	  = (chromosome,strand,start,end,substitutions,deletions)
					if blocks:	  # The results are stored in the self.count dictionary
						self.count[interval]['coverage']  = 1
					else:		
						self.count[interval]['coverage'] += 1
					self.single_reads += 1
					self.mapped_reads += 1
					self.__paired_reads.removeRead()
			else:
					self.printToDiscardedFile(self.readinterval())

		self.total_cDNAs = len([keys for keys in self.count])
		if self.__discarded:
			self.__discarded.close()
		return True

#--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#
									##### READ_LINE_BY_LINE #####
#--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#


	def getRead(self,align_score=0,align_quality=0,length=1000,maximum=float("infinity"),blocks=False,unique=False,distance=1000,force_single_end=False,muts_filter=None,properpair=False,correct_seq=True):
		"""This method returns the start and end coordinates of a read for single end sequenced data and start and end positions of correctly paired reads. It also returns the chromosome coordinates of 
		substitutions and deletions found in the read. Insertions are removed from the read and not further considered. Many variables can be set: read length(i.e. length=18), how many mapped reads you want to have analyzed (maximum=10000),
		whether you only want reads with a single alignment locations included (unique=True) and you can also set alignment scores and alignment qualities (align_quality=50, align_score=60). The 'distance'
		indicates the maximum allowed distance between two paired reads mapped to the same chromosome. Default is 1 kb.
		This method can handle both Novoalign native and SAM output formats. Reads can be filtered for mutations. If muts_filter="delsonly", only reads with deletions are considered. Other muts_filter options
		are "subsonly", "allmuts" and "nomuts". If you set force_single_end = True then all the reads will be treated as single end reads. Useful when making multiple sequence alignments"""
		self.read_seq_ID	  = str()
		self.read_seq		  = str()
		self.mate_read_seq	  = str()
		self.read_genomic_seq = str()
		self.mate_genomic_seq = str()
		self.read_coordinates = None
		self.read_ispaired	  = False
		ispaired			  = None
		if self.total_reads == 0:
			self.duplicatesremoved	= blocks
		while self.readinterval.readline(correct_seq=correct_seq):
			interval = tuple()
			if properpair:
				if self.paired_reads >= maximum or not self.readinterval():
					return False
			else:
				if self.mapped_reads >= maximum or not self.readinterval():
					return False
			self.total_reads += 1
#--------------------------------------------------------------------- Processing all the read filtering options ---------------------------------------------------------------#

			# checks if:
			# the read is mapped
			# the alignquality is acceptable
			# the alignscore is acceptable
			# the read has the right length
			# the read is uniquely mapped
			# the read has the desired mutations

			if not self.readinterval.ismapped()												: self.printToDiscardedFile(self.readinterval()); continue
			if align_quality and not self.readinterval.alignQualityThreshold(align_quality) : self.printToDiscardedFile(self.readinterval()); continue
			if align_score	 and not self.readinterval.alignScoreThreshold(align_score)		: self.printToDiscardedFile(self.readinterval()); continue
			if length		 and not self.readinterval.lengthThreshold(length)				: self.printToDiscardedFile(self.readinterval()); continue
			if unique		 and not self.readinterval.uniqueAlnLocation()					: self.printToDiscardedFile(self.readinterval()); continue
			if muts_filter:	 self.hasmutation = self.readinterval.filterMutations(muts_filter)		# allows filtering of mutations. If the read has the desired mutation(s) then the filterMutations method will return True, else False.

#----------------------------------------------------------For processing sequencing data with single end reads ---------------------------------------------------------#

			if force_single_end:				# in case you do not want pyReadCounters to automatically connect the paired reads.
				ispaired = False
			else:
				ispaired = self.readinterval.read_start_paired()
			if not ispaired and not properpair:
				self.line		   = self.readinterval()
				self.substitutions = tuple(self.readinterval.substitutions(chromosome_location=True))
				self.deletions	   = tuple(self.readinterval.deletions(chromosome_location=True))
				self.chromosome	   = self.readinterval.chr_ID()
				self.strand		   = self.readinterval.strand()
				self.read_start	   = self.readinterval.read_start()
				self.read_end	   = self.readinterval.read_end()	
				if blocks: 
					if self.readisDuplicate((self.chromosome,self.strand,self.read_start,self.read_end,self.substitutions,self.deletions)):		 # this filter can be used to remove reads with same start and end positions on the chromosome (i.e. --blocks option)
						self.printToDiscardedFile(self.line)
						continue
				self.single_reads += 1
				self.mapped_reads += 1
				self.read_ispaired	  = False
				self.read_seq		  = self.readinterval.sequence()
				self.read_seq_ID	  = self.readinterval.seq_ID()
				self.mate_read_seq	  = None
				return True

#--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#

																			##### READ_LINE_BY_LINE#####

#---------------------------------------------------------------- For processing sequencing data with paired end reads ----------------------------------------------------------------#

			elif ispaired and self.readinterval.paired_sequence(distance=distance):	 
				self.__paired_reads.addRead(self.readinterval)
				if self.__paired_reads.numberOfReadsinList() < 2:																# to make sure that two reads have been processed before continuing.
					self.line = self.readinterval()
					continue
				if self.__paired_reads.ispaired():																				# if two reads are proper pairs, then analyze them.
					self.line = "%s%s" % (self.line,self.readinterval())
					if self.__paired_reads.cDNALength() <= length:																# This bit will determine whether the read is proper length and proper align quality and/or align score
						self.chromosome	   = self.__paired_reads.chr_ID(read="L")
						self.strand		   = self.__paired_reads.strand(read="L")
						self.read_start,self.read_end = self.__paired_reads.coordinates(read="both")
						self.substitutions = tuple(set(self.__paired_reads.substitutions(read="both")))
						self.deletions	   = tuple(set(self.__paired_reads.deletions(read="both")))
						if blocks: 
							if self.readisDuplicate((self.chromosome,self.strand,self.read_start,self.read_end,self.substitutions,self.deletions)):		# this filter can be used to remove reads with same start and end positions on the chromosome (i.e. --blocks option)
								self.printToDiscardedFile(self.line)
								self.__paired_reads.reset()																		# if block filtering is on and cDNAs with the same read coordinates already exist, empty the read list and start again
								continue
						self.paired_reads += 1
						self.mapped_reads += 2
						self.read_coordinates = interval
						self.read_ispaired	  = True
						self.read_seq_ID	  = self.__paired_reads.read_ID(read="L")
						self.read_seq		  = self.__paired_reads.sequence(read="L")
						self.mate_read_seq	  = self.__paired_reads.sequence(read="R")
						self.__paired_reads.reset()
						return True
					else:
						self.printToDiscardedFile(self.line)
						self.__paired_reads.reset()
						continue
				elif properpair:
					self.__paired_reads.reset()
					continue
				else:
					self.single_reads += 1
					self.mapped_reads += 1
					self.read_ispaired	  = False
					self.read_seq		  = self.__paired_reads.sequence(read="L")
					self.read_seq_ID	  = self.__paired_reads.read_ID(read="L")
					self.mate_read_seq	  = None
					self.chromosome		  = self.__paired_reads.chr_ID(read="L")
					self.strand			  = self.__paired_reads.strand(read="L")
					self.read_start		  = self.__paired_reads.coordinates(read="L")[0]
					self.read_end		  = self.__paired_reads.coordinates(read="L")[1]
					self.substitutions	  = tuple(self.__paired_reads.substitutions(read="L"))
					self.deletions		  = tuple(self.__paired_reads.deletions(read="L"))
					self.__paired_reads.removeRead()
					return True
#----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#

			else:
				self.printToDiscardedFile(self.readinterval())