// Path: src/main/java/abra/ContigAligner.java
// public static class ContigAlignerResult {
// 		
// 	// Used for testing
// 	static boolean PAD_CONTIG = true;
// 		
// 	private int localRefPos;
// 	private String cigar;
// 		
// 	private String chromosome;
// 	private int refContextStart;
// 		
// 	private String sequence;
// 	private int score;
// 	private boolean isSecondary = false;
// 		
// 	public static final ContigAlignerResult INDEL_NEAR_END = new ContigAlignerResult();
// 
// 	private ContigAlignerResult() {
// 	}
// 		
// 	ContigAlignerResult(int refPos, String cigar, String chromosome, int refContextStart, String sequence, int score) {
// 		this.localRefPos = refPos;
// 		this.cigar = cigar;
// 		this.chromosome = chromosome;
// 		this.refContextStart = refContextStart;
// 		this.sequence = sequence;
// 		this.score = score;
// 	}
// 		
// 	public int getRefPos() {
// 		return localRefPos;
// 	}
// 	public String getCigar() {
// 		return cigar;
// 	}
// 
// 	public String getChromosome() {
// 		return chromosome;
// 	}
// 
// 	public int getRefContextStart() {
// 		return refContextStart;
// 	}
// 		
// 	// This is the actual genomic position 
// 	public int getGenomicPos() {
// 		return localRefPos + refContextStart;
// 	}
// 		
// 	public String getSequence() {
// 		return sequence;
// 	}
// 		
// 	public int getScore() {
// 		return score;
// 	}
// 		
// 	public boolean isSecondary() {
// 		return isSecondary;
// 	}
// 
// 	public void setSecondary(boolean isSecondary) {
// 		this.isSecondary = isSecondary;
// 	}
// }
// 
// Path: src/main/java/abra/SimpleMapper.java
// enum Orientation {
// 	UNSET, FORWARD, REVERSE;
// }
// 
// Path: src/main/java/abra/SimpleMapper.java
// static class SimpleMapperResult {
// 	private int pos;
// 	private int mismatches;
// 	private Orientation orientation;
// 		
// 	SimpleMapperResult(int pos, int mismatches, Orientation orientation) {
// 		this.pos = pos;
// 		this.mismatches = mismatches;
// 		this.orientation = orientation;
// 	}
// 		
// 	public int getPos() {
// 		return pos;
// 	}
// 	public int getMismatches() {
// 		return mismatches;
// 	}
// 	public Orientation getOrientation() {
// 		return orientation;
// 	}
// }


import htsjdk.samtools.SAMRecord;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import abra.ContigAligner.ContigAlignerResult;
import abra.SimpleMapper.Orientation;
import abra.SimpleMapper.SimpleMapperResult;
package abra;




public class ReadEvaluator {

	// key = SimpleMapper with cached contig, value = contig SW alignment result
	private Map<Feature, Map<SimpleMapper, ContigAlignerResult>> mappedContigs;
	
	public ReadEvaluator(Map<Feature, Map<SimpleMapper, ContigAlignerResult>> mappedContigs) {
		this.mappedContigs = mappedContigs;
	}
	
	
	/**
	 * If an improved alignment exists for the input read, return it.
	 * Returns null if there is no improved alignment
	 * If multiple alignments exist for the read with the same number of mismatches,
	 * the alignments are ambiguous and null is returned.
	 * A read may align to multiple contigs, but result in the same alignment in
	 * the context of the reference.  In this case the alignment is considered distinct. 
	 */
	public Alignment getImprovedAlignment(int origEditDist, SAMRecord samRecord, CompareToReference2 c2r) {
		Alignment result = getImprovedAlignment(origEditDist, samRecord.getReadString());
		
		if (result == null) {
			// If soft clipped, attempt to map only the unclipped portion of the read
			// Relying on the original mapper to handle clipping here
			if (samRecord.getCigarString().contains("S")) {
				
				// Use mapper's edit distance which should be for the unclipped portion of the read only
				int unclippedEditDist = SAMRecordUtils.getEditDistance(samRecord, c2r, false);
				
				// Don't bother remapping if alignment cannot be improved
				if (unclippedEditDist > 0) {
					String unclipped = SAMRecordUtils.getMappedReadPortion(samRecord);
					result = getImprovedAlignment(unclippedEditDist, unclipped);
					if (result != null) {
						result.cigar = SAMRecordUtils.getLeadingClips(samRecord) + result.cigar + SAMRecordUtils.getTrailingClips(samRecord);
					}
				}
			}
		}
		
		return result;
	}
	
