package artificialFastqGenerator;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class is responsible for checking whether the user's command is valid,
 * and if it is, creating instances of the ArtificialFastqGenerator class in order
 * to generate fastq files.
 * 
 * Copyright (C) 2012 The Institute of Cancer Research (ICR).
 * 
 * This file is part of ArtificialFastqGenerator v1.0.0.
 * 
 * ArtificialFastqGenerator is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 * 
 * This program is distributed in the hope that it will be useful but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU Public License along with this
 * program. If not, see <http://www.gnu.org/licenses/>
 * 
 * Authour's contact email: Matthew.Frampton@icr.ac.uk
 */

public class Main {

	//ALWAYS ESSENTIAL:
	public static String outputPath = null;
	public static String referenceGenomePath = null;
	public static String startSequenceIdentifier;
	
	//SOMETIMES ESSENTIAL:
	public static String fastq1ForQualityScores = null;
	public static String fastq2ForQualityScores = null;
	
	//OPTIONAL (HAVE DEFAULT SETTINGS):
	public static int captureProbeLength = 150;
	public static boolean coverageBasedOnGCcontent = true;
	public static double coverageMeanPeak = 37.7;
	public static double coverageMeanPeakGCcontent = 0.45;
	public static double coverageMeanGCcontentSpread = 0.22;
	public static double coverageSD = 0.2;
	public static String DEBUG_NUCLEOBASES = "debug_nucleobases";
	public static String DEBUG_NUCLEOBASES_NUC_IDS = "debug_nucleobases_nuc_ids";
	public static String DEBUG_NUCLEOBASES_PE_IDS = "debug_nucleobases_pe_ids";
	public static String DEFAULT_OUTPUT = "default";
	public static String endSequenceIdentifier;
	public static int logRegionStats = 2;
	public static int nucleobaseBufferSize = 5000;
	public static String outputFormat = DEFAULT_OUTPUT;
	public static int readsContainingNfilter = 0;
	public static int readLength = 76;
	public static boolean simulateErrorInRead = false;
	public static int startingX = 1000;
	public static int startingY = 1000;
	public static int templateLengthMean = 210;
	public static double templateLengthSD = 60;
	public static boolean useRealQualityScores = false;
	
	//Logger:
	public final static Logger logger = Logger.getLogger(Main.class.getName());
	
	//Store information about all of the user arguments.
	public static ArrayList<HashMap<String,String>> userArgsInfoArrayList = new ArrayList<HashMap<String,String>>();
	private static String ARGUMENT = "ARGUMENT";
	private static String ABBREVIATION = "ABBREVIATION";
	private static String ESSENTIAL = "ESSENTIAL";
	private static String DESCRIPTION = "DESCRIPTION";
	private static String VALUE = "VALUE";
	
	/**
	 * Check whether the user's command is valid for artificial fastq generation.
	 * If it is, then create ArtificialFastqGenerator objects to create fastqs.
	 * 
	 * @param args
	 */
	
	public static void main(String[] args) {
		
		printCopyright();
		fillUserArgsInfoArrayList();
		Collections.sort(userArgsInfoArrayList,new UserArgsInfoArrayListComparator());
		ArrayList<String> commandArgs = new ArrayList<String>(Arrays.asList(args));	
		if (commandIsValidForFastqGeneration(commandArgs)) {
			readInNonEssentialArguments(commandArgs);
			FileHandler logFH = null;
			try {
				logger.setLevel(Level.ALL);
				logFH = new FileHandler(outputPath + ".log");
				logFH.setFormatter(new BriefLogFormatter());
				logger.addHandler(logFH);				
				for (HashMap<String,String> userArgInfo : userArgsInfoArrayList) {
					logger.log(Level.CONFIG,userArgInfo.get(ABBREVIATION) + "=" + userArgInfo.get(VALUE));}
				if (endSequenceIdentifier == null) {
					logger.log(Level.INFO,"No end sequence identifier was specified by the -E argument so" +
							" ArtificialFastqGenerator will stop generating reads at the end of the Fasta.");}
			} catch (IOException ioe) {
				ioe.printStackTrace();
				logger.log(Level.SEVERE,ArtificialFastqGenerationUtils.getStackTraceString(ioe));}
			ArtificialFastqGenerator artificialFastqGenerator = new ArtificialFastqGenerator();
			artificialFastqGenerator.generateArtificialFastqs(referenceGenomePath);
			logFH.close();
			}
		
	}
	
