#!/usr/bin/env python3

# Copyright (C) 2017 Tobias Jakobi
#
# 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.
#
# 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 General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import argparse
import os.path
import sys

# global settings
version = "2.0.1"
program_name = "circtools"


# samtools/git like parsing from http://chase-seibert.github.io/blog/2014/03/21/python-multilevel-argparse.html


def main():
    CircTools()


def product_range(p):
    try:
        a, b = map(int, p.split(','))
        return a, b
    except:
        raise argparse.ArgumentTypeError("Product must be a range x->y as in x,y")


class CircTools(object):

    def __init__(self):
        parser = argparse.ArgumentParser(
            description="circtools: a modular, python-based framework for circRNA-related tools that unifies "
                        "several functions in single command line driven software.",
            usage="""circtools [-V] <command> [<args>]

            Available commands:

               detect:       circular RNA detection
               quickcheck:   circular RNA sequencing library quick checks
               circtest:     circular RNA statistical testing
               primex:       circular RNA primer design tool
               nanopore:     circular RNA detection from Oxford Nanopore data
               padlock:      circular RNA padlock probe design tool
               sirna:        circular RNA siRNA design tool
               reconstruct:  circular RNA reconstruction
               enrich:       circular RNA RBP enrichment scan
               exon:         circular RNA alternative exon analysis
               conservation: circular RNA conservation analysis
            """)
        parser.add_argument("command", help="Command to run")

        parser.add_argument("-V",
                            "--version",
                            action="version",
                            version=version
                            )

        # parse_args defaults to [1:] for args, but you need to
        # exclude the rest of the args too, or validation will fail
        args = parser.parse_args(sys.argv[1:2])
        if not hasattr(self, args.command):
            print("The supplied command is unknown")
            parser.print_help()
            exit(1)
        # use dispatch pattern to invoke method with same name
        getattr(self, args.command)()

    @staticmethod
    def enrich():
        # build the argument list
        parser = argparse.ArgumentParser(
            description="circular RNA RBP enrichment tools")

        # REQUIRED ARGUMENTS
        group = parser.add_argument_group("Required options")

        group.add_argument("-c",
                           "--circ-file",
                           dest="circ_rna_input",
                           help="Path to the CircCoordinates file generated by circtools detect",
                           required=True
                           )

        group.add_argument("-b",
                           "--bed-input",
                           dest="bed_input",
                           help="One or more BED files containing features to overlap",
                           required=True
                           )

        group.add_argument("-a",
                           "--annotation",
                           dest="annotation",
                           help="Genome reference annotation file used to not shuffle into intragenic regions",
                           required=True
                           )

        group.add_argument("-g",
                           "--genome",
                           dest="genome_file",
                           help="Genome file for use with bedtools shuffle. See bedtools man page for details.",
                           required=True
                           )

        # OPTIONAL ARGUMENTS
        group = parser.add_argument_group("Additional options")

        group.add_argument("-o",
                           "--output",
                           dest="output_directory",
                           default="./",
                           help="The output folder for files created by " + program_name + " [default: .]",
                           )

        group.add_argument("-i",
                           "--iterations",
                           dest="num_iterations",
                           help="Number of iterations for CLIP shuffling [default: 1000]",
                           type=int,
                           default=1000
                           )

        group.add_argument("-p",
                           "--processes",
                           dest="num_processes",
                           help="Number of threads to distribute the work to",
                           type=int,
                           default=1
                           )

        group.add_argument("-t",
                           "--temp",
                           dest="tmp_directory",
                           help="Temporary directory used by pybedtools",
                           default="/tmp/"
                           )

        group.add_argument("-T",
                           "--threshold",
                           dest="threshold",
                           help="p-value cutoff",
                           type=int,
                           default=2
                           )

        group.add_argument("-P",
                           "--pval",
                           dest="pval",
                           help="p-value cutoff",
                           type=float,
                           default=0.05
                           )

        group.add_argument("-W",
                           "--white-list",
                           dest="whitelist",
                           help="Path to a BED file containing coordinates of exons that should be exclusively taken "
                                "into account when generating the enrichment. These may be exons produced by the exon"
                                "module that show enrichment in the RNase R treated sample.",
                           default=""
                           )

        group.add_argument("-F",
                           "--output-filename",
                           dest="output_filename",
                           help="Defines the output file prefix [default: output]",
                           default="output"
                           )

        group.add_argument("-I",
                           "--include-features",
                           dest="include_features",
                           help="Defines the the features which should be used for shuffling. "
                                "May be specified multiple times. [default: gene - shuffle over the the whole gene region]",
                           # May be used as list: e.g. -I exon -I UTR
                           action='append'
                           )

        group.add_argument("-k",
                           "--keep-temp",
                           dest="keep_temp",
                           help="Keep temporary files created by circtools/bedtools [default: no]",
                           type=bool,
                           default=False
                           )

        args = parser.parse_args(sys.argv[2:])

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        # start the enrichment module
        import enrichment.enrichment_check
        enrich = enrichment.enrichment_check.EnrichmentModule(args, program_name, version)
        enrich.run_module()

    @staticmethod
    def primex():
        parser = argparse.ArgumentParser(
            description="circular RNA primer design")
        # NOT prefixing the argument with -- means it"s not optional

        group = parser.add_argument_group("Input")

        group.add_argument("-d",
                           "--detect-dir",
                           dest="detect_dir",
                           help="CircCoordinates file from circtools detect module",
                           required=True
                           )

        group.add_argument("-g",
                           "--gtf-file",
                           dest="gtf_file",
                           help="GTF file of genome annotation e.g. ENSEMBL",
                           required=True
                           )

        group.add_argument("-f",
                           "--fasta",
                           dest="fasta_file",
                           help="FASTA file with genome sequence (must match annotation)",
                           required=True
                           )

        group.add_argument("-O",
                           "--organism",
                           dest="organism",
                           help="Organism of the study (used for primer BLASTing), "
                                "rn = Rattus norvegicus, mm = Mus musculus, hs = Homo sapiens, ss = Sus scrofa",
                           choices=("mm", "rn", "hs", "ss")
                           )

        group.add_argument("-s",
                           "--sequence",
                           dest="sequence_file",
                           help="FASTA file containing the circRNA sequence (exons and introns)"
                           )

        group = parser.add_argument_group("Output options")

        group.add_argument("-o",
                           "--output",
                           dest="output_dir",
                           help="Output directory (must exist)",
                           default="./"
                           )

        group.add_argument("-T",
                           "--title",
                           dest="experiment_title",
                           help="Title of the experiment for HTML output and file name",
                           default="circtools_primer_design"
                           )

        group = parser.add_argument_group("Additional options")

        group.add_argument("-t",
                           "--temp",
                           dest="global_temp_dir",
                           help="Temporary directory (must exist)",
                           default="/tmp/"
                           )

        group.add_argument("-G",
                           "--genes",
                           dest="gene_list",
                           help="Space-separated list of host gene names. Primers for CircRNAs of those genes will be "
                                "designed."
                                "E.g. -G \"CAMSAP1\" \"RYR2\"",
                           required=False,
                           nargs='+'
                           )

        
        group.add_argument("-p",
                           "--product-size",
                           dest="product_size",
                           help="Space-separated range for the desired PCR product. E.g. -p 80 160 [default]",
                           required=False,
                           default=[80, 160],
                           type=int,
                           nargs='+'
                           )

        group.add_argument("-i",
                           "--id-list",
                           dest="id_list",
                           help="Space-separated list of circRNA IDs."
                                " E.g. -i \"CAMSAP1_9_135850137_135850461_-\" \"CAMSAP1_9_135881633_135883078_-\"",
                           required=False,
                           nargs='+'
                           )

        group.add_argument("-j",
                           "--junction",
                           dest="junction",
                           help="Should the forward [f] or reverse [r] primer be located on the BSJ? [Default: n]",
                           choices=("r", "n", "f"),
                           default="n"
                           )

        group.add_argument("-b",
                           "--no-blast",
                           dest="blast",
                           help="Should primers be BLASTED? Even if selected yes here, not more than 50 primers will"
                                "be sent to BLAST in any case.",
                           default=False,
                           action='store_true'
                           )
        group.add_argument("-n",
                           "--num-pairs",
                           dest="num_pairs",
                           help="Number of primer pairs to be designed",
                           type=int,
                           default=10
                           )

        args = parser.parse_args(sys.argv[2:])

        # start the primer module

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import primex.primex
        primex_instance = primex.primex.Primex(args, program_name, version)
        primex_instance.run_module()

    ## padlock probe module added by Shubhada
    @staticmethod
    def padlock():
        parser = argparse.ArgumentParser(
            description="circular and linear RNA padlock probe design")
        # NOT prefixing the argument with -- means it"s not optional

        group = parser.add_argument_group("Input")

        group.add_argument("-d",
                           "--detect-dir",
                           dest="detect_dir",
                           help="CircCoordinates file from circtools detect module")

        group.add_argument("-g",
                           "--gtf-file",
                           dest="gtf_file",
                           help="GTF file of genome annotation e.g. ENSEMBL",
                           required=True
                           )

        group.add_argument("-f",
                           "--fasta",
                           dest="fasta_file",
                           help="FASTA file with genome sequence (must match annotation)",
                           required=True
                           )

        group.add_argument("-O",
                           "--organism",
                           dest="organism",
                           help="Organism of the study (used for primer BLASTing), "
                                "rn = Rattus norvegicus, mm = Mus musculus, hs = Homo sapiens, ss = Sus scrofa",
                           choices=("mm", "rn", "hs", "ss")
                           )

        group.add_argument("-s",
                           "--sequence",
                           dest="sequence_file",
                           help="FASTA file containing the circRNA sequence (exons and introns)"
                           )

        group = parser.add_argument_group("Output options")

        group.add_argument("-o",
                           "--output",
                           dest="output_dir",
                           help="Output directory (must exist)",
                           default="./"
                           )

        group.add_argument("-T",
                           "--title",
                           dest="experiment_title",
                           help="Title of the experiment for HTML output and file name",
                           default="circtools_padlock_probe_design"
                           )

        group = parser.add_argument_group("Additional options")

        group.add_argument("-t",
                           "--temp",
                           dest="global_temp_dir",
                           help="Temporary directory (must exist)",
                           default="/tmp/"
                           )

        group.add_argument("-G",
                           "--genes",
                           dest="gene_list",
                           help="Space-separated list of host gene names. Primers for CircRNAs of those genes will be "
                                "designed."
                                "E.g. -G \"CAMSAP1\" \"RYR2\"",
                           required=False,
                           nargs='+'
                           )
        
        group.add_argument("-GL",
                           "--genes-file",
                           dest="gene_list_file",
                           help="File containing gene names for which primers need to be designed. Need to provide this if -G option not provided"                          ,
                           required=False,
                           nargs='+'
                           )

        group.add_argument("-i",
                           "--id-list",
                           dest="id_list",
                           help="Space-separated list of circRNA IDs."
                                " E.g. -i \"CAMSAP1_9_135850137_135850461_-\" \"CAMSAP1_9_135881633_135883078_-\"",
                           required=False,
                           nargs='+'
                           )

        group.add_argument("-b",
                           "--no-blast",
                           dest="blast",
                           help="Should primers be BLASTED? Even if selected yes here, not more than 50 primers will"
                                "be sent to BLAST in any case.",
                           default=False,
                           action='store_true'
                           )
        
        group.add_argument("-n",
                           "--num-pairs",
                           dest="num_pairs",
                           help="Number of primer pairs to be designed",
                           type=int,
                           default=10
                           )

        group.add_argument("-r",
                           "--rna-type",
                           dest="rna_type",
                           help="Flag for RNA type for which you want to generated padlock probes\n. 0 for Circular RNAs only, 1 for Linear RNAs only and 2 for Both. DEFAULT 2",
                           type=int,
                           default=2
                           )
        
        group.add_argument("-svg",
                           "--no-svg",
                           dest="svg",
                           help="Should the SVG files for graphical representation be generated?",
                           default=False,
                           action='store_true'
                           )

        args = parser.parse_args(sys.argv[2:])

        # start the padlock module

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import padlock.padlock
        padlock_instance = padlock.padlock.Padlock(args, program_name, version)
        padlock_instance.run_module()

    ## Conservation module added by Shubhada
    @staticmethod
    def conservation():
        parser = argparse.ArgumentParser(
            description="circular RNA conservation analysis")
        # NOT prefixing the argument with -- means it"s not optional

        group = parser.add_argument_group("Input")

        group.add_argument("-d",
                           "--detect-dir",
                           dest="detect_dir",
                           help="CircCoordinates file from circtools detect module")

        group.add_argument("-g",
                           "--gtf-file",
                           dest="gtf_file",
                           help="GTF file of genome annotation e.g. ENSEMBL",
                           required=True
                           )

        group.add_argument("-f",
                           "--fasta",
                           dest="fasta_file",
                           help="FASTA file with genome sequence (must match annotation)",
                           required=True
                           )

        group.add_argument("-C",
                           "--config",
                           dest="config",
                           help="config file containing species with their IDs required for different settings",
                           required=False)

        group.add_argument("-O",
                           "--organism",
                           dest="organism",
                           help="Organism of the study (used for primer BLASTing), "
                                "rn = Rattus norvegicus, mm = Mus musculus, hs = Homo sapiens, ss = Sus scrofa, cl = Canis lupus familiaris",
                           choices=("mm", "rn", "hs", "ss", "cl")
                           )
        
        group.add_argument("-TS",
                           "--target_organism",
                           dest="target_species",
                           help="Target species to be used to calculate conservation score"
                           #choices=("mm", "rn", "hs", "ss", "cl")
                           )

        group.add_argument("-s",
                           "--sequence",
                           dest="sequence_file",
                           help="FASTA file containing the circRNA sequence (exons and introns)"
                           )

        group = parser.add_argument_group("Output options")

        group.add_argument("-o",
                           "--output",
                           dest="output_dir",
                           help="Output directory (must exist)",
                           default="./"
                           )

        group.add_argument("-T",
                           "--title",
                           dest="experiment_title",
                           help="Title of the experiment for HTML output and file name",
                           default="circtools_padlock_probe_design"
                           )

        group = parser.add_argument_group("Additional options")

        group.add_argument("-t",
                           "--temp",
                           dest="global_temp_dir",
                           help="Temporary directory (must exist)",
                           default="/tmp/"
                           )

        group.add_argument("-G",
                           "--genes",
                           dest="gene_list",
                           help="Space-separated list of host gene names. Primers for CircRNAs of those genes will be "
                                "designed."
                                "E.g. -G \"CAMSAP1\" \"RYR2\"",
                           required=False,
                           nargs='+'
                           )
        
        group.add_argument("-GL",
                           "--genes-file",
                           dest="gene_list_file",
                           help="File containing gene names for which primers need to be designed. Need to provide this if -G option not provided"                          ,
                           required=False,
                           nargs='+'
                           )

        group.add_argument("-i",
                           "--id-list",
                           dest="id_list",
                           help="Space-separated list of circRNA IDs."
                                " E.g. -i \"CAMSAP1_9_135850137_135850461_-\" \"CAMSAP1_9_135881633_135883078_-\"",
                           required=False,
                           nargs='+'
                           )
        
        group.add_argument("-hg19",
                           "--hg19",
                           dest="hg19_conversion",
                           help="Are given circular co-ordinates for human from hg19 assembly?"
                           "If the flag is on, these will be converted into hg38.",
                           default=False,
                           action='store_true'
                           )
        
        group.add_argument("-mm10",
                           "--mm10",
                           dest="mm10_conversion",
                           help="Are given circular co-ordinates for mouse from mm10 assembly?"
                           "If the flag is on, these will be converted into mm39.",
                           default=False,
                           action='store_true'
                           )
        
        group.add_argument("-pairwise_flag",
                           "--pairwise_flag",
                           dest="pairwise_flag",
                           help="Should pairwise alignments be performed as well? Additional barplot will be plotted in this case.",
                           default=False,
                           action='store_true'
                           )

        args = parser.parse_args(sys.argv[2:])

        # start the conservation module

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import conservation.conservation
        conservation_instance = conservation.conservation.Conservation(args, program_name, version)
        conservation_instance.run_module()


    @staticmethod
    def sirna():
        parser = argparse.ArgumentParser(
            description="circular RNA siRNA design")
        # NOT prefixing the argument with -- means it"s not optional

        group = parser.add_argument_group("Input")

        group.add_argument("-d",
                           "--detect-file",
                           dest="detect_file",
                           help="CircCoordinates file from circtools detect module",
                           required=True
                           )

        group.add_argument("-g",
                           "--gtf-file",
                           dest="gtf_file",
                           help="GTF file of genome annotation e.g. ENSEMBL",
                           required=True
                           )

        group.add_argument("-f",
                           "--fasta",
                           dest="fasta_file",
                           help="FASTA file with genome sequence (must match annotation)",
                           required=True
                           )

        group.add_argument("-O",
                           "--organism",
                           dest="organism",
                           help="Organism of the study (used for primer BLASTing), "
                                "mm = Mus musculus, hs = Homo sapiens,"
                                " rn = Rattus norvegicus, ss = Sus Scrofa",
                           choices=("mm", "hs", "rn", "ss"),
                           default="hs"
                           )

        group.add_argument("-s",
                           "--sequence",
                           dest="sequence_file",
                           help="FASTA file containing the circRNA sequence (exons and introns)"
                           )

        group.add_argument("-fp",
                           "--find_parameter",
                           dest="findParameter",
                           help="Rule used to find siRNA (0 for Ui-Tei, 1 for Reynolds, 2 "
                                "for multi-length search mode (Ui-Tei))",
                           type=int,
                           default=0
                           )

        group.add_argument("-op",
                           "--overlap_parameter",
                           dest="overlapParameter",
                           help="Minimum number of base pair overlap over the BSJ for all siRNAs",
                           type=int,
                           default=3)

        group.add_argument("-gl",
                           "--G_repeat_length",
                           dest="GLength",
                           help="Maximum number of consecutive Gs in an siRNA sequence that will be tolerated",
                           type=int,
                           default=4)

        group.add_argument("-tl",
                           "--T_repeat_length",
                           dest="TLength",
                           help="Maximum number of consecutive Ts in an siRNA sequence that will be tolerated",
                           type=int,
                           default=4)

        group.add_argument("-al",
                           "--A_repeat_length",
                           dest="ALength",
                           help="Maximum number of consecutive As in an siRNA sequence that will be tolerated",
                           type=int,
                           default=4)

        group.add_argument("-mt",
                           "--Mismatch_tolerance",
                           dest="mismatchTolerance",
                           help="Minimum number of mismatches a siRNA has to have against each blast result",
                           type=int,
                           default=2)

        group.add_argument("-mthr",
                           "--Mismatch_threshhold",
                           dest="mismatchThreshhold",
                           help="Maximum number of blast results complementary to siRNA (containing fewer mismatches "
                                "than mismatch tolerance) that will be tolerated",
                           type=int,
                           default=2)

        group.add_argument("-sm",
                           "--Seed_mismatch",
                           dest="seedMismatch",
                           help="If chosen, this option means that the minimum number of mismatches "
                                "(mismatch tolerance) must be in the seed region of the siRNA",
                           default=False,
                           action='store_true')

        group.add_argument("-hp",
                           "--overhang_parameter",
                           dest="overhangParameter",
                           help="Determines the type of overhang that is added to the siRNA "
                                "(0 for UU overhang, 1 for TT overhang, blank for no overhang)",
                           type=int
                           )

        group = parser.add_argument_group("Output options")

        group.add_argument("-o",
                           "--output",
                           dest="output_dir",
                           help="Output directory (must exist)",
                           default="./"
                           )

        group.add_argument("-T",
                           "--title",
                           dest="experiment_title",
                           help="Title of the experiment for HTML output and file name",
                           default="circtools_sirna_design"
                           )

        group = parser.add_argument_group("Additional options")

        group.add_argument("-t",
                           "--temp",
                           dest="global_temp_dir",
                           help="Temporary directory (must exist)",
                           default="/tmp/"
                           )

        group.add_argument("-tr",
                           "--target",
                           dest="target",
                           help="Which strand the siRNA should target "
                                "(ex: supplying anti-sense creates an anti-sense guide RNA targeting the sense strand)",
                           default="anti-sense"
                           )

        group.add_argument("-G",
                           "--genes",
                           dest="gene_list",
                           help="Space-separated list of host gene names. siRNAs for CircRNAs of those genes will be "
                                "designed."
                                "E.g. -G \"Camsap1\" \"Ryr2\"",
                           required=False,
                           nargs='+'
                           )

        group.add_argument("-i",
                           "--id-list",
                           dest="id_list",
                           help="Space-separated list of circRNA IDs."
                                " E.g. -i \"CAMSAP1_9_135850137_135850461_-\" \"CAMSAP1_9_135881633_135883078_-\"",
                           required=False,
                           nargs='+'
                           )

        group.add_argument("-b",
                           "--no-blast",
                           dest="blast",
                           help="Should siRNAs be BLASTED? (Choosing this option means siRNAs won't be blasted)",
                           default=False,
                           action='store_true'
                           )

        args = parser.parse_args(sys.argv[2:])

        # start the sirna module

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import sirna.sirna
        sirna_instance = sirna.sirna.Sirna(args, program_name, version)
        sirna_instance.run_module()

    @staticmethod
    def detect():
        parser = argparse.ArgumentParser(
            description="circular RNA detection",
            fromfile_prefix_chars="@",
        )

        parser.add_argument("--version", action="version", version=version)

        parser.add_argument("Input", metavar="Input", nargs="+",
                            help="Input of the Chimeric.out.junction file from STAR. Alternatively, a sample sheet "
                                 "specifying where your chimeric.out.junction files are, each sample per line, "
                                 "provide with @ prefix (e.g. @samplesheet)")
        parser.add_argument("-k", "--keep-temp", dest="temp", action="store_true", default=False,
                            help="Temporary files will not be deleted [default: False]")
        parser.add_argument("-T", "--threads", dest="cpu_threads", type=int, default=2,
                            help="Number of CPU threads used for computation [default: 2]")
        parser.add_argument("-O", "--output", dest="out_dir", default="./",
                            help="Output directory [default: .]")
        parser.add_argument("-t", "--temp", dest="tmp_dir", default="_tmp_circtools/",
                            help="Temporary directory [default: _tmp_circtools/]")

        group = parser.add_argument_group("Find circRNA Options", "Options to find circRNAs from STAR output")
        group.add_argument("-D", "--detect", action="store_true", dest="detect", default=False,
                           help="Enable circRNA detection from Chimeric.out.junction files [default: False]")
        group.add_argument("-ss", action="store_true", dest="secondstrand", default=False,
                           help="Must be enabled for stranded libraries, aka 'fr-secondstrand' [default: False]")
        group.add_argument("-N", "--nonstrand", action="store_false", dest="strand", default=True,
                           help="The library is non-stranded [default stranded]")
        group.add_argument("-E", "--endTol", dest="endTol", type=int, default=5, choices=range(0, 10),
                           help="Maximum base pair tolerance of reads extending over junction sites [default: 5]")
        group.add_argument("-m", "--maximum", dest="max", type=int, default=1000000,
                           help="The maximum length of candidate circRNAs (including introns) [default: 1000000]")
        group.add_argument("-n", "--minimum", dest="min", type=int, default=30,
                           help="The minimum length of candidate circRNAs (including introns) [default 30]")
        group.add_argument("-an", "--annotation", dest="annotate",
                           help="Gene annotation file in GTF/GFF3 format, to annotate "
                                "circRNAs by their host gene name/identifier")

        group.add_argument("-Pi", "--PE-independent", action="store_true", dest="pairedendindependent", default=False,
                           help="Has to be specified if the paired end mates have also been mapped separately."
                                "If specified, -mt1 and -mt2 must also be provided [default: False]")
        group.add_argument("-mt1", "--mate1", dest="mate1", nargs="+",
                           help="For paired end data, Chimeric.out.junction files from mate1 independent mapping result")
        group.add_argument("-mt2", "--mate2", dest="mate2", nargs="+",
                           help="For paired end data, Chimeric.out.junction files from mate2 independent mapping result")
        parser.add_argument_group(group)

        group = parser.add_argument_group("Filtering Options", "Options to filter the circRNA candidates")
        group.add_argument("-F", "--filter", action="store_true", dest="filter", default=False,
                           help="If specified, the program will perform a recommended filter step on the detection results")
        group.add_argument("-f", "--filter-only", dest="filteronly", nargs=2,
                           help="If specified, the program will only filter based on two files provided: "
                                "1) a coordinates file [BED6 format] and 2) a count file. E.g.: -f example.bed counts.txt")
        group.add_argument("-M", "--chrM", action="store_true", dest="chrM", default=False,
                           help="If specified, circRNA candidates located on the mitochondrial chromosome will be removed")
        group.add_argument("-R", "--rep_file", dest="rep_file",
                           help="Custom repetitive region file in GTF format to filter out "
                                "circRNA candidates in repetitive regions")
        group.add_argument("-L", "--Ln", dest="length", type=int, default=50,
                           help="Minimum length in base pairs to check for repetitive regions [default 50]")
        group.add_argument("-Nr", nargs=2, type=int, metavar=("countthreshold", "replicatethreshold"), default=[2, 5],
                           help="countthreshold replicatethreshold [default: 2,5]")
        group.add_argument("-fg", "--filterbygene", action="store_true", dest="filterbygene", default=False,
                           help="If specified, filter also by gene annotation (candidates are not allowed to span"
                                " more than one gene) default: False")
        parser.add_argument_group(group)

        group = parser.add_argument_group("Host gene count Options", "Options to count host gene expression")
        group.add_argument("-G", "--gene", action="store_true", dest="gene", default=False,
                           help="If specified, the program will count host gene expression given circRNA coordinates "
                                "[default: False]")
        group.add_argument("-C", "--circ", dest="circ",
                           help="User specified circRNA coordinates, any tab delimited file with first three "
                                "columns as circRNA coordinates: chr\tstart\tend, which circtools will use to count "
                                "host gene expression")
        group.add_argument("-B", "--bam", dest="bam", nargs="+",
                           help="A file specifying the mapped BAM files from which host gene expression is computed; "
                                "must have the same order as input chimeric junction files")
        group.add_argument("-A", "--refseq", dest="refseq",
                           help="Reference sequence FASTA file")
        parser.add_argument_group(group)

        # ciriquant merging
        group = parser.add_argument_group("CIRIquant Options", "Options for merging Circtools and CIRIquant matches")
        group.add_argument("-cq", "--flag_ciriquant", action="store_true",
                           dest="flag_ciriquant", default=False,
                           help="If specified, -cql must also be provided. [default: False]")
        group.add_argument("-cql", "--list_ciriquant", nargs="+", dest="list_ciriquant", help="Two-column tab-separated text file with list of CIRIquant output files. First column is sample ID and second column is full path to .ciri output file")
        group.add_argument("-S", "--cleanup", dest="cleanup", help="String to be removed from each sample name so that the names of Circtools and CIRIquant are the same [Default: \"_STARmapping.*Chimeric.out.junction\"]", default="*Chimeric.out.junction")
        parser.add_argument_group(group)
        

        args = parser.parse_args(sys.argv[2:])

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import detect.detect
        detect_instance = detect.detect.Detect(args, program_name, version)
        detect_instance.run_module()

    @staticmethod
    def circtest():
        parser = argparse.ArgumentParser(
            description="circular RNA statistical testing - Interface to https://github.com/dieterich-lab/CircTest")
        # NOT prefixing the argument with -- means it"s not optional

        ######################################################

        group = parser.add_argument_group("Required")
        group.add_argument("-d",
                           "--detect",
                           dest="detect_dir",
                           help="Path to the circtools detect data directory",
                           required=True
                           )

        group.add_argument("-l",
                           "--condition-list",
                           dest="condition_list",
                           help="Comma-separated list of conditions which should be compared"
                                "E.g. \"RNaseR +\",\"RNaseR -\"",
                           required=True
                           )

        group.add_argument("-c",
                           "--condition-columns",
                           dest="condition_columns",
                           help="Comma-separated list of 1-based column numbers in the circtools detect output"
                                " which should be compared; e.g. 10,11,12,13,14,15",
                           required=True
                           )

        group.add_argument("-g",
                           "--grouping",
                           dest="grouping",
                           help="Comma-separated list describing the relation of the columns specified via -c to the"
                                " sample names specified via -l; e.g. -g 1,2 and -r 3 would assign sample1 to each "
                                "even column and sample 2 to each odd column",
                           required=True
                           )
        ######################################################

        group = parser.add_argument_group("Processing options")

        group.add_argument("-r",
                           "--replicates",
                           dest="num_replicates",
                           help="Number of replicates used for the circRNA experiment [Default: 3]",
                           type=int,
                           default=3
                           )

        group.add_argument("-f",
                           "--max-fdr",
                           dest="max_fdr",
                           help="Cut-off value for the FDR [Default: 0.05]",
                           type=float,
                           default=0.05
                           )

        group.add_argument("-p",
                           "--percentage",
                           dest="percentage",
                           help="The minimum percentage of circRNAs account for the total "
                                "transcripts in at least one group. [Default: 0.01]",
                           type=float,
                           default=0.01
                           )

        group.add_argument("-s",
                           "--filter-sample",
                           dest="filter_sample",
                           help="Number of samples that need to contain the amount of reads "
                                "specified via -C [Default: 3]",
                           type=int,
                           default=3
                           )

        group.add_argument("-C",
                           "--filter-count",
                           dest="filter_count",
                           help="Number of CircRNA reads that each sample specified via -s has to contain "
                                "[Default: 5]",
                           type=int,
                           default=5
                           )

        ######################################################

        group = parser.add_argument_group("Output options")

        group.add_argument("-o",
                           "--output-directory",
                           dest="output_directory",
                           default="./",
                           help="The output directory for files created by " + program_name + " [Default: .]",
                           )

        group.add_argument("-n",
                           "--output-name",
                           dest="output_name",
                           default="circtest",
                           help="The output name for files created by " + program_name + " [Default: circtest]",
                           )

        group.add_argument("-m",
                           "--max-plots",
                           dest="max_plots",
                           help="How many of candidates should be plotted as bar chart? [Default: 50]",
                           type=int,
                           default=50
                           )

        group.add_argument("-a",
                           "--label",
                           dest="label",
                           help="How should the samples be labeled? [Default: Sample]",
                           default="Sample"
                           )

        group.add_argument("-L",
                           "--limit",
                           dest="range",
                           help="How should the samples be labeled? [Default: Sample]",
                           type=float,
                           default=1.05
                           )

        group.add_argument("-O",
                           "--only-negative-direction",
                           dest="only_negative",
                           help="Only print entries with negative direction indicator [Default: False]",
                           default=False
                           )
        group.add_argument("-H",
                           "--add-header",
                           dest="add_header",
                           help="Add header to CSV output [Default: False]",
                           default=False
                           )

        group.add_argument("-M",
                           "--colour",
                           dest="colour",
                           help="Can be set to bw to create grayscale graphs for manuscripts",
                           choices=("colour", "bw"),
                           default="colour"
                           )
        ######################################################

        args = parser.parse_args(sys.argv[2:])

        # start the primer module

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import circtest.circtest
        circtest_instance = circtest.circtest.CircTest(args, program_name, version)
        circtest_instance.run_module()

    @staticmethod
    def quickcheck():
        parser = argparse.ArgumentParser(
            description="circular RNA sequencing library quality assessment")
        # NOT prefixing the argument with -- means it"s not optional

        ######################################################

        group = parser.add_argument_group("Required")
        group.add_argument("-d",
                           "--detect",
                           dest="detect_dir",
                           help="Path to the circtools detect data directory",
                           required=True
                           )

        group.add_argument("-s",
                           "--star",
                           dest="star_dir",
                           help="Path to the base STAR data directory containing sub-folders with per-sample mappings",
                           required=True
                           )

        group.add_argument("-l",
                           "--condition-list",
                           dest="condition_list",
                           help="Comma-separated list of conditions which should be compared"
                                "E.g. \"RNaseR +\",\"RNaseR -\"",
                           required=True
                           )

        group.add_argument("-g",
                           "--grouping",
                           dest="grouping",
                           help="Comma-separated list describing the relation of the columns specified via -c to the"
                                " sample names specified via -l; e.g. -g 1,2 and -r 3 would assign sample1 to each "
                                "even column and sample 2 to each odd column",
                           required=True
                           )
        ######################################################

        group = parser.add_argument_group("Output options")

        group.add_argument("-o",
                           "--output-directory",
                           dest="output_directory",
                           default="./",
                           help="The output directory for files created by " + program_name + " [Default: ./]",
                           )

        group.add_argument("-n",
                           "--output-name",
                           dest="output_name",
                           default="quickcheck",
                           help="The output name for files created by " + program_name + " [Default: quickcheck]",
                           )

        group.add_argument("-c",
                           "--colour",
                           dest="colour",
                           help="Can be set to bw to create grayscale graphs for manuscripts",
                           choices=("colour", "bw"),
                           default="colour"
                           )

        group.add_argument("-C",
                           "--cleanup",
                           dest="cleanup",
                           help="String to be removed from each sample name "
                                "[Default: \"_STARmapping.*Chimeric.out.junction\"]",
                           default="*Chimeric.out.junction"
                           )

        group.add_argument("-S",
                           "--starfolder",
                           dest="starfolder",
                           help="Suffix string of the STAR folders"
                                "[Default: \"\"]",
                           default=""
                           )

        group.add_argument("-L",
                           "--remove-last",
                           dest="remove_suffix_chars",
                           help="Remove last N characters from each column name of the circtools detect input data "
                                "[Default: 0]",
                           type=int,
                           default=0
                           )

        group.add_argument("-F",
                           "--remove-first",
                           dest="remove_prefix_chars",
                           help="Remove first N characters from each column name of the circtools detect input data "
                                "[Default: 0]",
                           type=int,
                           default=0
                           )

        group.add_argument("-R",
                           "--remove-columns",
                           dest="remove_columns",
                           help="Comma-separated list of columns in the circtools detect data files to not includes in the check",
                           default="0"
                           )
        ######################################################

        args = parser.parse_args(sys.argv[2:])

        # start the primer module

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import quickcheck.quickcheck
        quickcheck_instance = quickcheck.quickcheck.QuickCheck(args, program_name, version)
        quickcheck_instance.run_module()

    @staticmethod
    def exon():
        parser = argparse.ArgumentParser(
            description="circular RNA exon usage analysis")
        # NOT prefixing the argument with -- means it"s not optional

        ######################################################

        group = parser.add_argument_group("Required")
        group.add_argument("-d",
                           "--detect",
                           dest="detect_dir",
                           help="Path to the circtools detect data directory",
                           required=True
                           )

        group.add_argument("-l",
                           "--condition-list",
                           dest="condition_list",
                           help="Comma-separated list of conditions which should be compared"
                                "E.g. \"RNaseR +\",\"RNaseR -\"",
                           required=True
                           )

        group.add_argument("-c",
                           "--condition-columns",
                           dest="condition_columns",
                           help="Comma-separated list of 1-based column numbers in the circtools detect output"
                                " which should be compared; e.g. 10,11,12,13,14,15",
                           required=True
                           )

        group.add_argument("-g",
                           "--grouping",
                           dest="grouping",
                           help="Comma-separated list describing the relation of the columns specified via -c to the"
                                " sample names specified via -l; e.g. -g 1,2 and -r 3 would assign sample1 to each "
                                "even column and sample 2 to each odd column",
                           required=True
                           )

        group.add_argument("-r",
                           "--replicates",
                           dest="replicates",
                           help="Comma-separated list describing the relation of the samples specified via -g to the"
                                " sample names specified via -l; e.g. -g 1,2 and -r 3 would assign sample1 to each "
                                "even column and sample 2 to each odd column",
                           required=True
                           )

        group.add_argument("-b",
                           "--ballgown-data",
                           dest="ballgown_data",
                           help="Path to the ballgown data directory",
                           required=True
                           )

        group.add_argument("-G",
                           "--gtf-file",
                           dest="gtf_file",
                           help="Path to the GTF file containing the employed genome annotation",
                           required=True
                           )

        group.add_argument("-C",
                           "--circtest-output",
                           dest="circtest_file",
                           help="Path to the CircTest CSV file containing the CircTest results",
                           required=True
                           )

        ######################################################

        group = parser.add_argument_group("Additional options")

        group.add_argument("-H",
                           "--has-header",
                           dest="has_header",
                           help="Do the CircTest result files have a header? [Default: No]",
                           type=bool,
                           default=False
                           )

        ######################################################

        group = parser.add_argument_group("Output options")

        group.add_argument("-o",
                           "--output-directory",
                           dest="output_directory",
                           default="./",
                           help="The output directory for files created by " + program_name + " [Default: .]",
                           )

        group.add_argument("-n",
                           "--output-prefix",
                           dest="output_prefix",
                           default="exon_analysis_",
                           help="The output name (prefix) for files created by " + program_name +
                                " [Default: exon_analysis]"
                           )

        # group.add_argument("-p",
        #                    "--max-plots",
        #                    dest="max_plots",
        #                    help="How many of candidates should be plotted as bar chart? [Default: 10]",
        #                    type=int,
        #                    default=10
        #                    )

        ######################################################

        args = parser.parse_args(sys.argv[2:])

        # start the primer module

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import exon_usage.exon_usage
        exon_instance = exon_usage.exon_usage.ExonUsage(args, program_name, version)
        exon_instance.run_module()

    @staticmethod
    def reconstruct():
        parser = argparse.ArgumentParser(
            description="circular RNA reconstruction")
        # NOT prefixing the argument with -- means it's not optional

        # input
        parser.add_argument('-C', '--circIDs', dest='circlefile', default='none',
                            help='Tab-separated file chr:start_end(tab)read1,read2,read3.')
        parser.add_argument('-D', '--detect', dest='CircRNACount', default='none',
                            help='If you mapped with STAR and are using step1 you need to provide a list'
                                 ' of circle ids (CircRNACount or CircCoordinates from circtools detect)'
                                 'You must supply either -C or -D')
        parser.add_argument('-J', '--chimericJunctions', dest='chimeric_junction', default='none',
                            help='If you mapped with STAR and are using step1 you need to provide the paired end Chimeric.junction.out file here')
        parser.add_argument('-F', '--mate1', dest='mate1', default='none',
                            help='If you mapped with STAR and are using step1 you need to provide the mate1.Chimeric.junction.out file here (optional if ends were mapped separately)')
        parser.add_argument('-R', '--mate2', dest='mate2', default='none',
                            help='If you mapped with STAR and are using step1 you need to provide the mate2.Chimeric.junction.out file here (optional if ends were mapped separately)')
        parser.add_argument('-B', '--bamfile', dest='bamfile', required=True,
                            help='BAM file containing chimeric reads, linear reads may be in it but are not required.')
        parser.add_argument('-A', '--annotation', dest='bedfile', required=True,
                            help='bed formatted feature file including exons.')
        # output
        parser.add_argument('-O', '--outFolder', dest='out_folder', default='.',
                            help='Output folder. There will be a sub folder for the sample containing a BAM file '
                                 'for each circle.')
        parser.add_argument('-N', '--sampleName', dest='sample', required=True,
                            help='sample name to title every thing.')

        # options
        parser.add_argument('-r', '--thresholdReads', dest='reads', default=5, type=int,
                            help='Circle has to have at least <r> reads to be analysed.')

        # TODO: default: no multi map
        parser.add_argument('-q', '--thresholdMapq', dest='mapq', default=3, type=int,
                            help='MAPQ cutoff, only reads passing this threshold will be written to circle BAM file.')
        # TODO: add 0 based info
        parser.add_argument('-c', '--splitCharacter', dest='split_character', default='_',
                            help='feature name separator.')
        parser.add_argument('-e', '--exonIndex', dest='exon_index', default=3, type=int,
                            help='Field indicating the exon number after splitting feature name by split_character (for the annotation file).')
        parser.add_argument('-p', '--annotationFormat', dest='ref_platform', default='refseq',
                            help='Specifies the annotation platform which was used (refseq or ensembl)')
        parser.add_argument('-s', '--skipSteps', dest='skipped_steps', default='none',
                            help='Comma separated list of steps that should be skipped (e.g. step3,step4,step6)')
        parser.add_argument('-T', '--tmp', dest='tmp_folder', default='/tmp/',
                            help='Folder to store temporary files generated by pybedtools.')

        parser.add_argument('-P', '--cpus', dest='num_cpus', default=4, type=int,
                            help='Number of CPUs used.')

        args = parser.parse_args(sys.argv[2:])

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import reconstruct.reconstruct
        reconstruct_instance = reconstruct.reconstruct.Reconstruct(args, program_name, version)
        reconstruct_instance.run_module()

    @staticmethod
    def nanopore():
        parser = argparse.ArgumentParser(
            description="circular RNA detection in Oxford Nanopore data")
        # NOT prefixing the argument with -- means it's not optional

        ######################################################

        group = parser.add_mutually_exclusive_group(required=True)

        group.add_argument("-r",
                           "--run",
                           dest="run",
                           action="store_true",
                           help="Run the analysis"
                           )

        group.add_argument("-c",
                           "--check",
                           dest="check",
                           action="store_true",
                           help="Check the installation for required software."
                           )

        group.add_argument("-d",
                           "--download",
                           dest="download",
                           action="store_true",
                           help="Download third-party data, such as "
                                "genomes, required for the analysis.",
                           )

        group = parser.add_argument_group("Options")

        group.add_argument('-s',
                            '--sample',
                            help="Provide a sample input .fq.gz file"
                                 " that should be processed.",
                            dest="sample"
        )
        group.add_argument(
                            '-R',
                            '--reference-path',
                            default="./data",
                            help="Provide a path for where the reference "
                                 "data is located. Default is './data'.",
                            dest="reference_path"
        )
        group.add_argument(
                            '-O',
                            '--output',
                            help="Provide a path for where the "
                                 "output data is stored.",
                            dest="output_path"
        )
        group.add_argument("-C",
                           "--config",
                           dest="config",
                           choices=['hg19', 'hg38',
                                    'mm9', 'mm10',
                                    'rn6', 'rn7',
                                    'susScr11',
                                    'canFam6'],
                           help="Required. Select which genome build "
                                "the sample that is from, and specify which "
                                "genome reference files should be used.",
        )
        group.add_argument(
                            '-t',
                            '--threads',
                            default=4,
                            type=int,
                            dest="threads",
                            help="Number of threads for parallel steps. "
                                 "Default: 4."
        )
        group.add_argument(
                            '-D',
                            '--dry-run',
                            action="store_true",
                            help="Perform all of the input checks without "
                                 "starting the detection scripts.",
                            dest="dry_run"

        )
        group.add_argument(
                            '-k',
                            '--keep-temp',
                            action="store_true",
                            dest="keep_temp",
                            default=False,
                            help="Keep all of the temporary files."
        )

        args = parser.parse_args(sys.argv[2:])

        # make sure we can load the sub module
        sys.path.append(os.path.join(os.path.dirname(__file__)))

        import nanopore.nanopore
        nanopore_instance = nanopore.nanopore.Nanopore(args, program_name, version)
        nanopore_instance.run_module()


if __name__ == "__main__":
    main()