	public Alignment getImprovedAlignment(int origEditDist, String read) {
		Alignment result = null;
		
		List<AlignmentHit> alignmentHits = new ArrayList<AlignmentHit>();
		
		int bestMismatches = read.length()+1;
		
		// Map read to all contigs, caching the hits with the smallest number of mismatches
		for (Feature region : mappedContigs.keySet()) {
			Map<SimpleMapper, ContigAlignerResult> regionContigs = mappedContigs.get(region);
			
			for (SimpleMapper mapper : regionContigs.keySet()) {
// Path: src/main/java/abra/ContigAligner.java
// public static class ContigAlignerResult {
// 		
// 	// Used for testing
// 	static boolean PAD_CONTIG = true;
// 		
// 	private int localRefPos;
// 	private String cigar;
// 		
// 	private String chromosome;
// 	private int refContextStart;
// 		
// 	private String sequence;
// 	private int score;
// 	private boolean isSecondary = false;
// 		
// 	public static final ContigAlignerResult INDEL_NEAR_END = new ContigAlignerResult();
// 
// 	private ContigAlignerResult() {
// 	}
// 		
// 	ContigAlignerResult(int refPos, String cigar, String chromosome, int refContextStart, String sequence, int score) {
// 		this.localRefPos = refPos;
// 		this.cigar = cigar;
// 		this.chromosome = chromosome;
// 		this.refContextStart = refContextStart;
// 		this.sequence = sequence;
// 		this.score = score;
// 	}
// 		
// 	public int getRefPos() {
// 		return localRefPos;
// 	}
// 	public String getCigar() {
// 		return cigar;
// 	}
// 
// 	public String getChromosome() {
// 		return chromosome;
// 	}
// 
// 	public int getRefContextStart() {
// 		return refContextStart;
// 	}
// 		
// 	// This is the actual genomic position 
// 	public int getGenomicPos() {
// 		return localRefPos + refContextStart;
// 	}
// 		
// 	public String getSequence() {
// 		return sequence;
// 	}
// 		
// 	public int getScore() {
// 		return score;
// 	}
// 		
// 	public boolean isSecondary() {
// 		return isSecondary;
// 	}
// 
// 	public void setSecondary(boolean isSecondary) {
// 		this.isSecondary = isSecondary;
// 	}
// }
// 
// Path: src/main/java/abra/SimpleMapper.java
// enum Orientation {
// 	UNSET, FORWARD, REVERSE;
// }
// 
// Path: src/main/java/abra/SimpleMapper.java
// static class SimpleMapperResult {
// 	private int pos;
// 	private int mismatches;
// 	private Orientation orientation;
// 		
// 	SimpleMapperResult(int pos, int mismatches, Orientation orientation) {
// 		this.pos = pos;
// 		this.mismatches = mismatches;
// 		this.orientation = orientation;
// 	}
// 		
// 	public int getPos() {
// 		return pos;
// 	}
// 	public int getMismatches() {
// 		return mismatches;
// 	}
// 	public Orientation getOrientation() {
// 		return orientation;
// 	}
// }


// Path: src/main/java/abra/ReadEvaluator.java
import htsjdk.samtools.SAMRecord;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import abra.ContigAligner.ContigAlignerResult;
import abra.SimpleMapper.Orientation;
import abra.SimpleMapper.SimpleMapperResult;

package abra;




public class ReadEvaluator {

	// key = SimpleMapper with cached contig, value = contig SW alignment result
	private Map<Feature, Map<SimpleMapper, ContigAlignerResult>> mappedContigs;
	
	public ReadEvaluator(Map<Feature, Map<SimpleMapper, ContigAlignerResult>> mappedContigs) {
		this.mappedContigs = mappedContigs;
	}
	
	
	/**
	 * If an improved alignment exists for the input read, return it.
	 * Returns null if there is no improved alignment
	 * If multiple alignments exist for the read with the same number of mismatches,
	 * the alignments are ambiguous and null is returned.
	 * A read may align to multiple contigs, but result in the same alignment in
	 * the context of the reference.  In this case the alignment is considered distinct. 
	 */
	public Alignment getImprovedAlignment(int origEditDist, SAMRecord samRecord, CompareToReference2 c2r) {
		Alignment result = getImprovedAlignment(origEditDist, samRecord.getReadString());
		
		if (result == null) {
			// If soft clipped, attempt to map only the unclipped portion of the read
			// Relying on the original mapper to handle clipping here
			if (samRecord.getCigarString().contains("S")) {
				
				// Use mapper's edit distance which should be for the unclipped portion of the read only
				int unclippedEditDist = SAMRecordUtils.getEditDistance(samRecord, c2r, false);
				
				// Don't bother remapping if alignment cannot be improved
				if (unclippedEditDist > 0) {
					String unclipped = SAMRecordUtils.getMappedReadPortion(samRecord);
					result = getImprovedAlignment(unclippedEditDist, unclipped);
					if (result != null) {
						result.cigar = SAMRecordUtils.getLeadingClips(samRecord) + result.cigar + SAMRecordUtils.getTrailingClips(samRecord);
					}
				}
			}
		}
		
		return result;
	}
	
	public Alignment getImprovedAlignment(int origEditDist, String read) {
		Alignment result = null;
		
		List<AlignmentHit> alignmentHits = new ArrayList<AlignmentHit>();
		
		int bestMismatches = read.length()+1;
		
		// Map read to all contigs, caching the hits with the smallest number of mismatches
		for (Feature region : mappedContigs.keySet()) {
			Map<SimpleMapper, ContigAlignerResult> regionContigs = mappedContigs.get(region);
			
			for (SimpleMapper mapper : regionContigs.keySet()) {
				SimpleMapperResult mapResult = mapper.map(read);