	/**
	 * Print the copyright statement.
	 */
	
	private static void printCopyright() {
		
		System.out.println("ArtificialFastqGenerator v1.0.0 Copyright (C) 2012 The Institute of Cancer Research (ICR).");
		System.out.println("");
		System.out.println("This Program is free software; you can redistribute it and/or modify it under the terms of " +
				"the GNU General Public License as published by the Free Software Foundation; either version 3 of the " +
				"License, or (at your option) any later version.");
		System.out.println("");
		System.out.println("Additional permissions under GNU GPL versions 3 section 7:");
		System.out.println("");
		System.out.println("This Program is distributed as a service to the research community and is experimental in " +
				"nature and may have hazardous properties. The Program is distributed WITHOUT ANY WARRANTY, express or " +
				"implied. In particular all warranties as to SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR PURPOSE are " +
				"excluded. See the GNU General Public License for more details.");
		System.out.println("");
		System.out.println("You should have received a copy of the GNU General Public License along with this program; if " +
				"not, see <http://www.gnu.org/licenses>.");
		System.out.println("");
		
	}
	
	/**
	 * Check that the user's command is valid for generating artificial fastqs. A command is invalid if either 1. the user has requested help, 2.
	 * has supplied at least 1 unrecognised arguments, 3. has failed to supply an essential argument.
	 * 
	 * @param command - the command arguments.
	 * @return commandIsValid - true if the command is valid, else false.
	 */
	
	private static boolean commandIsValidForFastqGeneration(ArrayList<String> command) {
				
		//Has the user requested help?
		if (command.contains("-h")) {
			printHelpMessage();
			return false;}
		
		//Check for unrecognised arguments...
		String unrecognisedArg = findFirstUnrecognisedArg(command);
		if (!unrecognisedArg.equals("")) {
			printHelpMessage();
			printErrorMessage(unrecognisedArg + " is an unrecognised argument.");
			return false;}
		
		//Essential fields...
		outputPath = getArgumentValueFromCommand("-O",command);
		if (outputPath == null) {
			printHelpMessage();
			printErrorMessage("The path for output files must be specified with the -O argument.");
			return false;}
		referenceGenomePath = getArgumentValueFromCommand("-R",command);
		if (referenceGenomePath == null) {
			printHelpMessage();
			printErrorMessage("The path to the reference sequence must be specified with the -R argument.");
			return false;}
		startSequenceIdentifier = getArgumentValueFromCommand("-S",command);
		if (startSequenceIdentifier == null) {
			printHelpMessage();
			printErrorMessage("The string in the reference after which reads should be generated must be specified with " +
					"the -S argument.");
			return false;
		} else if (!startSequenceIdentifier.startsWith(">")) {
			printHelpMessage();
			printErrorMessage("The start sequence identifier specified by the -S argument must begin with > .");
			return false;}
		
		//FastqsForQualityScores may be essential...
		if (getArgumentValueFromCommand("-U",command) != null) {
			useRealQualityScores = Boolean.parseBoolean(getArgumentValueFromCommand("-U",command));}
		if (useRealQualityScores) {
		//fastqsForQualityScores are essential
		fastq1ForQualityScores = getArgumentValueFromCommand("-F1",command);
		fastq2ForQualityScores = getArgumentValueFromCommand("-F2",command);
		if (fastq1ForQualityScores == null || fastq2ForQualityScores == null) {
			printHelpMessage();
			printErrorMessage("The path to the 2 fastq files whose quality scores will be used must be specified with the " +
					"-F1 and -F2 arguments.");
			return false;}
		}	
		return true;
		
	}
	
	/**
	 * Read in the non-essential arguments in the command.
	 * 
	 * @param command
	 */
	
