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

import htsjdk.samtools.fastq.FastqRecord;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.vetmeduni.io.FastqPairedRecord;
import org.vetmeduni.io.readers.FastqReaderInterface;
import org.vetmeduni.io.readers.paired.FastqReaderPairedImpl;
import org.vetmeduni.io.readers.paired.FastqReaderPairedInterface;
import org.vetmeduni.io.readers.paired.FastqReaderPairedSanger;
import org.vetmeduni.io.readers.single.FastqReaderSingleInterface;
import org.vetmeduni.io.readers.single.FastqReaderSingleSanger;
import org.vetmeduni.io.readers.single.FastqReaderWrapper;
import org.vetmeduni.io.writers.ReadToolsFastqWriterFactory;
import org.vetmeduni.io.writers.SplitFastqWriter;
import org.vetmeduni.methods.barcodes.BarcodeDictionary;
import org.vetmeduni.methods.barcodes.BarcodeDictionaryFactory;
import org.vetmeduni.methods.barcodes.BarcodeMethods;
import org.vetmeduni.tools.AbstractTool;
import org.vetmeduni.tools.defaults.CommonOptions;
import org.vetmeduni.utils.fastq.FastqLogger;
import org.vetmeduni.utils.record.FastqRecordUtils;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;

import static org.vetmeduni.tools.ToolNames.ToolException;

/**
 * Tool for split by barcode (in the read name) both BAM and FASTQ files
 *
 * TODO: tests!!
 *
 * @author <NAME>
 */
public class FastqBarcodeDetector extends AbstractTool {

	@Override
	public int run(String[] args) {
		try {
			// PARSING THE COMMAND LINE
			CommandLine cmd = programParser(args);
			File input1 = new File(cmd.getOptionValue("input1"));
			File input2 = (cmd.hasOption("input2")) ? new File(cmd.getOptionValue("input2")) : null;
			String outputPrefix = cmd.getOptionValue("output");
			File barcodes = new File(cmd.getOptionValue("bc"));
			int max;
			try {
				max = (cmd.hasOption("m")) ?
					Integer.parseInt(cmd.getOptionValue("m")) :
					BarcodeMethods.DEFAULT_MISMATCHES;
			} catch (IllegalArgumentException e) {
				throw new ToolException("Maximum mismatches should be an integer");
			}
			int nThreads = CommonOptions.numberOfThreads(logger, cmd);
			boolean multi = nThreads != 1;
			boolean split = cmd.hasOption("x");
			// logging command line
			logCmdLine(args);
			// create the combined dictionary and the barcode method associated
			BarcodeDictionary dictionary = BarcodeDictionaryFactory.createCombinedDictionary(barcodes);
			logger.info("Loaded barcode file for ", dictionary.numberOfUniqueSamples(), " samples with ",
				dictionary.numberOfSamples(), " different barcode sets");
			BarcodeMethods methods = new BarcodeMethods(dictionary);
			// create the reader and the writer
			FastqReaderInterface reader = getReaderForTool(input1, input2, CommonOptions.isMaintained(logger, cmd));
			SplitFastqWriter writer = getWriterForTools(outputPrefix, dictionary,
				cmd.hasOption(CommonOptions.disableZippedOutput.getOpt()), multi, input2 == null, split);
			// run the method
			run(reader, writer, methods, max);
		} catch (ToolException e) {
			// This exceptions comes from the command line parsing
			printUsage(e.getMessage());
			return 1;
		} catch (IOException e) {
			logger.debug(e);
			logger.error(e.getMessage());
			return 1;
		}
		return 0;
	}

	/**
	 * Get the reader for the tool based on the parameters
	 *
	 * @param input1       the input for the first pair
	 * @param input2       the input for the second pair; <code>null</code>
	 * @param isMaintained should be the format maintained or standardize?
	 *
	 * @return the reader for the file(s)
	 */
	private static FastqReaderInterface getReaderForTool(File input1, File input2, boolean isMaintained) {
		FastqReaderInterface toReturn;
		if (input2 == null) {
			toReturn = (isMaintained) ? new FastqReaderWrapper(input1) : new FastqReaderSingleSanger(input1);
		} else {
			toReturn = (isMaintained) ?
				new FastqReaderPairedImpl(input1, input2) :
				new FastqReaderPairedSanger(input1, input2);
		}
		return toReturn;
	}

