content: package aliview.alignment;
  
  import java.awt.Point;
  import java.awt.Rectangle;
  import java.awt.Toolkit;
  import java.awt.datatransfer.StringSelection;
  import java.awt.event.MouseEvent;
  import java.io.BufferedWriter;
  import java.io.File;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.OutputStream;
  import java.io.StringReader;
  import java.io.StringWriter;
  import java.io.Writer;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.Date;
  import java.util.HashSet;
  import java.util.List;
  import java.util.regex.Pattern;
  
  import javax.swing.JOptionPane;
  import javax.swing.undo.UndoableEdit;
  
  import org.apache.commons.io.FileUtils;
  import org.apache.commons.lang.StringUtils;
  import org.apache.log4j.Logger;
  
  import utils.DialogUtils;
  import utils.nexus.CharSet;
  import utils.nexus.CharSets;
  import utils.nexus.Excludes;
  import utils.nexus.NexusRange;
  import utils.nexus.NexusUtilities;
  import aliview.AliViewExtraNexusUtilities;
  import aliview.AminoAcid;
  import aliview.Base;
  import aliview.GeneticCode;
  import aliview.NucleotideUtilities;
  import aliview.gui.TextEditDialog;
  import aliview.importer.AlignmentImportException;
  import aliview.importer.FileFormat;
  import aliview.importer.MSFFileIndexer;
  import aliview.importer.MSFImporter;
  import aliview.importer.SequencesFactory;
  import aliview.messenges.Messenger;
  import aliview.primer.Dimer;
  import aliview.primer.Primer;
  import aliview.sequencelist.AlignmentDataListener;
  import aliview.sequencelist.AlignmentSelectionEvent;
  import aliview.sequencelist.AlignmentSelectionListener;
  import aliview.sequencelist.FileSequenceAlignmentListModel;
  import aliview.sequencelist.FindObject;
  import aliview.sequencelist.MemorySequenceAlignmentListModel;
  import aliview.sequencelist.AlignmentListModel;
  import aliview.sequencelist.FileSequenceLoadListener;
  import aliview.sequences.FastaFileSequence;
  import aliview.sequences.InMemorySequence;
  import aliview.sequences.PhylipSequence;
  import aliview.sequences.Sequence;
  import aliview.sequences.SequenceUtils;
  import aliview.settings.Settings;
  import aliview.utils.ArrayUtilities;
  
  public class Alignment implements FileSequenceLoadListener {
  	private static final Logger logger = Logger.getLogger(Alignment.class);
  	private static final String LF = System.getProperty("line.separator");
  	private static final SequencesFactory seqFactory = new SequencesFactory();
  	AlignmentListModel sequences;
  	protected int nextFindSequenceNumber;
  	protected int nextFindStartPos;
  	private AlignmentFile alignmentFile;
  	private int PRIMER_MAX_DEGENERATE_SCORE = 1000;
  	private boolean isEditedAfterLastSave = false;
  	private AlignmentMeta alignmentMeta;
  	private int readingFrame = 1;
  	private boolean editMode;
  	private ArrayList<AlignmentListener> alignmentListeners = new ArrayList<AlignmentListener>();
  	private boolean isSelectable;
  
  	public Alignment(){
  		this(null, new MemorySequenceAlignmentListModel(), new AlignmentMeta());
  	}
  
  	public Alignment(AlignmentListModel sequences) {
  		this(null, sequences, new AlignmentMeta());
  	}
  
  	public Alignment(File file, AlignmentListModel sequences, AlignmentMeta aliMeta) {
  		setAlignmentFile(file);
  		this.sequences = sequences;
  		this.sequences.setAlignment(this);
  		//this.sequences.addAlignmentSelectionListener(this);
  		this.alignmentMeta = aliMeta;
  		setEditedAfterLastSave(false);
  		fireAllSequencesAreNew();
  	}
  
  	public void setNewSequencesFromUndo(AlignmentListModel seqs){
  		this.sequences.setSequences(seqs.getDelegateSequences());
  		// TODO this should also be read from file
  		this.alignmentMeta = new AlignmentMeta();	
  		//setEditedAfterLastSave(false);
  		fireAllSequencesAreNew();
  	}
  
  
  	public double getApproximateMemorySizeMB(){
  		double seqSize = sequences.getSize() * sequences.getLongestSequenceLength();
  		double MB = 1000 * 1000;
  		return seqSize /MB;
  	}
  
  	public void addAlignmentListener(AlignmentListener l){
  		alignmentListeners.add(l);
  	}
  
  	public void addAlignmentDataListener(AlignmentDataListener l){
  		sequences.addAlignmentDataListener(l);
  	}
  
  	public void addAlignmentSelectionListener(AlignmentSelectionListener l){
  		sequences.addAlignmentSelectionListener(l);
  	}
  
  	private void fireAlignmentMetaOnlyChanged(){
  		for(AlignmentListener listener: alignmentListeners){
  			//	listener.alignmentMetaChanged(new AlignmentEvent(this));
  		}
  	}
  
  	// instead listen to sequence change and then set cached histogram
  	private void fireAllSequencesAreNew(){
  		for(AlignmentListener listener: alignmentListeners){
  			listener.newSequences(new AlignmentEvent(this));
  		}
  	}
  
  	public int getMaxY() {
  		return sequences.getSize();
  	}
  
  	public byte getBaseAt(int x, int y) {
  		return sequences.getBaseAt(x,y);
  	}
  
  	public AminoAcid getTranslatedAminoAcidAtNucleotidePos(int x, int y) {
  		return sequences.getTranslatedAminoAcidAtNucleotidePos(x,y);
  	}
  
  	public int getLengthAt(int y) {
  		return sequences.getLengthAt(y);
  	}
  
  
  	// TODO this is the only place where sequences are checked for modification when they are "outside"
  	public AlignmentListModel getSequences() {
  		return sequences;
  	}
  
  	public int getMaxX() {
  		return sequences.getLongestSequenceLength();
  	}
  
  	public int getMaximumSequenceLength() {
  		return getMaxX();
  	}
  
  	public int getLongestSequenceLength() {
  		return getMaxX();
  	}
  
  	public int getSize(){
  		return sequences.getSize();
  	}
  
  	public void clearSelection() {
  		sequences.clearSequenceSelection();
  		//fireSelectionChanged();
  	}
  
  	public int getSequenceIndex(Sequence seq) {
  		return sequences.indexOf(seq);
  	}
  
  	public AlignmentFile getAlignmentFile() {
  		return this.alignmentFile;
  	}
  
  	public String getFileName() {
  		if(alignmentFile == null){
  			return null;
  		}
  		return alignmentFile.getName();
  	}
  
  	public void setAlignmentFile(File alignmentFile) {
  		if(alignmentFile != null && alignmentFile.exists()){
  			this.alignmentFile = new AlignmentFile(alignmentFile);
  		}
  	}
  
  	public void storeAlignmetAsFasta(Writer out) throws IOException{
  		int longest = sequences.getLongestSequenceLength();
  		for(Sequence seq: sequences){
  			String name = seq.getName();
  
  			out.write('>');
  			out.write(seq.getName());
  			out.write(LF);
  			seq.writeBases(out);
  
  			out.write(LF);
  
  		}		
  		out.flush();
  		out.close();
  	}
  
  	private void storeAlignmetAsClustal(Writer out) throws IOException {
  
  		// First write meta
  		out.write("CLUSTAL W (1.99) multiple sequence alignment" + LF);
  		out.write(LF);
  		out.write(LF);
  
  		int longSeq = sequences.getLongestSequenceLength();
  		int namePadSize = sequences.getLongestSequenceName() + 1;
  
  		//  Clustal uses up to 60 residues per line
  		for(int pos = 0; pos < longSeq; pos += 60){
  
  			int endPos = pos + 59; // end is inclusive
  			endPos = Math.min(endPos, longSeq - 1);
  
  			for(int n = 0; n < sequences.getSize(); n++){		
  				// Write name space and up to 60 residues
  				Sequence seq = sequences.get(n);
  				String paddedName = StringUtils.rightPad(seq.getName(), namePadSize);
  				//	byte[] bases = seq.getBasesBetween(pos,  endPos);
  
  				out.write(paddedName);
  				seq.writeBasesBetween(pos, endPos, out);
  				out.write(LF);
  				//	out.flush();
  			}
  
  			// add two blank lines
  			out.write(LF);
  			out.write(LF);
  			out.flush();
  		}
  		out.flush();
  		out.close();
  	}
  
  	private void storeAlignmetAsMSF(Writer out) throws IOException {
  
  		int longSeq = sequences.getLongestSequenceLength();
  		int namePadSize = sequences.getLongestSequenceName() + 1;
  
  		// first write alignment meta
  		String type = "P";
  		String type2 = "!!AA";
  		if(this.isNucleotideAlignment()){
  			type = "N";
  			type2 = "!!NA";
  		}
  
  		out.write("" + type2 + "_MULTIPLE_ALIGNMENT" + LF);
  		out.write(LF);
  		out.write(LF);
  
  
  		//	 checksumTotal += check;
  		//	 checksumTotal %= 10000;
  
  		int checkTotal = 0;
  		for(int n = 0; n < sequences.getSize(); n++){
  			Sequence seq = sequences.get(n);
  			String paddedName = StringUtils.rightPad(seq.getName(), namePadSize);
  			int check = MSFImporter.GCGchecksum(seq);
  			checkTotal += check;
  			checkTotal %= 10000;
  		}
  
  
  		String totalMeta = "   MSF: " + longSeq + "  Type: " + type + "  Check: "  + checkTotal + "  ..";
  		out.write(totalMeta);
  		out.write(LF);
  
  		out.write(LF);
  
  		// then write names and checksums
  		for(int n = 0; n < sequences.getSize(); n++){
  			Sequence seq = sequences.get(n);
  			String paddedName = StringUtils.rightPad(seq.getName(), namePadSize);
  			int check = MSFImporter.GCGchecksum(seq);
  			String seqMeta = " Name: " + paddedName + "  Len: " + seq.getLength() + "  Check: "  + check + "  Weight: 1.00";
  			out.write(seqMeta);
  			out.write(LF);
  
  		}
  
  		// start sequences
  		out.write(LF);
  		out.write(LF);
  		out.write("//");
  		out.write(LF);
  		out.write(LF);
  		// then write 
  
  
  		// MSF uses up to 50 residues per line
  		for(int pos = 0; pos < longSeq; pos += 50){
  
  			int endPos = pos + 50;
  			endPos = Math.min(endPos, longSeq);
  
  			for(int n = 0; n < sequences.getSize(); n++){		
  				// Write name space and up to 50 residues
  				Sequence seq = sequences.get(n);
  				String paddedName = StringUtils.rightPad(seq.getName(), namePadSize);
  
  				out.write(paddedName);
  
  				byte[] bases = seq.getBasesBetween(pos,  endPos - 1);
  				// replace gaps to MSF notation
  				bases = ArrayUtilities.replaceAll(bases, '-', (byte) '.');			
  
  				// write bases with space every 10 pos
  				for(int delPos = 0; delPos < bases.length; delPos += 10){
  					int maxLen = 10;
  					maxLen = Math.min(maxLen, bases.length - delPos);
  					out.write(new String(bases, delPos, maxLen));
  
  					// add space, but not at last
  					if(delPos+10 < bases.length){
  						out.write(' ');
  					}
  				}
  
  				out.write(LF);
  			}
  
  			// add two blank lines
  			out.write(LF);
  			out.write(LF);
  		}
  		out.flush();
  		out.close();
  	}
  
  
  	private void storeAlignmetAsPhyFile(Writer out, FileFormat fileFormat) throws IOException{
  		// First line number of seq + seqLen
  		out.write("" + sequences.getSize() + " " + this.getMaximumSequenceLength() + LF);
  
  
  		// Interleaved
  		if(fileFormat == FileFormat.PHYLIP_RELAXED_PADDED_INTERLEAVED_AKA_LONG_NAME_INTERLEAVED){
  			int longSeq = sequences.getLongestSequenceLength();
  			int namePadSize = sequences.getLongestSequenceName() + 1;
  
  			// 60 residues per line
  			int residuesPerLine = 80;
  			// names on first round only
  			for(int n = 0; n < sequences.getSize(); n++){		
  				// Write name space and up to xx residues
  				Sequence seq = sequences.get(n);
  				String seqName = seq.getName();
  				seqName = escapeSeqName(seqName);
  
  				String paddedName = StringUtils.rightPad(seqName, namePadSize);
  
  				out.write(paddedName);
  
  				//byte[] bases = seq.;
  
  				seq.writeBasesBetween(0,  residuesPerLine - 1, out);
  
  				out.write(LF);
  			}
  
  			out.write(LF);
  			out.write(LF);
  
  
  
  			for(int pos = residuesPerLine; pos < longSeq; pos += residuesPerLine){
  				int endPos = pos + residuesPerLine;
  				endPos = Math.min(endPos, longSeq);
  
  				for(int n = 0; n < sequences.getSize(); n++){		
  					// Write name space and up to xx residues
  					Sequence seq = sequences.get(n);
  					//					String seqName = seq.getName();
  					//					seqName = escapeSeqName(seqName);
  					//					
  					//					String paddedName = StringUtils.rightPad(seqName, namePadSize);
  					//					
  					//					out.write(paddedName);
  
  					//byte[] bases = seq.getBasesBetween;
  
  					// ((out.write(new String(bases));
  					seq.writeBasesBetween(pos,  endPos - 1, out);	
  
  					out.write(LF);
  				}
  
  				// add two blank lines before next "interleave"
  				out.write(LF);
  				out.write(LF);
  			}
  		}
  		// Sequential versions
  		else{
  			for(Sequence seq: sequences){
  				String seqName = seq.getName();
  				seqName = escapeSeqName(seqName);
  
  
  				if(fileFormat == FileFormat.PHYLIP_RELAXED){
  					int longSeqName = sequences.getLongestSequenceName();
  					seqName += ' '; // + one space
  				}
  				else if(fileFormat == FileFormat.PHYLIP_STRICT_SEQUENTIAL_AKA_SHORT_NAME_SEQUENTIAL){
  					// Make sure exact 10 positions
  					if(seqName.length() > 10){
  						seqName = StringUtils.substring(seqName, 0, 10);					
  					}
  					seqName = StringUtils.rightPad(seqName , 10); // 10 name no space
  				}
  				// default is FileFormat.PHYLIP_RELAXED_PADDED
  				else{
  					int longSeqName = sequences.getLongestSequenceName();
  					seqName = StringUtils.rightPad(seqName , longSeqName + 1); // + one space
  				}
  
  				out.write(seqName); // no space after name that is added on name already (but not on strict)
  				seq.writeBases(out);
  				out.write(LF);
  			}
  		}
  
  		out.flush();
  		out.close();	
  
  	}
  
  	private String escapeSeqName(String name) {
  		if(name != null && name.indexOf(' ') > -1){
  			name = StringUtils.replace(name, " ", "_");
  		}
  		return name;
  	}
  	/*
  	private void storeAlignmetAsPhyTranslatedAminoAcidFile(Writer out) throws IOException{
  		// First line number of seq + seqLen
  		out.write("" + sequences.getSize() + " " + alignmentMeta.getCodonPositions().getAminoAcidPosFromNucleotidePos(sequences.getLongestSequenceLength()) + LF);
  
  		// int longSeqName = sequences.getLongestSequenceName();
  
  		AATranslator aaTransSeq = new AATranslator(getAlignmentMeta().getCodonPositions(),getGeneticCode());
  		for(Sequence seq: sequences){
  			aaTransSeq.setSequence(seq);
  
  			out.write(escapeSeqName(seq.getName()) + " ");
  			aaTransSeq.writeTranslation(out);
  			out.write(LF);
  		}
  
  		out.flush();
  		out.close();	
  
  	}
  
  	private void storeAlignmetAsFastaTranslatedAminoAcidFile(Writer out) throws IOException{
  		// make sure it is translated
  		boolean wasTranslated = isTranslatedOnePos();
  		setTranslationOnePos(true);
  
  		for(Sequence seq: sequences){
  
  			String name = seq.getName();
  			out.write('>');
  			out.write(seq.getName());
  			out.write(LF);
  			seq.writeBases(out);
  			out.write(LF);
  		}		
  		out.flush();
  		out.close();
  
  		setTranslationOnePos(wasTranslated);
  	}
  	 */
  
  	private void storeMetaData(BufferedWriter outMeta) throws IOException {
  		outMeta.write("" + NexusUtilities.getExcludesAsNexusBlock(alignmentMeta.getExcludes()));
  		outMeta.write(LF);
  		outMeta.write("" + NexusUtilities.getCodonPosAsNexusBlock(alignmentMeta.getCodonPositions(), 0, this.getMaximumSequenceLength()));
  		outMeta.write(LF);
  		outMeta.write("" + NexusUtilities.getCharsetsBlockAsNexus(alignmentMeta.getCharsets()));
  		outMeta.write(LF);
  
  		outMeta.flush();
  		outMeta.close();
  	}
  
  	private void storeTranslatedMetaData(BufferedWriter outMeta, AlignmentMeta translatedMeta) throws IOException {
  		outMeta.write("" + NexusUtilities.getExcludesAsNexusBlock(translatedMeta.getExcludes()));
  		outMeta.write(LF);
  		//outMeta.write("" + NexusUtilities.getCodonPosAsNexusBlock(alignmentMeta.getCodonPositions()));
  		outMeta.write("" + NexusUtilities.getCharsetsBlockAsNexus(translatedMeta.getCharsets()));
  		outMeta.write(LF);
  		outMeta.flush();
  		outMeta.close();
  	}
  
  	public void saveSelectionAsFastaFile(File selectedFile){
  
  		try {
  			BufferedWriter buffOut = new BufferedWriter(new FileWriter(selectedFile));
  			sequences.writeSelectionAsFasta(buffOut);
  			buffOut.flush();
  			buffOut.close();
  		} catch (IOException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  	}
  
  	public void exportPartitionsFileRaxMLFormat(File outFile)  throws IOException{
  
  		//		DNA,p1=1-1194
  		//		DNA,p2=1195-3029
  		//		DNA,p3=3030-4345
  		//		DNA,p4=4346-5697
  		//		DNA,p5=5698-6997
  
  
  		String datatypeModel = "";
  
  		if(isAAAlignment()){
  			datatypeModel = "JTT";
  		}
  		else{
  			datatypeModel = "DNA";
  		}
  
  
  		String partitionsAsText = "";
  		CharSets charsets = getAlignmentMeta().getCharsets();
  		for(CharSet charSet: charsets){
  
  			String charsetRangeText = "";
  			ArrayList<NexusRange> ranges = charSet.getAsContinousNexusRanges();
  			int rangeCount = 0;
  			for(NexusRange range: ranges){
  				charsetRangeText += range.toString() + ",";		
  				rangeCount ++;
  			}
  
  			// remove last ,
  			if(rangeCount > 0){
  				charsetRangeText = StringUtils.removeEnd(charsetRangeText, ",");
  			}
  
  			partitionsAsText += datatypeModel + "," + charSet.getName() + " = " + charsetRangeText + LF;
  
  		}
  
  		FileUtils.writeStringToFile(outFile, partitionsAsText);
  
  	}
  
  
  	public void saveAlignmentAsFile(File outFile, FileFormat fileFormat, boolean rightPadOrTrimIfNeeded) throws IOException{
  
  		logger.info("fileFormat" + fileFormat);
  
  		// make sure they are equal length (if editable)
  		if(rightPadOrTrimIfNeeded && sequences.isEditable()){			
  			sequences.rightPadWithGapUntilEqualLength();
  			sequences.rightTrimSequencesRemoveGapsUntilEqualLength();			
  		}
  		// store current translation (might be modofied below)
  		boolean wasTranslated = isTranslatedOnePos();
  
  		BufferedWriter out = new BufferedWriter(new FileWriter(outFile));
  
  		int nexusDatatype = AliViewExtraNexusUtilities.DATATYPE_DNA;
  		if(sequences.getSequenceType() == SequenceUtils.TYPE_AMINO_ACID){
  			nexusDatatype = AliViewExtraNexusUtilities.DATATYPE_PROTEIN;
  		}
  
  		if(fileFormat == FileFormat.FASTA){
  			setTranslationOnePos(false);
  			storeAlignmetAsFasta(out);
  			// save meta if exset it is set
  			if(this.alignmentMeta.isMetaOutputNeeded()){
  				BufferedWriter outMeta = new BufferedWriter(new FileWriter(new File(outFile.getAbsoluteFile() + ".meta")));
  				storeMetaData(outMeta);
  			}
  		}else if(fileFormat == FileFormat.PHYLIP || fileFormat == FileFormat.PHYLIP_RELAXED ||
  				fileFormat == FileFormat.PHYLIP_RELAXED_PADDED_AKA_LONG_NAME_SEQUENTIAL || fileFormat == FileFormat.PHYLIP_STRICT_SEQUENTIAL_AKA_SHORT_NAME_SEQUENTIAL ||
  				fileFormat == FileFormat.PHYLIP_RELAXED_PADDED_INTERLEAVED_AKA_LONG_NAME_INTERLEAVED){
  			setTranslationOnePos(false);
  			storeAlignmetAsPhyFile(out, fileFormat);
  			// save meta if exset it is set
  			if(this.alignmentMeta.isMetaOutputNeeded()){
  				BufferedWriter outMeta = new BufferedWriter(new FileWriter(new File(outFile.getAbsoluteFile() + ".meta")));
  				storeMetaData(outMeta);
  			}
  
  
  		}else if(fileFormat == FileFormat.PHYLIP_TRANSLATED_AMINO_ACID){
  			setTranslationOnePos(true);
  			storeAlignmetAsPhyFile(out, FileFormat.PHYLIP_RELAXED_PADDED_AKA_LONG_NAME_SEQUENTIAL);
  			// save meta if exset it is set
  			if(this.alignmentMeta.isMetaOutputNeeded()){
  				//AlignmentMeta translatedMeta = getTranslatedMeta();
  				BufferedWriter outMeta = new BufferedWriter(new FileWriter(new File(outFile.getAbsoluteFile() + ".meta")));
  				//	storeTranslatedMetaData(outMeta, translatedMeta);
  			}
  		}else if(fileFormat == FileFormat.FASTA_TRANSLATED_AMINO_ACID){		
  			setTranslationOnePos(true);
  			storeAlignmetAsFasta(out);
  			// save meta if exset it is set
  			if(this.alignmentMeta.isMetaOutputNeeded()){
  				//AlignmentMeta translatedMeta = getTranslatedMeta();
  				BufferedWriter outMeta = new BufferedWriter(new FileWriter(new File(outFile.getAbsoluteFile() + ".meta")));
  				//storeTranslatedMetaData(outMeta, translatedMeta);
  			}
  		}else if(fileFormat == FileFormat.CLUSTAL){
  			setTranslationOnePos(false);
  			storeAlignmetAsClustal(out);
  		}else if(fileFormat == FileFormat.MSF){
  			setTranslationOnePos(false);
  			storeAlignmetAsMSF(out);
  		}else if(fileFormat == FileFormat.NEXUS){
  			setTranslationOnePos(false);
  			AliViewExtraNexusUtilities.exportAlignmentAsNexus(new BufferedWriter(new FileWriter(outFile)), this, false,nexusDatatype);
  		}else if(fileFormat == FileFormat.NEXUS_TRANSLATED_AMINO_ACID){
  			// make sure it is translated
  			setTranslationOnePos(true);
  			AliViewExtraNexusUtilities.exportAlignmentAsNexus(new BufferedWriter(new FileWriter(outFile)), this, false, AliViewExtraNexusUtilities.DATATYPE_PROTEIN);
  		}else if(fileFormat == FileFormat.NEXUS_SIMPLE){
  			setTranslationOnePos(false);
  			AliViewExtraNexusUtilities.exportAlignmentAsNexus(new BufferedWriter(new FileWriter(outFile)), this, true, nexusDatatype);
  		}else if(fileFormat == FileFormat.NEXUS_CODONPOS_CHARSET){
  			setTranslationOnePos(false);
  			AliViewExtraNexusUtilities.exportAlignmentAsNexusCodonpos(new BufferedWriter(new FileWriter(outFile)), this, AliViewExtraNexusUtilities.DATATYPE_DNA);
  		}
  		// revert translation
  		setTranslationOnePos(wasTranslated);
  	}
  
  	/*
  	 * utility method
  	 */
  	/*
  	private String translateSequence(Sequence seq){
  		AATranslator aaTransSeq = new AATranslator(getAlignmentMeta().getCodonPositions(),getGeneticCode());
  		aaTransSeq.setSequence(seq);
  		return aaTransSeq.getTranslatedAsString();
  	}
  
  	private Alignment getTranslatedAlignment(){
  		AlignmentMeta transMeta = getTranslatedMeta();
  		AlignmentListModel transSeq = createTranslatedSequences();
  		Alignment translatedAlignment = new Alignment(null, null, transSeq, transMeta);
  		return translatedAlignment;
  	}
  	 */
  
  	//
  	//  TODO this is not working for fileSequences
  	//
  	/*
  	private AlignmentListModel createTranslatedSequences(){
  		AlignmentListModel transSeqs = new MemorySequenceAlignmentListModel();
  		for(Sequence seq: sequences){			
  			transSeqs.add(new PhylipSequence(seq.getName(), translateSequence(seq)));
  		}
  		return transSeqs;
  	}
  	 */
  
  
  	// TODO this might get wrong if translating sequence and sequence order in alignment not is same, or sequence 
  	// has x offset in alignment
  	//	private AlignmentMeta getTranslatedMeta(){	
  	//		CharSets charsetsTrans = new CharSets();
  	//		Excludes excludesTrans = new Excludes();
  	//		AlignmentMeta metaTrans = new AlignmentMeta(excludesTrans,null,charsetsTrans,alignmentMeta.getGeneticCode());
  	//		
  	//		// This is the translation of the Charsets
  	//		for(CharSet charset: alignmentMeta.getCharsets()){
  	//			ArrayList<NexusRange> nexusRanges = charset.getAsContinousNexusRanges();
  	//			if(nexusRanges != null){
  	//				logger.info("rangesSize" + nexusRanges.size());
  	//				
  	//				CharSet translatedSet = new CharSet(charset.getName());
  	//				for(NexusRange range: nexusRanges){	
  	//					NexusRange translatedRange = new NexusRange(alignmentMeta.getCodonPositions().getAminoAcidPosFromNucleotidePos(range.getMinimumInt()) + 1, // +1 för Nexus ranges börjar på 1
  	//							                                         alignmentMeta.getCodonPositions().getAminoAcidPosFromNucleotidePos(range.getMaximumInt()) + 1, // +1 för Nexus ranges börjar på 1
  	//							                                           range.getPositionVal(), range.getSteps()); 
  	//					logger.info(translatedRange);
  	//					translatedSet.addNexusRange(translatedRange);
  	//				}
  	//				charsetsTrans.add(translatedSet);
  	//			}
  	//		}
  	//		
  	//		// This is the translaiton of the excludes
  	//		for(NexusRange range: alignmentMeta.getExcludes().getAsContinousNexusRanges()){
  	//			NexusRange translatedRange = new NexusRange(alignmentMeta.getCodonPositions().getAminoAcidPosFromNucleotidePos(range.getMinimumInt()),
  	//					                                         alignmentMeta.getCodonPositions().getAminoAcidPosFromNucleotidePos(range.getMaximumInt()),
  	//					                                         range.getPositionVal(), range.getSteps());
  	//			excludesTrans.addNexusRange(translatedRange);
  	//		}
  	//		return metaTrans;
  	//	}
  
  
  	/*
  	 * Alternate method
  	 */
  	public boolean mergeTwoSequences(List<Sequence> seqs, boolean allowOverlap) {
  		boolean isMerged = false;
  		if(seqs.size() == 2){
  			isMerged = mergeTwoSequences(seqs.get(0),seqs.get(1), allowOverlap);
  		}
  		return isMerged;
  	}
  
  
  	public boolean mergeTwoSequences(Sequence seq1, Sequence seq2, boolean allowOverlap){
  		if(seq1 instanceof InMemorySequence && seq2 instanceof InMemorySequence){
  			boolean wasMerged = sequences.mergeTwoSequences((InMemorySequence)seq1, (InMemorySequence) seq2, allowOverlap);
  			return wasMerged;
  		}
  		else{
  			return false;
  		}
  	}
  
  
  	public void deleteAllExsetBases(){	
  		// first create a deletemask
  		boolean[] deleteMask = new boolean[this.alignmentMeta.getExcludes().getMaximumEndPos()];
  		for(int n = 0; n < deleteMask.length; n++){
  			if(this.alignmentMeta.isExcluded(n)){
  				deleteMask[n] = true;
  			}
  		}
  		sequences.deleteBasesInAllSequencesFromMask(deleteMask);
  		//and finally remove in AlignmentMeta(excludes, codonpos & charset)
  		alignmentMeta.deleteFromMask(deleteMask);
  		//		sequencesChanged();
  	}
  
  	public ArrayList<Primer> findPrimerInSelection(){
  		ArrayList<Primer> allPrimers = new ArrayList<Primer>();
  
  		// no selection returm
  		if(! sequences.hasSelection()){
  			return allPrimers;
  		}
  
  		String selection = getSelectionAsNucleotides();
  
  		//		logger.info(selection);
  
  		String[] selectionSeq = selection.split("\\n");
  
  		// Start with a prototype as consensus
  		int consensusVal[] = new int[selectionSeq[0].length()];
  
  		for(String seq: selectionSeq){
  			for(int n = 0; n < consensusVal.length; n++){
  				int baseVal = NucleotideUtilities.baseValFromChar(seq.charAt(n));
  				// Create consensus by bitwise OR of the bases in the same column 
  				consensusVal[n] = consensusVal[n] | baseVal;
  			}
  		}
  
  		// Create consensus from baseValues
  		StringBuilder consensusBuilder = new StringBuilder();
  		for(int n = 0; n < consensusVal.length; n++){
  			consensusBuilder.append(NucleotideUtilities.charFromBaseVal(consensusVal[n]));
  		}
  		String consensus = consensusBuilder.toString();
  
  		logger.info(consensus.length());
  
  		// remove gaps in consensus
  		consensus = consensus.replaceAll("\\-", "");
  
  		// create primer put them in a list and sort list (on score)
  		// create all primer min-max bases long 
  		int primerMinLen = Settings.getMinPrimerLength().getIntValue();
  		int primerMaxLen = Settings.getMaxPrimerLength().getIntValue();
  		int primerMinTM = Settings.getPrimerMinTM().getIntValue();
  		int primerMaxTM = Settings.getPrimerMaxTM().getIntValue();
  		Dimer.setDimerLengthThreashold(Settings.getDimerReportThreashold().getIntValue());
  
  		long nCount = 0;
  		int selectionStartPos = getFirstSelectedPositionX();
  		for(int winSize = primerMinLen; winSize <= primerMaxLen; winSize ++){
  
  			int offset = 0;		
  			int startPos = offset;
  			int endPos = offset + winSize;
  
  			logger.info(consensus.length());
  			logger.info( endPos);
  
  			while(endPos <= consensus.length()){
  				String primerSeq = consensus.substring(startPos, endPos);
  				Primer aPrimer = new Primer(primerSeq, selectionStartPos + startPos);
  				// only add primers below score
  				long degenFold = aPrimer.getDegenerateFold();
  				double baseStackTM = aPrimer.getBaseStackingAvgTm();
  				if(degenFold <= PRIMER_MAX_DEGENERATE_SCORE){
  					if(baseStackTM >= primerMinTM && baseStackTM <= primerMaxTM){
  						allPrimers.add(aPrimer);
  					}
  				}
  				offset ++;
  				startPos = offset;
  				endPos = offset + winSize;
  				nCount ++;
  			}
  			logger.info("winSize=" + winSize);
  		}
  		logger.info("Primers tested:" + nCount);
  
  		Collections.sort(allPrimers);
  
  
  		return allPrimers;
  		// https://ecom.mwgdna.com/services/webgist/mops.tcl?ot=OLIGO_ALC_UNMOD&oligoSequence=RTTGCTYRAKACTCGGTRA&ShowModiTables=OFF&oligo_name=&mod3=&mod5=&modx=&modz=&ot=OLIGO_ALC_UNMOD&oligo_name=&oligoSequence=RTT+GCT+YYY+RAK+ACT+CGG+TRA&action=properties&next_url=
  
  	}
  
  	/*
  	 * 
  	 * 	Selection section
  	 * 
  	 * 
  	 */
  
  	private String getSelectionAsNucleotides(){
  		return sequences.getSelectionAsNucleotides();
  	}
  
  	public void copySelectionToClipboardAsFasta(){
  		StringWriter writer = new StringWriter();
  		sequences.writeSelectionAsFasta(writer);
  		// Set to clipboard
  		StringSelection ss = new StringSelection(writer.toString());
  		Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null);
  	}
  
  	public void copySelectionToClipboardAsNucleotides(){
  		// Set to clipboard
  		StringSelection ss = new StringSelection(getSelectionAsNucleotides());
  		Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null);
  	}
  
  	public void copySelectionNames() {
  		// Set to clipboard
  		logger.info("copy sel names");
  		String names = sequences.getSelectionNames();
  		StringSelection ss = new StringSelection(names);
  		Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null);
  		Toolkit.getDefaultToolkit().getSystemSelection().setContents(ss, null);
  	}
  
  
  
  	public boolean isBaseSelected(int x, int y) {
  		return sequences.isBaseSelected(x, y);
  	}
  
  	public void setSelectionAt(int xPos, int yPos) {
  		setSelectionAt(xPos, yPos, false);
  
  	}
  
  	public void setSelectionAt(int xPos, int yPos, boolean clearFirst) {
  		sequences.setSelectionAt(xPos, yPos, clearFirst);
  		//fireSelectionChanged();
  	}
  
  	public void setSelectionWithin(Rectangle bounds) {
  		sequences.setSelectionWithin(bounds);
  		//fireSelectionChanged();
  	}
  
  	public void reverseComplementAlignment(){
  		sequences.reverseComplement();
  		alignmentMeta.reverse(getMaximumSequenceLength());
  	}
  
  	public void reverseComplementFullySelectedSequences(){
  		sequences.reverseComplementFullySelectedSequences();
  	}
  
  	/*
  	 * Find
  	 */
  	public FindObject findInNames(FindObject findObj) {
  		findObj = sequences.findInNames(findObj);
  		return findObj;
  	}
  
  	public void clearFindLastPos() {
  		nextFindSequenceNumber = 0;
  		nextFindStartPos = 0;
  	}
  
  	public FindObject findAndSelectInSequences(FindObject findObj){
  		findObj = sequences.findAndSelect(findObj);
  		return findObj;
  	}
  
  	public Sequence getSequenceByName(String name){
  		return sequences.getSequenceByName(name);
  	}
  
  	public Sequence getSequenceByID(int id){
  		return sequences.getSequenceByID(id);
  	}
  
  
  	public void incReadingFrame() {
  		int newReadFrame = this.getReadingFrame();
  		newReadFrame ++;
  		if(newReadFrame > 3){
  			newReadFrame = 1;
  		}
  		this.setReadingFrame(newReadFrame);
  	}
  
  	public int getReadingFrame() {
  		return this.readingFrame;
  	}
  
  	public void decReadingFrame() {
  		int newReadFrame = this.getReadingFrame();
  		newReadFrame --;
  		if(newReadFrame < 1){
  			newReadFrame = 3;
  		}
  		this.setReadingFrame(newReadFrame);
  	}
  
  
  	public List<Sequence> clearSelectedBases(boolean undoable){
  		List<Sequence> affected =  sequences.replaceSelectedBasesWithGap(undoable);
  		return affected;
  	}
  
  	public void deleteFullySelectedSequences(){
  		sequences.deleteFullySelectedSequences();
  	}
  
  	public void deleteSelectedBases() {
  
  		int first = sequences.getFirstSelectedWholeColumn();
  		// If whole column is selected do a bit different since we also want to delete
  		// in meta
  		if(first != -1){
  			int last = sequences.getLastSelectedWholeColumn();
  
  			logger.info("first" + first);
  			logger.info("last" + last);
  
  			// create a bit-mask with pos to delete
  			boolean[] deleteMask = new boolean[getMaxX()];
  			for(int n = first; n <= last; n++){
  				deleteMask[n] = true;
  			}
  			sequences.deleteBasesInAllSequencesFromMask(deleteMask);
  			//and finally remove in AlignmentMeta(excludes, codonpos & charset)
  			//logger.info(alignmentMeta.getCodonPositions().getLength());
  			alignmentMeta.deleteFromMask(deleteMask);
  			//logger.info(alignmentMeta.getCodonPositions().getLength());
  
  		}
  		// only delete in selected sequences
  		else{	
  			List<Sequence> affected =  sequences.deleteSelectedBases();
  		}
  	}
  
  	public String getConsensus(){
  		return sequences.getConsensus();
  	}
  
  	public void deleteVerticalGaps(){
  		String cons = getConsensus();
  
  		logger.info("done cons" + cons);
  
  		//logger.info('-' == cons.charAt(0));
  
  		// if there is a gap
  		if(cons.indexOf(SequenceUtils.GAP_SYMBOL) >= 0){		
  
  			logger.info("there is a gap in cons");
  
  			// create a bit-mask with pos to delete
  			boolean[] deleteMask = new boolean[cons.length()];
  			for(int n = 0; n < deleteMask.length; n++){
  				if(cons.charAt(n) == SequenceUtils.GAP_SYMBOL){
  					deleteMask[n] = true;
  				}
  			}	
  			sequences.deleteBasesInAllSequencesFromMask(deleteMask);
  
  			//and finally remove in AlignmentMeta(excludes, codonpos & charset)
  			alignmentMeta.deleteFromMask(deleteMask);
  		}
  
  	}
  
  	public void addFasta(String clipboardSelection) {
  		try {
  			AlignmentListModel sequencesFromClipboard = seqFactory.createFastaSequences(new StringReader(clipboardSelection));
  			sequences.addAll(sequencesFromClipboard, true);
  		} catch (AlignmentImportException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  	}
  
  	public void addNewSequence() {
  		int minLen = sequences.getShortestSequenceLength();
  		int maxLen = sequences.getLongestSequenceLength();
  		int len;
  		if(maxLen == minLen){
  			len = maxLen;
  		}else{
  			len = 1;
  		}
  		String seqName = "New_sequence";
  
  		byte[] bases = new byte[len];
  		Arrays.fill(bases,(byte)'-');
  
  		// TODO this is not working on file sequences 
  		Sequence newSeq = new InMemorySequence(seqName, bases);
  
  		Point lastSel = sequences.getLastSelectedPos();
  
  		//			logger.info("lastSel.y" + lastSel.y);
  		//			logger.info("sequences.getSize()" + sequences.getSize());
  
  		if(lastSel != null && lastSel.y + 1 <  sequences.getSize()){
  			sequences.add(lastSel.y +1, newSeq);
  		}else{
  			sequences.add(newSeq);
  		}
  	}
  
  
  	public void addSequences(File additionalFile) {
  		addSequences(additionalFile, 0);
  	}
  
  	public void addSequences(File additionalFile, int index) {
  		try {
  			AlignmentListModel additionalSequences = seqFactory.createSequences(additionalFile);
  			sequences.addAll(index, additionalSequences);
  
  		} catch (AlignmentImportException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  	}
  
  
  	/*
  	 * 
  	 * 
  	 * Start Aligner section
  	 * 
  	 * 
  	 * 
  	 */
  	public void clearSelectionOffset() {
  		sequences.setSelectionOffset(0);
  	}
  
  	public List<Sequence> moveSelectionRight(boolean undoable){
  		logger.info("move");
  		List<Sequence> previousState = sequences.moveSelectedResiduesRightIfGapIsPresent(undoable);
  		return previousState;
  	}
  
  	public List<Sequence> moveSelectionLeft(boolean undoable) {
  		List<Sequence> previousState = sequences.moveSelectedResiduesLeftIfGapIsPresent(undoable);
  		return previousState;
  	}
  
  	public List<Sequence> moveSelection(int diff, boolean undoable) {
  		List<Sequence> previousState = sequences.moveSelectedResiduesIfGapIsPresent(diff, undoable);
  		return previousState;
  	}
  	public boolean isMoveSelectionLeftPossible() {
  		return sequences.isGapPresentLeftOfSelection();
  	}
  	public boolean isMoveSelectionRightPossible() {
  		return sequences.isGapPresentRightOfSelection();
  	}
  
  	public List<Sequence> deleteGapMoveLeft(boolean undoable) {
  		List<Sequence> previousState = sequences.deleteGapMoveLeft(undoable);
  		if(previousState.size()> 0){
  			rightPadSequencesWithGapUntilEqualLength();
  		}
  		return previousState;
  	}
  
  	public List<Sequence> deleteGapMoveRight(boolean undoable) {
  		List<Sequence> previousState = sequences.deleteGapMoveRight(undoable);
  		if(previousState.size()> 0){
  			rightPadSequencesWithGapUntilEqualLength();
  		}
  		return previousState;
  	}
  
  	public List<Sequence> insertGapLeftOfSelectionMoveRight(boolean undoable) {
  		List<Sequence> previousState = sequences.insertGapLeftOfSelectedBase(undoable);
  		if(previousState.size()> 0){
  			rightPadSequencesWithGapUntilEqualLength();
  		}
  		return previousState;
  	}
  
  	public List<Sequence> insertGapRightOfSelectionMoveLeft(boolean undoable) {
  		List<Sequence> previousState = sequences.insertGapRightOfSelectedBase(undoable);
  		if(previousState.size()> 0){
  			leftPadSequencesWithGapUntilEqualLength();
  		}
  		return previousState;
  	}
  
  
  	public boolean rightPadSequencesWithGapUntilEqualLength(){
  		boolean wasPadded = sequences.rightPadWithGapUntilEqualLength();
  		//		if(wasPadded){
  		//			alignmentMeta.verifyLength(sequences.getLongestSequenceLength());
  		//		}
  		return wasPadded;
  
  	}
  
  	public boolean leftPadSequencesWithGapUntilEqualLength() {
  		boolean wasPadded = sequences.leftPadWithGapUntilEqualLength();
  		//		if(wasPadded){
  		//			alignmentMeta.verifyLength(sequences.getLongestSequenceLength());
  		//		}
  		return wasPadded;
  	}
  
  	/*
  	 * 
  	 * End Aligner section
  	 * 
  	 */
  
  	public long getSelectedBasesCount(){
  		return sequences.getSelectionSize();
  	}
  
  	public Rectangle getSelectionAsMinRect(){
  		if(! hasSelection()){
  			return null;
  		}
  		int minX = Integer.MAX_VALUE;
  		int minY = Integer.MAX_VALUE;
  		int maxX = Integer.MIN_VALUE;
  		int maxY = Integer.MIN_VALUE;
  
  		int seqIndex = 0;
  		for(int n = 0; n < sequences.getSize(); n++){
  			int[] selection = sequences.get(n).getSelectedPositions();
  			if(selection.length > 0){
  				minY = Math.min(minY, n);
  				maxY = Math.max(maxY, n);
  				minX = Math.min(minX, selection[0]);
  				maxX = Math.max(minX, selection[selection.length - 1]);
  			}
  		}
  		return new Rectangle(minX, minY, maxX - minX, maxY - minY);
  
  	}
  
  
  	public void addOrRemoveSelectionToExcludes() {
  		// check if anythin is excluded already - then remove
  		// otherwise add
  		Rectangle selection = getSelectionAsMinRect();
  
  		if(isTranslatedOnePos()){
  			selection = getAlignmentMeta().reTranslatePositions(selection);
  		}
  
  		boolean containsExcludedAlready = getAlignmentMeta().excludesIntersectsPositions(selection.x, selection.x + selection.width);
  
  		logger.info("containsExcludedAlready" + containsExcludedAlready);
  
  		if(containsExcludedAlready){
  			removeSelectionFromExcludes();
  		}else{
  			addSelectionToExcludes();
  		}
  	}
  
  	public void addSelectionToExcludes() {
  
  
  		Rectangle selection = getSelectionAsMinRect();
  
  		if(isTranslatedOnePos()){
  			selection = getAlignmentMeta().reTranslatePositions(selection);
  		}
  
  		getAlignmentMeta().excludeRange(selection.x, selection.x + selection.width);	
  
  		//	getAlignmentMeta().excludePositions(selection.x, selection.x + selection.width);	
  
  		//fireAlignmentMetaOnlyChanged();
  
  	}
  
  	public void removeSelectionFromExcludes() {
  		Rectangle selection = getSelectionAsMinRect();
  
  		if(isTranslatedOnePos()){
  			selection = getAlignmentMeta().reTranslatePositions(selection);
  		}
  
  		getAlignmentMeta().removeExcludeRange(selection.x, selection.x + selection.width);	
  
  		//getAlignmentMeta().excludesRemovePositions(selection.x, selection.x + selection.width);	
  	}
  
  	public void setSelectionAsCoding(int startOffset) {
  		Rectangle bounds = sequences.getSelectionBounds();
  
  		if(sequences.isTranslated()){
  			bounds = getAlignmentMeta().reTranslatePositions(bounds);
  		}
  
  		// TODO check that selection is continous
  
  		if(bounds != null){
  			this.alignmentMeta.getCodonPositions().addRange(bounds.x, bounds.x + bounds.width, startOffset);
  		}
  
  		//fireAlignmentMetaOnlyChanged();
  	}
  
  	public void setSelectionAsNonCoding() {
  		setSelectionAsCoding(0);
  	}
  
  	public void complementAlignment() {	
  		sequences.complement();
  	}
  
  	public boolean isEditedAfterLastSave() {
  		return isEditedAfterLastSave;
  	}
  
  	public void setEditedAfterLastSave(boolean isEditedAfterLastSave) {
  		this.isEditedAfterLastSave = isEditedAfterLastSave;
  	}
  
  	/*
  	public boolean isAllCharactersValid() {
  		return getFirstMuscleInvalidCharacter().length() == 0;
  	}
  	 */
  
  	// TODO should be different depending on alignment class nuc or protein
  	public String getFirstAlignmentProgInvalidCharacter() {
  		String testChars = "?ÅÄÖ*";
  		String invalidCharsInAlignment = "";
  		for(int n = 0; n < testChars.length(); n++){
  			char testChar = testChars.charAt(n);
  			for(Sequence seq: getSequences()){
  				if(seq.contains(testChar)){
  					invalidCharsInAlignment += testChar;
  					break; // breaking out of this is loop no reason to check other seq
  				}
  			}
  		}
  		return invalidCharsInAlignment;
  	}
  
  
  	public void setAlignmentFormat(FileFormat fileFormat) {
  		sequences.setFileFormat(fileFormat);
  	}
  
  	public FileFormat getFileFormat() {
  		return sequences.getFileFormat();
  	}
  
  	public int getLongestSequenceName() {
  		return sequences.getLongestSequenceName();
  	}
  
  	public int getCodonPosAt(int x) {
  		return alignmentMeta.getCodonPosAt(x);
  	}
  
  	public void setReadingFrame(int readingFrame){
  		if(readingFrame > 0 && readingFrame <=3){
  			this.readingFrame = readingFrame;
  			alignmentMeta.setReadingFrame(readingFrame);
  			//	fireAlignmentMetaOnlyChanged();
  		}
  	}
  
  	public boolean isFullCodonStartingAt(int x) {
  		return alignmentMeta.isFullCodonStartingAt(x);
  	}
  
  	public boolean isExcluded(int x) {
  		return alignmentMeta.isExcluded(x);
  	}
  
  	public AlignmentMeta getAlignentMetaCopy(){
  		return alignmentMeta.getCopy();
  	}
  
  	public void setAlignentMeta(AlignmentMeta aliMeta) {
  		this.alignmentMeta = aliMeta;
  		//fireAlignmentMetaOnlyChanged();
  	}
  
  	public AlignmentMeta getAlignmentMeta() {
  		return alignmentMeta;
  	}
  
  	public ArrayList<Integer> getAllCodonPositions(int i, boolean removeExcluded, int startPos, int endPosInclusive) {		
  		return alignmentMeta.getAllCodonPositions(i, removeExcluded, startPos, endPosInclusive);
  	}
  
  	/*
  	 * 
  	 */
  	public void selectDuplicates(){
  		sequences.findAndSelectDuplicates();
  	}
  
  	public HashSet<String> findDuplicateNames(){
  		if(sequences != null){
  			return sequences.findDuplicateNames();
  		}else{
  			return null;
  		}
  	}
  
  	public void selectDuplicateNamesSequences(){
  		sequences.selectDuplicateNamesSequences();
  	}
  
  
  	/*
  	 * 
  	 * This one is just dumpting to stdout
  	 * 
  	 */
  	public void getStats() {
  
  		// TODO might not work if no charset
  		for(CharSet charSet: alignmentMeta.getCharsets()){
  
  			// Start with a prototype as consensus
  			int consensusVal[] = new int[sequences.getLongestSequenceLength()];
  			int missingCount = 0;
  			int nonemptySeqCount = 0;
  
  			for(Sequence seq: sequences){
  				boolean sequenceIsAllEmpty = true;
  				int sequenceMissingCount = 0;
  				for(int n = 0; n <consensusVal.length; n++){
  
  					if(charSet.contains(n)){
  
  						int baseVal = NucleotideUtilities.baseValFromBase(seq.getBaseAtPos(n));
  						// Create consensus by bitwise OR of the bases in the same column
  						// Skip if n or unknown
  						if(baseVal == NucleotideUtilities.N || baseVal == NucleotideUtilities.UNKNOWN || baseVal == NucleotideUtilities.GAP){
  							sequenceMissingCount ++;
  						}else{
  							consensusVal[n] = consensusVal[n] | baseVal;
  							sequenceIsAllEmpty = false;
  						}
  
  					}
  				}
  				if(! sequenceIsAllEmpty){
  					nonemptySeqCount ++;
  					missingCount += sequenceMissingCount;
  				}
  			}
  
  			// Create consensus from baseValues
  			StringBuilder consensusBuilder = new StringBuilder();
  			int invaribleCount = 0;
  			int varibleCount = 0;
  			for(int n = 0; n < consensusVal.length; n++){
  				if(charSet.contains(n)){
  					consensusBuilder.append(NucleotideUtilities.charFromBaseVal(consensusVal[n]));
  					if(consensusVal[n] == NucleotideUtilities.A || consensusVal[n] == NucleotideUtilities.C || consensusVal[n] == NucleotideUtilities.G || consensusVal[n] == NucleotideUtilities.T){
  						invaribleCount ++;
  					}
  					else{
  						varibleCount ++;
  					}
  				}
  			}
  			String consensus = consensusBuilder.toString();	
  
  			logger.info(charSet.getName());
  			logger.info("consensus" + consensus);
  			logger.info("invaribleCount" + invaribleCount);
  			logger.info("variableCount" + varibleCount);
  			logger.info("totalCount" + (invaribleCount + varibleCount));
  			logger.info("missingCount" + missingCount);
  			logger.info("nonemptySeqCount" + nonemptySeqCount);
  			double totalBases = (invaribleCount + varibleCount) * nonemptySeqCount;
  			double missingPercent = missingCount/totalBases;
  			logger.info("missingPercent" + missingPercent);
  		}
  	}
  
  
  	public ArrayList<Sequence> deleteEmptySequences() {
  		ArrayList<Sequence> deleted = sequences.deleteEmptySequences();
  		return deleted;
  	}
  
  	public void deleteSequence(Sequence sequence) {
  		sequences.deleteSequence(sequence);
  	}
  
  	public int getFirstSelectedSequenceIndex() {
  		int selectedIndex = -1;
  		int n = 0;
  		for(Sequence seq: sequences){
  			if(seq.hasSelection()){
  				selectedIndex = n;
  				break;
  			}
  			n++;
  		}
  		return selectedIndex;
  	}
  
  	public void selectSequencesWithIndex(List<Integer> selectedIndex) {
  		sequences.selectSequencesWithIndex(selectedIndex);
  	}
  
  	public void selectSequencesWithIndex(int[] selectedIndex) {
  		sequences.selectSequencesWithIndex(selectedIndex);
  	}
  
  	public GeneticCode getGeneticCode() {
  		return alignmentMeta.getGeneticCode();
  	}
  
  	public void setGeneticCode(GeneticCode genCode) {
  		alignmentMeta.setGeneticCode(genCode);
  		//fireAlignmentMetaOnlyChanged();
  	}
  
  	public boolean isUndoable() {
  		boolean isUndoable = true;
  		if(this.getSequences() != null){
  			if(this.getSequences() instanceof FileSequenceAlignmentListModel){
  				isUndoable = false;
  			}
  		}
  		return isUndoable;
  	}
  
  	public boolean isNucleotideAlignment() {
  		return (sequences.getSequenceType() == SequenceUtils.TYPE_NUCLEIC_ACID);
  	}
  
  	public boolean isAAAlignment() {
  		return (sequences.getSequenceType() == SequenceUtils.TYPE_AMINO_ACID);
  	}
  
  	public boolean isUnknownAlignment() {
  		return (sequences.getSequenceType() == SequenceUtils.TYPE_UNKNOWN);
  	}
  
  	public boolean replaceSelectedWithChar(char newChar) {
  		List<Sequence> replacedSeqs = sequences.replaceSelectedWithChar(newChar, isUndoable());
  		if(replacedSeqs.size() > 0){
  			return true;
  		}else{
  			return false;
  		}
  	}
  
  	public void selectEverythingWithinGaps(Point point) {
  		sequences.selectEverythingWithinGaps(point);
  	}
  
  	public void realignNucleotidesUseThisAAAlignmentAsTemplate(Alignment realignment) throws Exception {
  		sequences.realignNucleotidesUseTheseAASequenceAsTemplate(realignment.getSequences());
  	}
  
  	public void deleteAllGaps() {
  		sequences.deleteAllGaps();
  	}
  
  	public void padAndTrimSequences(){
  		boolean wasPadded = sequences.rightPadWithGapUntilEqualLength();
  		boolean wasTrimed = sequences.rightTrimSequencesRemoveGapsUntilEqualLength();
  	}
  
  	public void trimSequences(){
  		boolean wasTrimed = sequences.rightTrimSequencesRemoveGapsUntilEqualLength();
  	}
  
  	public void selectAll(){
  		sequences.selectAll();
  	}
  
  	public void selectionExtendRight() {
  		sequences.selectionExtendRight();
  	}
  
  	public void selectionExtendLeft() {
  		sequences.selectionExtendLeft();
  	}
  	
  	public void selectionExtendDown() {
  		sequences.selectionExtendDown();
  	}
  	
  	public void selectionExtendTop() {
  		sequences.selectionExtendTop();
  	}
  
  	public void invertSelection() {
  		long startTime = System.currentTimeMillis();
  		sequences.invertSelection();
  		long endTime = System.currentTimeMillis();
  		logger.info("Invert took" + (endTime - startTime) + " milliseconds");
  	}
  
  	public boolean isPositionValid(int x, int y) {
  		return sequences.isPositionValid(x,y);
  	}
  
  	public boolean getSelectionAt(int x, int y) {
  		return sequences.isBaseSelected(x, y);
  	}
  
  	public void setAllHorizontalSelectionAt(int y, boolean selection) {
  		if(selection == true){
  			sequences.selectSequenceWithIndex(y);
  		}else{
  			sequences.clearAllSelectionInSequenceWithIndex(y);
  		}
  	}
  
  	public void copySelectionFromSequenceTo(int indexFrom, int indexTo) {
  		sequences.copySelectionFromInto(indexFrom,indexTo);
  		//fireSelectionChanged();
  
  	}
  
  	public void selectColumn(int columnIndex) {
  		sequences.selectColumn(columnIndex);
  	}
  
  	public void clearColumnSelection(int columnIndex) {
  		sequences.clearColumnSelection(columnIndex);
  		//fireSelectionChanged();
  	}
  
  	public void copySelectionFromPosX1toX2(int x1, int x2) {
  		sequences.copySelectionFromPosX1toX2(x1, x2);
  		//fireSelectionChanged();
  	}
  
  	public void setEditMode(boolean editMode) {
  		this.editMode = editMode;
  	}
  
  	public boolean isEditMode() {
  		return editMode;
  	}
  
  	public Point getFirstSelectedPosition() {
  		return sequences.getFirstSelectedPos();
  	}
  
  	public int getFirstSelectedPositionX() {
  		Point pos = sequences.getFirstSelectedPos();
  		if(pos == null){
  			return 0;
  		}else{
  			return pos.x;
  		}
  	}
  
  	public int getFirstSelectedUngapedPositionX() {
  		Point pos = sequences.getFirstSelectedUngapedPos();
  		if(pos == null){
  			logger.info("pos == null");
  			return -1;
  		}else{
  			logger.info("pos == " + pos.x);
  			return pos.x;
  		}
  	}
  
  	public void sortSequencesByName() {
  		sequences.sortSequencesByName();
  	}
  
  	public void sortSequencesByCharInSelectedColumn() {
  		sequences.sortSequencesByCharInSelectedColumn(getHistogram());
  	}
  
  	public AliHistogram getHistogram(){
  		return sequences.getHistogram();
  	}
  
  	public boolean hasSelection() {
  		return sequences.hasSelection();
  	}
  
  	public List<Sequence> replaceSelectedCharactersWithThis(Alignment realignment) {
  		boolean undoable = true;
  		List<Sequence> affected = sequences.replaceSelectedCharactersWithThis(realignment.getSequences(), undoable);
  		return affected;
  	}
  
  	public void saveSelectedSequencesAsFastaFile(File outFile, boolean useIDAsName) throws IOException {
  		BufferedWriter buffWriter = new BufferedWriter(new FileWriter(outFile));
  		sequences.writeSelectedSequencesAsFasta(buffWriter, useIDAsName);
  		buffWriter.flush();
  		buffWriter.close();
  	}
  
  	public void saveUnSelectedSequencesAsFastaFile(File outFile, boolean useIDAsName) throws IOException {
  		BufferedWriter buffWriter = new BufferedWriter(new FileWriter(outFile));
  		sequences.writeUnSelectedSequencesAsFasta(buffWriter, useIDAsName);
  		buffWriter.flush();
  		buffWriter.close();
  	}
  
  	public boolean isEditable() {
  		boolean isEditable = false;
  		if(sequences != null){
  			isEditable = sequences.isEditable();
  		}
  		//		if(sequences.isTranslated()){
  		//			isEditable = false;
  		//		}
  		return isEditable;
  	}
  
  	public boolean isSelectable(){
  		return isSelectable;
  	}
  
  	public long getSelectionSize() {
  		return sequences.getSelectionSize();
  	}
  
  	public void fileSequenceContentsChanged() {
  		logger.info("fileSequenceContChanged");
  		//		if(alignmentMeta.getCodonPositions().size()getLength() == 0){
  		//			//logger.info("sequences.getLongestSequenceLength()" + sequences.getLongestSequenceLength());
  		//			alignmentMeta = new AlignmentMeta(this.geneticCode);
  		//		}
  	}
  
  	public void setTranslationOnePos(boolean showTranslationOnePos){
  		if(isNucleotideAlignment()){
  			sequences.setTranslation(showTranslationOnePos);
  			alignmentMeta.setTranslation(showTranslationOnePos);
  		}
  	}
  
  	public boolean isTranslatedOnePos(){
  		return sequences.isTranslated();
  	}
  
  	public boolean hasFullySelectedSequences() {
  		return sequences.hasFullySelectedSequences();
  	}
  
  	public void sortSequencesByThisModel(AlignmentListModel prevSeqOrder) {
  		sequences.sortSequencesByThisModel(prevSeqOrder);
  	}
  
  	public int getSelectedColumnCount() {
  		return sequences.getSelectedColumnCount();
  	}
  
  	public int getSelectedSequencesCount() {
  		return sequences.getSelectedSequencesCount();
  	}
  
  	public List<Sequence> getSelectedSequences() {
  		return sequences.getSelectedSequences();
  	}
  
  	public String getFirstSelectedSequenceName() {
  		return sequences.getFirstSelectedName();
  	}
  
  	public List<Sequence> setFirstSelectedSequenceName(String newName) {
  		List<Sequence> previous = sequences.setFirstSelectedName(newName);
  		return previous;
  	}
  
  	public ArrayList<CharSet> getSelectedCharsets() {
  		Rectangle selection = sequences.getSelectionBounds();
  		if(selection == null){
  			return new ArrayList<CharSet>();
  		}
  		ArrayList<CharSet> intersected = alignmentMeta.getCharsets().getIntersected(selection);
  		return intersected;
  	}
  
  	public void selectAll(CharSet aCharSet) {
  		sequences.selectAll(aCharSet);
  	}
  
  	public int countStopCodons() {
  		int totalCount = 0;
  		if(isNucleotideAlignment()){
  			for(Sequence seq: sequences){
  				totalCount += seq.countStopCodon();	
  			}
  		}else{
  			for(Sequence seq: sequences){
  				totalCount += seq.countChar('*');	
  			}
  		}
  		return totalCount;
  	}
  
  	public void saveFastaIndex() {
  		logger.info("this.fileFormat");
  
  		if(getFileFormat() == FileFormat.FASTA && sequences instanceof FileSequenceAlignmentListModel){
  			FileSequenceAlignmentListModel model = (FileSequenceAlignmentListModel) getSequences();
  
  			//String indexName = FileFormat.stripFileSuffixFromName(this.getAlignmentFile().getAbsolutePath());
  			String indexName = this.getAlignmentFile().getAbsolutePath();
  			indexName += ".fai";
  
  			File indexFile = new File(indexName);
  
  			try {
  				BufferedWriter out = new BufferedWriter(new FileWriter(indexFile));
  
  				Sequence firstSeq = sequences.get(0);
  
  
  				int charsPerLine = 0;
  				int newlinePos = firstSeq.indexOf('\n');
  				if(newlinePos == -1){
  					charsPerLine = firstSeq.getLength() + 2; // +2 because of .......
  				}else{
  					charsPerLine = newlinePos + 1 + 1; // +1 because of 0f index,  +1 because of LF;
  				}
  
  				int spacePerLine =  firstSeq.countChar(' ', 0, newlinePos);
  
  				logger.info("newlinePos" + newlinePos);
  				logger.info("spacePerLine" + spacePerLine);
  
  
  
  				spacePerLine += 1; // +1 because of LF 
  				int residuesPerLine = charsPerLine - spacePerLine;
  
  				//				int returnCount = firstSeq.countChar('\r');
  				//				int tabCount = firstSeq.countChar('\t');
  				//				int spaceCount = firstSeq.countChar(' ');
  
  				//	int totalLen = firstSeq.getLength();
  				//int lenWithoutWhite = totalLen - newlineCount - returnCount - tabCount - spaceCount;
  
  				//	logger.info("lenWithoutWhite" + lenWithoutWhite);
  
  
  				for(Sequence seq: sequences){
  
  					FastaFileSequence fileSeq = (FastaFileSequence) seq;
  
  					//String name = StringUtils.substring(fileSeq.getName(), 0, 50);
  					String name = fileSeq.getName();
  
  					out.write(name);
  
  					out.write('\t');
  
  					int length = fileSeq.getLength();
  					logger.info("length" + length);
  					int fullRows = length/charsPerLine;
  					logger.info("fullRows" + fullRows);
  					int diff = charsPerLine - residuesPerLine;
  					logger.info("diff" + diff);
  					double decimalPart = (double)length/(double)charsPerLine - (double)fullRows;
  					int remindDiff = (int) (decimalPart * (double)diff);
  					logger.info("remindDiff" + remindDiff);
  					diff = diff * fullRows;
  					diff = diff + remindDiff;
  
  					logger.info("diff" + diff);
  
  					int lengthWithoutWhite = length - diff + 1; // don't know exactly why +1
  
  					out.write("" + lengthWithoutWhite);
  
  					out.write('\t');
  
  					out.write("" + (fileSeq.getSequenceAfterNameStartPointer() -1));
  
  					out.write('\t');
  
  					out.write("" + residuesPerLine);
  
  					out.write('\t');
  
  					out.write("" + charsPerLine);
  
  					out.write(LF);
  
  				}	
  
  				out.flush();
  				out.close();
  
  
  			} catch (Exception e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  
  		}else{
  			Messenger.showOKOnlyMessage(Messenger.NO_FASTA_INDEX_COULD_BE_SAVED);
  		}
  	}
  
  	public void setTempSelection(Rectangle selectRectMatrixCoords) {
  		getSequences().setTempSelection(selectRectMatrixCoords);
  
  	}
  
  	public Rectangle getTempSelection() {
  		return getSequences().getTempSelection();
  	}
  
  	public void clearTempSelection() {
  		getSequences().clearTempSelection();
  
  	}
  
  	public void moveSelectedSequencesToBottom() {
  		getSequences().moveSelectedSequencesToBottom();
  
  	}
  
  	public void moveSelectedSequencesToTop() {
  		getSequences().moveSelectedSequencesToTop();
  
  	}
  
  	public void moveSelectedSequencesUp() {
  		getSequences().moveSelectedSequencesUp();
  
  	}
  
  	public void moveSelectedSequencesDown() {
  		getSequences().moveSelectedSequencesDown();
  
  	}
  
  	public void moveSelectedSequencesTo(int index) {
  		getSequences().moveSelectedSequencesTo(index);
  	}
  
  	public void selectIndices(List<Integer> allFoundIndices) {
  		getSequences().getAlignmentSelectionModel().selectSequencesWithIndex(allFoundIndices);
  	}
  
  	public boolean isFileSequences() {
  		if(getSequences() instanceof FileSequenceAlignmentListModel){
  			return true;
  		}
  		return false;
  	}
  
  	public boolean isEmptyAlignment() {
  		if(getSequences() == null || getSequences().size() == 0){
  			return true;
  		}
  		return false;
  	}
  
  	public void terminalGAPtoMissing() {
  		sequences.terminalGAPtoMissing();
  	}
  
  	public void missingToGAP() {
  		sequences.missingToGAP();
  	}
  
  	public boolean isSequencesEqualLength() {
  		return sequences.isEqualLength();
  	}
  
  }
  