	private static void readInNonEssentialArguments(ArrayList<String> command) {
		
		String argumentValue = getArgumentValueFromCommand("-GCR",command);
		if (argumentValue != null) {captureProbeLength = Integer.valueOf(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-GCC",command);
		if (argumentValue != null) {coverageBasedOnGCcontent = Boolean.parseBoolean(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-CMP",command);
		if (argumentValue != null) {coverageMeanPeak = Double.parseDouble(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-CMPGC",command);
		if (argumentValue != null) {coverageMeanPeakGCcontent = Double.parseDouble(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-CMGCS",command);
		if (argumentValue != null) {coverageMeanGCcontentSpread = Double.parseDouble(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-CSD",command);
		if (argumentValue != null) {coverageSD = Double.parseDouble(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-E",command);
		if (argumentValue != null) {endSequenceIdentifier = argumentValue;}
		argumentValue = getArgumentValueFromCommand("-F1",command);
		if (argumentValue != null) {fastq1ForQualityScores = argumentValue;}
		argumentValue = getArgumentValueFromCommand("-F2",command);
		if (argumentValue != null) {fastq2ForQualityScores = argumentValue;}
		argumentValue = getArgumentValueFromCommand("-L",command);
		if (argumentValue != null) {logRegionStats = Integer.parseInt(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-N",command);
		if (argumentValue != null) {nucleobaseBufferSize = Integer.parseInt(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-OF",command);
		if (argumentValue != null) {outputFormat = argumentValue;}
		argumentValue = getArgumentValueFromCommand("-RCNF",command);
		if (argumentValue != null) {readsContainingNfilter = Integer.parseInt(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-RL",command);
		if (argumentValue != null) {readLength = Integer.parseInt(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-SE",command);
		if (argumentValue != null) {simulateErrorInRead = Boolean.parseBoolean(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-X",command);
		if (argumentValue != null) {startingX = Integer.parseInt(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-Y",command);
		if (argumentValue != null) {startingY = Integer.parseInt(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-TLM",command);
		if (argumentValue != null) {templateLengthMean = Integer.parseInt(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-TLSD",command);
		if (argumentValue != null) {templateLengthSD = Integer.parseInt(argumentValue);}
		argumentValue = getArgumentValueFromCommand("-URQS",command);
		if (argumentValue != null) {useRealQualityScores = Boolean.parseBoolean(argumentValue);}
		
	}
	
	/**
	 * Find the first unrecognised argument (if there is one) i.e. the first argument which is not in userArgsInfoArrayList.
	 * 
	 * @param commandArgs
	 * @return firstUnrecognisedString
	 */
	
	private static String findFirstUnrecognisedArg(ArrayList<String> commandArgs) {
		
		ArrayList<String> recognisedArgs = new ArrayList<String>();
		for (HashMap<String,String> userArgInfo : userArgsInfoArrayList) {
			recognisedArgs.add(userArgInfo.get(ARGUMENT));}
		
		String firstUnrecognisedString = "";
		for (int i=0; i<commandArgs.size(); i++) {
			if ((i+2) % 2 == 0) {
				if (!recognisedArgs.contains(commandArgs.get(i))) {
					firstUnrecognisedString = commandArgs.get(i);
				}
			}
		}
		return firstUnrecognisedString;
		
	}
	
	/**
	 * Get an argument value from the command.
	 * 
	 * @param argument
	 * @param command
	 * @return value
	 */
	
	private static String getArgumentValueFromCommand(String argument, ArrayList<String> command) {
		
		if (command.contains(argument) && command.indexOf(argument) < command.size()-1) {
			if (command.indexOf(argument) < command.size()-1) {
				String value = command.get(command.indexOf(argument)+1);
				setValueInUserArgsInfoArrayList(argument, value);
				return value;
			}
		}
		return null;
		
	}
	
	/**
	 * Set the value field in userArgsInfoArrayList.
	 * 
	 * @param argument
	 * @param value
	 */
	
	private static void setValueInUserArgsInfoArrayList(String argument, String value) {
		
		for (HashMap<String,String> userArgsInfo : userArgsInfoArrayList) {
			if (userArgsInfo.get(ARGUMENT).equals(argument)) {
				userArgsInfo.put(VALUE,value);
				break;}
		}
		
	}
	
	/**
	 * Print a help message.
	 */
	
	private static void printHelpMessage() {
		
		System.out.println("-------------------------------------------------------------");
		System.out.println("ArtificialFastqGenerator v1.0.0, <Matthew.Frampton@icr.ac.uk>");
		System.out.println("-------------------------------------------------------------");
		System.out.println("-------------------------------------------------------------");
		System.out.println("");	    
		System.out.print("usage java -jar ArtificialFastqGenerator.jar ");
		for (HashMap<String,String> userArgInfo : userArgsInfoArrayList) {
			System.out.print(userArgInfo.get(ARGUMENT) + " <" + userArgInfo.get(ABBREVIATION) + "> ");}
		System.out.println("\n");
		System.out.println("-h\t\t\t\t\tPrint usage help.");
		for (HashMap<String,String> userArgInfo : userArgsInfoArrayList) {
			System.out.println(userArgInfo.get(ARGUMENT) + ", <" + userArgInfo.get(ABBREVIATION) + ">" + 
		userArgInfo.get(DESCRIPTION));
		}
		System.out.println("");
		System.out.println("BUGS:");
	    System.out.println("Any bugs should be reported to Matthew.Frampton@icr.ac.uk");	
	
	}
	

	/**
	 * Print a user error message.
	 * 
	 * @param errorMessage - the user error message to print.
	 */
	
	private static void printErrorMessage(String errorMessage) {
		
		System.out.println("");
		System.out.println("USER ERROR:");
		System.out.println(errorMessage);	
	
	}	
	
	/**
	 * Fill userArgsInfoArrayList with information about all of the user arguments.
	 */
	
	private static void fillUserArgsInfoArrayList() {		
		
		HashMap<String,String> arg1 = new HashMap<String,String>();
		arg1.put(ARGUMENT,"-O"); arg1.put(ABBREVIATION,"outputPath"); arg1.put(ESSENTIAL,"yes"); arg1.put(DESCRIPTION,"\t\t\tPath for the artificial fastq and log files, including their base name (must be specified)."); arg1.put(VALUE,outputPath);
		userArgsInfoArrayList.add(arg1);
		HashMap<String,String> arg2 = new HashMap<String,String>();
		arg2.put(ARGUMENT,"-R"); arg2.put(ABBREVIATION,"referenceGenomePath"); arg2.put(ESSENTIAL,"yes"); arg2.put(DESCRIPTION,"\t\tReference genome sequence file, (must be specified)."); arg2.put(VALUE,referenceGenomePath);
		userArgsInfoArrayList.add(arg2);
		HashMap<String,String> arg3 = new HashMap<String,String>();
		arg3.put(ARGUMENT,"-S"); arg3.put(ABBREVIATION,"startSequenceIdentifier"); arg3.put(ESSENTIAL,"yes"); arg3.put(DESCRIPTION,"\t\tPrefix of the sequence identifier in the reference after which read generation should begin (must be specified)."); arg3.put(VALUE,startSequenceIdentifier);
		userArgsInfoArrayList.add(arg3);
		HashMap<String,String> arg4 = new HashMap<String,String>();
		arg4.put(ARGUMENT,"-F1"); arg4.put(ABBREVIATION,"fastq1ForQualityScores"); arg4.put(ESSENTIAL,"sometimes"); arg4.put(DESCRIPTION,"\t\tFirst fastq file to use for real quality scores, (must be specified if useRealQualityScores = true)."); arg4.put(VALUE,fastq1ForQualityScores);
		userArgsInfoArrayList.add(arg4);
		HashMap<String,String> arg5 = new HashMap<String,String>();
		arg5.put(ARGUMENT,"-F2"); arg5.put(ABBREVIATION,"fastq2ForQualityScores"); arg5.put(ESSENTIAL,"sometimes"); arg5.put(DESCRIPTION,"\t\tSecond fastq file to use for real quality scores, (must be specified if useRealQualityScores = true)."); arg5.put(VALUE,fastq2ForQualityScores);
		userArgsInfoArrayList.add(arg5);
		HashMap<String,String> arg6 = new HashMap<String,String>();
		arg6.put(ARGUMENT,"-GCR"); arg6.put(ABBREVIATION,"GCcontentRegionSize"); arg6.put(ESSENTIAL,"no"); arg6.put(DESCRIPTION,"\t\tRegion size in nucleobases for which to calculate GC content, (default = 150)."); arg6.put(VALUE,String.valueOf(captureProbeLength));
		userArgsInfoArrayList.add(arg6);
		HashMap<String,String> arg7 = new HashMap<String,String>();
		arg7.put(ARGUMENT,"-GCC"); arg7.put(ABBREVIATION,"GCcontentBasedCoverage"); arg7.put(ESSENTIAL,"no"); arg7.put(DESCRIPTION,"\t\tWhether nucleobase coverage is biased by GC content (default = true)."); arg7.put(VALUE,String.valueOf(coverageBasedOnGCcontent));
		userArgsInfoArrayList.add(arg7);
		HashMap<String,String> arg8 = new HashMap<String,String>();
		arg8.put(ARGUMENT,"-CMP"); arg8.put(ABBREVIATION,"coverageMeanPeak"); arg8.put(ESSENTIAL,"no"); arg8.put(DESCRIPTION,"\t\tThe peak coverage mean for a region (default = 37.7)."); arg8.put(VALUE,String.valueOf(coverageMeanPeak));
		userArgsInfoArrayList.add(arg8);
		HashMap<String,String> arg9 = new HashMap<String,String>();
		arg9.put(ARGUMENT,"-CMPGC"); arg9.put(ABBREVIATION,"coverageMeanPeakGCcontent"); arg9.put(ESSENTIAL,"no"); arg9.put(DESCRIPTION,"\tThe GC content for regions with peak coverage mean (default = 0.45)."); arg9.put(VALUE,String.valueOf(coverageMeanPeakGCcontent));
		userArgsInfoArrayList.add(arg9);
		HashMap<String,String> arg10 = new HashMap<String,String>();
		arg10.put(ARGUMENT,"-CMGCS"); arg10.put(ABBREVIATION,"coverageMeanGCcontentSpread"); arg10.put(ESSENTIAL,"no"); arg10.put(DESCRIPTION,"\tThe spread of coverage mean given GC content (default = 0.22)."); arg10.put(VALUE,String.valueOf(coverageMeanGCcontentSpread));
		userArgsInfoArrayList.add(arg10);
		HashMap<String,String> arg11 = new HashMap<String,String>();
		arg11.put(ARGUMENT,"-CSD"); arg11.put(ABBREVIATION,"coverageSD"); arg11.put(ESSENTIAL,"no"); arg11.put(DESCRIPTION,"\t\t\tThe coverage standard deviation divided by the mean (default = 0.2)."); arg11.put(VALUE,String.valueOf(coverageSD));
		userArgsInfoArrayList.add(arg11);
		HashMap<String,String> arg12 = new HashMap<String,String>();
		arg12.put(ARGUMENT,"-E"); arg12.put(ABBREVIATION,"endSequenceIdentifier"); arg12.put(ESSENTIAL,"no"); arg12.put(DESCRIPTION,"\t\tPrefix of the sequence identifier in the reference where read generation should stop, (default = end of file)."); arg12.put(VALUE,endSequenceIdentifier);
		userArgsInfoArrayList.add(arg12);
		HashMap<String,String> arg13 = new HashMap<String,String>();
		arg13.put(ARGUMENT,"-L"); arg13.put(ABBREVIATION,"logRegionStats"); arg13.put(ESSENTIAL,"no"); arg13.put(DESCRIPTION,"\t\t\tThe region size as a multiple of -NBS for which summary coverage statistics are recorded (default = 2)."); arg13.put(VALUE,String.valueOf(logRegionStats));
		userArgsInfoArrayList.add(arg13);
		HashMap<String,String> arg14 = new HashMap<String,String>();
		arg14.put(ARGUMENT,"-N"); arg14.put(ABBREVIATION,"nucleobaseBufferSize"); arg14.put(ESSENTIAL,"no"); arg14.put(DESCRIPTION,"\t\tThe number of reference sequence nucleobases to buffer in memory, (default = 5000)."); arg14.put(VALUE,String.valueOf(nucleobaseBufferSize));
		userArgsInfoArrayList.add(arg14);
		HashMap<String,String> arg15 = new HashMap<String,String>();
		arg15.put(ARGUMENT,"-OF"); arg15.put(ABBREVIATION,"outputFormat"); arg15.put(ESSENTIAL,"no"); arg15.put(DESCRIPTION,"\t\t\t'default': standard fastq output; 'debug_nucleobases(_nuc|read_ids)': debugging."); arg15.put(VALUE,outputFormat);
		userArgsInfoArrayList.add(arg15);
		HashMap<String,String> arg16 = new HashMap<String,String>();
		arg16.put(ARGUMENT,"-RL"); arg16.put(ABBREVIATION,"readLength"); arg16.put(ESSENTIAL,"no"); arg16.put(DESCRIPTION,"\t\t\tThe length of each read, (default = 76)."); arg16.put(VALUE,String.valueOf(readLength));
		userArgsInfoArrayList.add(arg16);
		HashMap<String,String> arg17 = new HashMap<String,String>();
		arg17.put(ARGUMENT,"-SE"); arg17.put(ABBREVIATION,"simulateErrorInRead"); arg17.put(ESSENTIAL,"no"); arg17.put(DESCRIPTION,"\t\tWhether to simulate error in the read based on the quality scores, (default = false)."); arg17.put(VALUE,String.valueOf(simulateErrorInRead));
		userArgsInfoArrayList.add(arg17);
		HashMap<String,String> arg18 = new HashMap<String,String>();
		arg18.put(ARGUMENT,"-X"); arg18.put(ABBREVIATION,"xStart"); arg18.put(ESSENTIAL,"no"); arg18.put(DESCRIPTION,"\t\t\t\tThe first read's X coordinate, (default = 1000)."); arg18.put(VALUE,String.valueOf(startingX));
		userArgsInfoArrayList.add(arg18);
		HashMap<String,String> arg19 = new HashMap<String,String>();
		arg19.put(ARGUMENT,"-Y"); arg19.put(ABBREVIATION,"yStart"); arg19.put(ESSENTIAL,"no"); arg19.put(DESCRIPTION,"\t\t\t\tThe first read's Y coordinate, (default = 1000)."); arg19.put(VALUE,String.valueOf(startingY));
		userArgsInfoArrayList.add(arg19);
		HashMap<String,String> arg20 = new HashMap<String,String>();
		arg20.put(ARGUMENT,"-TLM"); arg20.put(ABBREVIATION,"templateLengthMean"); arg20.put(ESSENTIAL,"no"); arg20.put(DESCRIPTION,"\t\tThe mean DNA template length, (default = 210)."); arg20.put(VALUE,String.valueOf(templateLengthMean));
		userArgsInfoArrayList.add(arg20);
		HashMap<String,String> arg21 = new HashMap<String,String>();
		arg21.put(ARGUMENT,"-TLSD"); arg21.put(ABBREVIATION,"templateLengthSD"); arg21.put(ESSENTIAL,"no"); arg21.put(DESCRIPTION,"\t\tThe standard deviation of the DNA template length, (default = 60)."); arg21.put(VALUE,String.valueOf(templateLengthSD));
		userArgsInfoArrayList.add(arg21);
		HashMap<String,String> arg22 = new HashMap<String,String>();
		arg22.put(ARGUMENT,"-URQS"); arg22.put(ABBREVIATION,"useRealQualityScores"); arg22.put(ESSENTIAL,"no"); arg22.put(DESCRIPTION,"\t\tWhether to use real quality scores from existing fastq files or set all to the maximum, (default = false)."); arg22.put(VALUE,String.valueOf(useRealQualityScores));
		userArgsInfoArrayList.add(arg22);
		HashMap<String,String> arg23 = new HashMap<String,String>();
		arg23.put(ARGUMENT,"-RCNF"); arg23.put(ABBREVIATION,"readsContainingNfilter"); arg23.put(ESSENTIAL,"no"); arg23.put(DESCRIPTION,"\t\tFilter out no \"N-containing\" reads (0), \"all-N\" reads (1), \"at-least-1-N\" reads (2), (default = 0)."); arg23.put(VALUE,String.valueOf(readsContainingNfilter));
		userArgsInfoArrayList.add(arg23);
		
	}
	
	/**
	 * A Comparator for ordering the userArgInfo HashMaps in userArgsInfoArrayList. 
	 * 
	 * @author mframpton
	 */
	
	private static class UserArgsInfoArrayListComparator implements Comparator<HashMap<String,String>> {
		
		@Override
		/**
		 * Order the HashMaps in userArgsInfoArrayList so that the essential arguments are 1st, then the sometimes essential, and
		 * then the non-essential. Within these 3 groups, order alphabetically.
		 */
		
		public int compare(HashMap<String,String> arg1, HashMap<String,String> arg2) {
			
			String arg1Essential = arg1.get(ESSENTIAL);
			String arg2Essential = arg2.get(ESSENTIAL);
			String arg1Argument = arg1.get(ARGUMENT);
			String arg2Argument = arg2.get(ARGUMENT);
			
			if (!arg1Essential.equals(arg2Essential)) {
				if (arg1Essential.equals("yes") && arg2Essential.equals("no")) {
					return -1;
				} else if (arg1Essential.equals("yes") && arg2Essential.equals("sometimes")) {
					return -1;
				} else if (arg1Essential.equals("sometimes") && arg2Essential.equals("no")) {
					return -1;
				} else {
					return 1;
				}
			} else {
				ArrayList<String> tempArguments = new ArrayList<String>();
				tempArguments.add(arg1Argument);
				tempArguments.add(arg2Argument);
				Collections.sort(tempArguments);
				if (tempArguments.get(0) == arg1Argument) {
					return -1;
				} else {
					return 1;}
			}
			
		}
		
	}

}