	/**
	 * Get the writer for the tool based on the parameters
	 *
	 * @param prefix     the output prefix
	 * @param dictionary the barcode dictionary
	 * @param dgzip      disable gzip?
	 * @param multi      multi-thread output?
	 * @param single     single end?
	 * @param split      should we split by barcode?
	 *
	 * @return the writer for splitting
	 */
	private static SplitFastqWriter getWriterForTools(String prefix, BarcodeDictionary dictionary, boolean dgzip,
		boolean multi, boolean single, boolean split) {
		ReadToolsFastqWriterFactory factory = new ReadToolsFastqWriterFactory();
		factory.setGzipOutput(!dgzip);
		factory.setUseAsyncIo(multi);
		if (split) {
			return factory.newSplitByBarcodeWriter(prefix, dictionary, !single);
		} else {
			return factory.newSplitAssingUnknownBarcodeWriter(prefix, !single);
		}
	}

	/**
	 * Run based on the reader the single or pair-end mode
	 *
	 * @param reader     the reader
	 * @param writer     the writer
	 * @param methods    the barcode methods instance
	 * @param mismatches the maximum number of mismatches
	 *
	 * @throws IOException if there are some problems with the files
	 */
	private void run(FastqReaderInterface reader, SplitFastqWriter writer, BarcodeMethods methods, int mismatches)
		throws IOException {
		if (reader instanceof FastqReaderSingleInterface) {
			logger.debug("Running single end");
			runSingle((FastqReaderSingleInterface) reader, writer, methods, mismatches);
		} else if (reader instanceof FastqReaderPairedInterface) {
			logger.debug("Running paired end");
			runPaired((FastqReaderPairedInterface) reader, writer, methods, mismatches);
		}
		logger.debug("ERROR: FastqReaderInterface is not an instance of Single or Paired interfaces");
		throw new IllegalArgumentException("Unreachable code");
	}

	/**
	 * Run single-end mode
	 *<commit_msg>fixed a minor bug for unreacheable code<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */
package org.vetmeduni.tools.implemented;

import htsjdk.samtools.fastq.FastqRecord;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.vetmeduni.io.FastqPairedRecord;
import org.vetmeduni.io.readers.FastqReaderInterface;
import org.vetmeduni.io.readers.paired.FastqReaderPairedImpl;
import org.vetmeduni.io.readers.paired.FastqReaderPairedInterface;
import org.vetmeduni.io.readers.paired.FastqReaderPairedSanger;
import org.vetmeduni.io.readers.single.FastqReaderSingleInterface;
import org.vetmeduni.io.readers.single.FastqReaderSingleSanger;
import org.vetmeduni.io.readers.single.FastqReaderWrapper;
import org.vetmeduni.io.writers.ReadToolsFastqWriterFactory;
import org.vetmeduni.io.writers.SplitFastqWriter;
import org.vetmeduni.methods.barcodes.BarcodeDictionary;
import org.vetmeduni.methods.barcodes.BarcodeDictionaryFactory;
import org.vetmeduni.methods.barcodes.BarcodeMethods;
import org.vetmeduni.tools.AbstractTool;
import org.vetmeduni.tools.defaults.CommonOptions;
import org.vetmeduni.utils.fastq.FastqLogger;
import org.vetmeduni.utils.record.FastqRecordUtils;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;

import static org.vetmeduni.tools.ToolNames.ToolException;

/**
 * Tool for split by barcode (in the read name) both BAM and FASTQ files
 *
 * TODO: tests!!
 *
 * @author <NAME>
 */
public class FastqBarcodeDetector extends AbstractTool {

	@Override
	public int run(String[] args) {
		try {
			// PARSING THE COMMAND LINE
			CommandLine cmd = programParser(args);
			File input1 = new File(cmd.getOptionValue("input1"));
			File input2 = (cmd.hasOption("input2")) ? new File(cmd.getOptionValue("input2")) : null;
			String outputPrefix = cmd.getOptionValue("output");
			File barcodes = new File(cmd.getOptionValue("bc"));
			int max;
			try {
				max = (cmd.hasOption("m")) ?
					Integer.parseInt(cmd.getOptionValue("m")) :
					BarcodeMethods.DEFAULT_MISMATCHES;
			} catch (IllegalArgumentException e) {
				throw new ToolException("Maximum mismatches should be an integer");
			}
			int nThreads = CommonOptions.numberOfThreads(logger, cmd);
			boolean multi = nThreads != 1;
			boolean split = cmd.hasOption("x");
			// logging command line
			logCmdLine(args);
			// create the combined dictionary and the barcode method associated
			BarcodeDictionary dictionary = BarcodeDictionaryFactory.createCombinedDictionary(barcodes);
			logger.info("Loaded barcode file for ", dictionary.numberOfUniqueSamples(), " samples with ",
				dictionary.numberOfSamples(), " different barcode sets");
			BarcodeMethods methods = new BarcodeMethods(dictionary);
			// create the reader and the writer
			FastqReaderInterface reader = getReaderForTool(input1, input2, CommonOptions.isMaintained(logger, cmd));
			SplitFastqWriter writer = getWriterForTools(outputPrefix, dictionary,
				cmd.hasOption(CommonOptions.disableZippedOutput.getOpt()), multi, input2 == null, split);
			// run the method
			run(reader, writer, methods, max);
		} catch (ToolException e) {
			// This exceptions comes from the command line parsing
			printUsage(e.getMessage());
			return 1;
		} catch (IOException e) {
			logger.debug(e);
			logger.error(e.getMessage());
			return 1;
		}
		return 0;
	}

	/**
	 * Get the reader for the tool based on the parameters
	 *
	 * @param input1       the input for the first pair
	 * @param input2       the input for the second pair; <code>null</code>
	 * @param isMaintained should be the format maintained or standardize?
	 *
	 * @return the reader for the file(s)
	 */
	private static FastqReaderInterface getReaderForTool(File input1, File input2, boolean isMaintained) {
		FastqReaderInterface toReturn;
		if (input2 == null) {
			toReturn = (isMaintained) ? new FastqReaderWrapper(input1) : new FastqReaderSingleSanger(input1);
		} else {
			toReturn = (isMaintained) ?
				new FastqReaderPairedImpl(input1, input2) :
				new FastqReaderPairedSanger(input1, input2);
		}
		return toReturn;
	}

	/**
	 * Get the writer for the tool based on the parameters
	 *
	 * @param prefix     the output prefix
	 * @param dictionary the barcode dictionary
	 * @param dgzip      disable gzip?
	 * @param multi      multi-thread output?
	 * @param single     single end?
	 * @param split      should we split by barcode?
	 *
	 * @return the writer for splitting
	 */
	private static SplitFastqWriter getWriterForTools(String prefix, BarcodeDictionary dictionary, boolean dgzip,
		boolean multi, boolean single, boolean split) {
		ReadToolsFastqWriterFactory factory = new ReadToolsFastqWriterFactory();
		factory.setGzipOutput(!dgzip);
		factory.setUseAsyncIo(multi);
		if (split) {
			return factory.newSplitByBarcodeWriter(prefix, dictionary, !single);
		} else {
			return factory.newSplitAssingUnknownBarcodeWriter(prefix, !single);
		}
	}

	/**
	 * Run based on the reader the single or pair-end mode
	 *
	 * @param reader     the reader
	 * @param writer     the writer
	 * @param methods    the barcode methods instance
	 * @param mismatches the maximum number of mismatches
	 *
	 * @throws IOException if there are some problems with the files
	 */
	private void run(FastqReaderInterface reader, SplitFastqWriter writer, BarcodeMethods methods, int mismatches)
		throws IOException {
		if (reader instanceof FastqReaderSingleInterface) {
			logger.debug("Running single end");
			runSingle((FastqReaderSingleInterface) reader, writer, methods, mismatches);
			return;
		} else if (reader instanceof FastqReaderPairedInterface) {
			logger.debug("Running paired end");
			runPaired((FastqReaderPairedInterface) reader, writer, methods, mismatches);
			return;
		}
		logger.debug("ERROR: FastqReaderInterface is not an instance of Single or Paired interfaces");
		throw new IllegalArgumentException("Unreachable code");
	}

	/**
	 * Run single-end mode
	 *
{'fraction_non_alphanumeric': 0.08967275818104548, 'fraction_numerical': 0.003824904377390565, 'mean_word_length': 4.5303564433999215, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1794388', 'n_tokens_mistral': 4383, 'n_tokens_neox': 3989, 'n_words': 1270}