#! /usr/bin/python3

# this is the complete command line parser and tool dispatcher for the MiModD
# Python package

terms= """\
    MiModD (Mutation Identification in Model Organism Genomes using Desktop PCs)
    Copyright (C) 2013  Wolfgang Maier

    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 os
import argparse
import importlib
import imp
from collections import OrderedDict
from MiModD.snap import SnapCLParser

# COMMAND LINE PARSERS FOR MIMODD MODULES
parser = argparse.ArgumentParser(description = terms, formatter_class = argparse.RawDescriptionHelpFormatter,
                                 usage = """%(prog)s [tool] tool-specific arguments

Type
%(prog)s -h (or --help)\t\tto see a list of available tools or
%(prog)s [tool] --help\tfor more detailed help on individual tools.
""")

subparsers = parser.add_subparsers()

# +++++++++++ config +++++++++++++
p_config = subparsers.add_parser('config')
v_xor_c = p_config.add_mutually_exclusive_group(required=True)
v_xor_c.add_argument('-v', '--view', action = 'store_true', default = argparse.SUPPRESS, help = 'view current configuration settings')
v_xor_c.add_argument('-c', '--change', action = 'store_true', default = argparse.SUPPRESS, help = 'change configuration settings')
changes = p_config.add_argument_group(title='arguments to update configuration', description='When the -c option is present, these arguments let you change MiModD configuration settings')
changes.add_argument('--tmpfiles', metavar = 'PATH', default = argparse.SUPPRESS, help = 'change the directory that MiModD will use to store temporary files')
changes.add_argument('--snpeff', metavar = 'PATH', default = argparse.SUPPRESS, help = "change the directory of your system's SnpEff installation")
changes.add_argument('-t', '--threads', type = int, default = argparse.SUPPRESS, help = 'change the maximal number of threads that any MiModD tool is allowed to use by default')
changes.add_argument('-m', '--memory', type = int, default = argparse.SUPPRESS, help = 'change the maximal memory in GB that any MiModD tool is allowed to use by default (will not affect the snap tool)')
p_config.set_defaults(func = ['change_config'])

# +++++++++++ enable_galaxy ++++++
p_galaxy = subparsers.add_parser('enable_galaxy')
p_galaxy.add_argument('galaxydir', metavar = 'path_to_galaxy', help = 'path to your local Galaxy installation')
p_galaxy.add_argument('-f', '--force', action = 'store_true', default = argparse.SUPPRESS, help = 'overwrite pre-existing MiModD installation in Galaxy')
p_galaxy.set_defaults(module = 'tool_conf_update', func = ['add_to_galaxy'])
                      
# +++++++++++ header +++++++++++++
p_samheader = subparsers.add_parser('header', usage = '',
                                    help = 'generate a SAM format header from an NGS run description')
# required args
req_group = p_samheader.add_argument_group('required read group description')
req_group.add_argument('--rg_id', nargs = '+', required = True, help = 'one or more unique read group identifiers')
req_group.add_argument('--rg_sm', nargs = '+', required = True, help = 'one sample name per read group identifier')
# optional args
opt_group = p_samheader.add_argument_group('optional read group description')
opt_group.add_argument("--rg_cn", nargs = "*", default = argparse.SUPPRESS, help = 'one sequencing center name per read group')
opt_group.add_argument("--rg_ds", nargs = "*", default = argparse.SUPPRESS, help = 'one description line per read group')
opt_group.add_argument("--rg_dt", nargs = "*", default = argparse.SUPPRESS, help = 'date runs were produced (YYYY-MM-DD); one date per read group')
opt_group.add_argument("--rg_lb", nargs = "*", default = argparse.SUPPRESS, help = 'library identifier for each read group')
opt_group.add_argument("--rg_pl", nargs = "*", default = argparse.SUPPRESS, help = 'sequencing platform/technology used to produce each read group')
opt_group.add_argument("--rg_pi", nargs = "*", default = argparse.SUPPRESS, help = 'predicted median insert size for the reads of each read group')
opt_group.add_argument("--rg_pu", nargs = "*", default = argparse.SUPPRESS, help = 'platform unit, e.g., flowcell barcode or slide identifier, for each read group')
oth_group = p_samheader.add_argument_group('additional information')
oth_group.add_argument("--co", nargs = "*", default = argparse.SUPPRESS, help = 'an arbitrary number of one-line comment strings')
oth_group.add_argument("-o", "--outputfile", metavar = 'OFILE', default = argparse.SUPPRESS, help = 'redirect the output to the specified file (default: stdout)')
p_samheader.set_defaults(module = 'samheader', func = ['run_as_main'])

# ++++++++++ reheader ++++++++++++++++++++
p_reheader = subparsers.add_parser('reheader', usage = '',
                                   help = 'From a BAM file generate a new file with the original header (if any) replaced by that found in a second SAM file')
p_reheader.add_argument('template', help = 'template SAM file providing header information')
p_reheader.add_argument('inputfile', metavar = 'input file', help = 'input BAM file to reheader')
p_reheader.add_argument('-o', '--outputfile', metavar = 'OFILE', help = 'redirect the output to the specified file (default: stdout)')
# optional args
p_reheader.add_argument('-v', '--verbose', action = 'store_true', default = False)
p_reheader.set_defaults(module = 'pysamtools', func = ['reheader'])

# ++++++++++ sort ++++++++++++
p_sort = subparsers.add_parser('sort', usage = '',
                                   help = 'Sort a BAM file by coordinates (or names) of the mapped reads')
p_sort.add_argument('inbam', metavar = 'input file', help = 'the unsorted input file in BAM format')
p_sort.add_argument('-o', '--ofile', dest = 'outfile', default = argparse.SUPPRESS, metavar = 'OFILE', help = 'redirect the output to the specified file (default: stdout)')
p_sort.add_argument('--oformat', metavar = 'bam|sam', choices = ('bam','sam'), default = argparse.SUPPRESS, help = 'specify whether the output should be in sam or bam format (default: bam)')
p_sort.add_argument('-n', '--by_name', dest = 'by_read_name', action = 'store_true', default = argparse. SUPPRESS, help = 'sort by read name')
p_sort.add_argument('-l', dest = 'compression_level', type = int, default = argparse.SUPPRESS, help = 'compression level, from 0 to 9')
p_sort.add_argument('-m', '--memory', type = int, dest = 'maxmem', metavar = 'MEMORY', default = argparse.SUPPRESS, help = 'maximal amount of memory to be used in GB (overrides config setting)')
p_sort.add_argument('-t', '--threads', type = int, default = argparse.SUPPRESS, help = 'the number of threads to use (overrides config setting)')
p_sort.set_defaults(module = 'pysamtools', func = ['sort'])

# ++++++++++ convert ++++++++++
p_convert = subparsers.add_parser('convert', conflict_handler='resolve', usage = '',
                                 help = 'convert between different sequenced reads file formats')
p_convert.add_argument("input1", metavar = 'input file(s)', nargs = '+', help = 'a list of input files (alternating r1 and r2 files for paired-end data')
p_convert.add_argument("--iformat", choices = ('fastq','fastq_pe','gz','gz_pe','sam','bam'), default = argparse.SUPPRESS, help = 'the format of the input file(s) (default: fastq)')
p_convert.add_argument('-o', '--output', metavar = 'OFILE', dest="outputname", default = argparse.SUPPRESS, help = 'redirect the output to the specified file (default: stdout)')
p_convert.add_argument("--oformat", choices = ('sam','bam'), default = argparse.SUPPRESS, help = 'the output format (default: sam)')
p_convert.add_argument('-h', "--header", default = argparse.SUPPRESS, help = 'optional SAM file, the header information of which should be used in the output (will overwrite pre-existing header information from the input file)')
p_convert.set_defaults(module = 'convert', func = ['clparse'])

# +++++++++++ snap ++++++++++++++++++
p_snap = subparsers.add_parser('snap', parents = [SnapCLParser], usage = '', conflict_handler = 'resolve',
                               help = 'align sequence reads using the SNAP aligner')
p_snap.set_defaults(module = 'snap', func = ['snap_call'])

# +++++++++++ snap_batch ++++++++++++
p_snap_batch = subparsers.add_parser('snap_batch', usage = '',
                                     help = 'run several snap jobs and pool the resulting alignments into a multi-sample SAM/BAM file')
xor = p_snap_batch.add_mutually_exclusive_group(required = True)
xor.add_argument('-s', metavar = '"COMMAND"', dest = 'commands', nargs='+', help = 'one or more completely specified command line calls to the snap tool (use "" to enclose individual lines)')
xor.add_argument('-f', metavar = 'FILE', dest = 'ifile', help = 'an input file of completely specified command line calls to the snap tool')

p_snap_batch.set_defaults(module = 'snap', func = ['snap_batch','make_snap_argdictlist'])

# +++++++++++ snap_index ++++++++++++
p_snap_index = subparsers.add_parser('snap_index', conflict_handler='resolve', usage = '',
                                     help = 'index a reference genome for use with the SNAP aligner')
p_snap_index.add_argument('ref_genome', help = 'fasta reference genome to index')
p_snap_index.add_argument('index_out', help = 'path specifying the index directory to be created')
p_snap_index.add_argument('-s', '--seedsize', '--idx_seedsize', type = int, default = argparse.SUPPRESS, help = 'Seed size used in building the index (default: 20)')
p_snap_index.add_argument('-h', '--slack', '--idx_slack', type = float, default = argparse.SUPPRESS, help = 'Hash table slack for indexing (default: 0.3)')
p_snap_index.add_argument("-t", "--threads", type = int, default = argparse.SUPPRESS, help = 'number of threads to use (overrides config setting)')
p_snap_index.set_defaults(module = 'snap', func = ['snap_index'])

# +++++++++++ varcall +++++++++++++
p_varcall = subparsers.add_parser('varcall', usage = '',
                                  help = 'predict SNPs and indels in one or more aligned read samples and calculate the coverage of every base in the reference genome using samtools/bcftools')
p_varcall.add_argument("ref_genome", help = 'the reference genome (in fasta format)')
p_varcall.add_argument("inputfiles", nargs = "+", help = 'one or more BAM input files of aligned reads from one or more samples (will be indexed automatically)')
# optional args
p_varcall.add_argument('-o', '--output_vcf', help = 'redirect the output (variant sites) to the specified file (default: stdout)')
p_varcall.add_argument('-c', '--output_cov', help = 'generate optional coverage output and write it to the specified file')
p_varcall.add_argument('--cov_stats', '--cstats', help = 'generate optional coverage statistics report and write it to the specified file')
p_varcall.add_argument('-d', '--depth', type = int, help = 'max per-BAM depth to avoid excessive memory usage (default: 250)')
p_varcall.add_argument('-s', '--sites', '--is', help = 'optional name of a vcf file specifying a white-list of genomic positions for which the result of the variant calling should always be reported, even if they are not considered variant sites (the resulting data will be written to OUTPUT_SITES)')
p_varcall.add_argument('--output_sites', '--os', help = 'redirect the output of variant calling at white-list sites to the specified file (ignored if -s/--sites is not specified; default: stdout)')
p_varcall.add_argument('-i', '--group_by_id', action = 'store_true', default = False, help = 'optional flag to control handling of multi-sample input; if enabled, reads from different read groups are analyzed as separate samples even if the sample names associated with the read groups are identical; otherwise, the samtools default is used (reads are grouped based on the sample names of their read groups)')
p_varcall.add_argument('-q', '--quiet', action = 'store_true', default = False, help = 'suppress original messages from samtools mpileup and bcftools view')
p_varcall.add_argument('-v', '--verbose', action = 'store_true', default = False, help = 'verbose output (independent of samtools/bcftools)')
p_varcall.set_defaults(module = 'variant_calling', func = ['varcall'])

# ++++++++++ delcall ++++++++
p_delcall = subparsers.add_parser('delcall', usage = '',
                                  help = 'Predict deletions in one or more samples of aligned paired-end reads based on coverage of the reference genome and on insert sizes')
p_delcall.add_argument('ibams', nargs = '+', help= 'one or more BAM input files of aligned reads from one or more samples')
p_delcall.add_argument('icov', help= 'coverage input file (as generated by the varcall tool with the -c option)')
# optional args
p_delcall.add_argument('-o', '--ofile', help = 'redirect the output to the specified file (default: stdout)')
p_delcall.add_argument('--max_cov', type = int, default = argparse.SUPPRESS, help = 'maximal coverage allowed at any site within an uncovered region (default: 0)')
p_delcall.add_argument('--min_size', type = int, default = argparse.SUPPRESS, help = 'minimal size in nts for an uncovered region to be reported (default: 100)')
p_delcall.add_argument('-u', '--include_uncovered', action = 'store_true', default=argparse.SUPPRESS, help = 'include uncovered regions in the output that did not get called as deletions')
p_delcall.add_argument('-i', '--group_by_id', action = 'store_true', default = False, help = 'optional flag to control handling of multi-sample input; if enabled, reads from different read groups will be treated strictly separate. If turned off, read groups with identical sample names are used together for identifying uncovered regions, but are still treated separately for the prediction of deletions.')
p_delcall.add_argument('-v', '--verbose', action = 'store_true', default = False, help = 'verbose output')
p_delcall.set_defaults(module = 'deletion_calling', func = ['delcall'])

# ++++++++++ vcf_filter ++++++++++++++++++
p_vcf_filter = subparsers.add_parser('vcf_filter', usage = '',
                                     formatter_class=argparse.RawDescriptionHelpFormatter,
                                     help = 'extract lines from a vcf variant file based on sample- and field-specific filters',
                                     epilog = """Example filters:
-s sample1 --gt 0/1,1/1
\t\tretain all entries of the vcf input file for which
\t\tsample1's genotype is 0/1 (heterozygous) or
\t\t1/1 (homozygous variant)
-s sample1 sample2 --gt 0/1,1/1 0/0
\t\tretain all entries for which sample1's genotype is 0/1 or 1/1
\t\tAND for which sample2's genotype is 0/0
-s sample1 sample2 --gt 0/1,1/1 ANY --dp 3 3
\t\tretain all entries for which sample1's genotype is 0/1 or 1/1
\t\tAND for which sample1 and sample2 show at least 3-fold coverage
\t\t(sample2's genotype doesn't matter)
"""
                                     )
p_vcf_filter.add_argument('ifile', metavar = 'input file', help = 'a vcf input file')
p_vcf_filter.add_argument('-o', '--ofile', metavar = 'output file', default = argparse.SUPPRESS, help = 'redirect the output to the specified file (default: stdout)')
p_vcf_filter.add_argument('-s', '--samples', nargs = '+', default = argparse.SUPPRESS, help = 'one or more sample names that the sample-specific filters --gt , --dp, and --gq should work on.')
p_vcf_filter.add_argument('--gt', nargs = '+', default = argparse.SUPPRESS, help = 'genotype patterns (one per sample, use ANY to skip the requirement for a given sample) to be included in the output; format: x/x where x = 0 and x = 1 stand for the reference and the variant allele, respectively; multiple allowed genotypes for a sample can be specified as a comma-separated list')
p_vcf_filter.add_argument('--dp', nargs = '+', default = argparse.SUPPRESS, type = int, help = 'minimal coverage (one per sample, use 0 to skip the requirement for a given sample) required to include a site in the output')
p_vcf_filter.add_argument('--gq', nargs = '+', default = argparse.SUPPRESS, type = int, help = 'minimal genotype quality (one per sample, use 0 to skip the requirement for a given sample) required to include a site in the output')
p_vcf_filter.add_argument('-r', '--region', nargs = '*', dest = 'region_filter', default = argparse.SUPPRESS, help = 'keep only variants that fall in one of the given chromsomal regions (specified in the format CHROM:START-STOP or CHROM: for a whole chromosome)')
p_vcf_filter.add_argument('-I', '--no_indels', dest = 'type_filter', action='store_const', const=1, default=0, help = 'skip indels in the output')
p_vcf_filter.add_argument('-i', '--indels_only', dest='type_filter', action='store_const', const=2, default=0, help = 'keep only indels in the output')
p_vcf_filter.add_argument('--v_filter', nargs = '+', default = argparse.SUPPRESS, help = 'vertical sample names filter; if given, only sample columns specified by name will be retained in the output')
p_vcf_filter.set_defaults(module = 'vcf_filter', func = ['filter'])

# +++++++++++ annotate ++++++++++++++++
class LinkFormatter(argparse.Action):
    def __call__ (self, parser, namespace, formatter_file, option_string=None):
        with open(formatter_file) as f:
            formatter_dict = {}
            keys_required = []
            for line in f:
                tmp = line.split(':', 1)
                if len(tmp) == 2:
                    attribute, value = tmp[0].strip(), tmp[1].strip()
                    if attribute == 'species':
                        species = value
                        if keys_required:
                            break
                        if value in formatter_dict:
                            raise KeyError('Species {0} found twice in formatter file.'.format(species))
                        formatter_dict[species] = {}
                        keys_required = ['gene', 'pos']
                    elif attribute in keys_required:
                        formatter_dict[species][attribute] = value
                        keys_required.remove(attribute)
                    elif attribute in formatter_dict[species]:
                        raise KeyError('Attribute {0} found twice in species description {1}.'.format(attribute, species))

            if keys_required:
                raise KeyError('Truncated entry {0} in file {1}: did not specify the required key(s): {2}'.format(species, formatter_file, keys_required))
            setattr(namespace, 'link_formatter', formatter_dict)
        
p_annotate = subparsers.add_parser('annotate', usage = '',
                                 help = 'annotate a vcf variant file with information about the affected genes')
p_annotate.add_argument("inputfile", metavar = 'input file', help = 'a vcf input file')
# optional args
p_annotate.add_argument('--species', '--sp', default = argparse.SUPPRESS, help = 'the name of the species to be assumed when generating annotations')
p_annotate.add_argument('-o', '--ofile', default = argparse.SUPPRESS, help = 'redirect the output to the specified file (default: stdout)')
p_annotate.add_argument('-f', '--oformat', choices = ('html', 'text'), default = argparse.SUPPRESS, help = 'the format of the output file (default: html)')
p_annotate.add_argument('-l', '--link', metavar = 'link formatter file', action = LinkFormatter, default = argparse.SUPPRESS, help = 'file to read hyperlink formatting instructions from')
p_annotate.add_argument('--grouping', choices = ('by_sample', 'by_genes'), default = argparse.SUPPRESS, help = 'group variants "by_sample" or "by_genes" instead of keeping the order defined in the input file')
p_annotate.add_argument('-q', '--quiet', action = 'store_true', default = False, help = 'suppress original messages from SnpEff')
p_annotate.add_argument('-v', '--verbose', action = 'store_true', default = False, help = 'verbose output (independent of SnpEff)')
# snpeff group of arguments
snpeff_group = p_annotate.add_argument_group('optional SnpEff-specific arguments')
snpeff_group.add_argument('-g', '--genome', default = argparse.SUPPRESS, help = 'the name of an installed SnpEff genome annotation file (the snpeff_genomes tool can be used to get a list of all such names')
snpeff_group.add_argument('-s', '--snpeff_out', default = argparse.SUPPRESS, help = 'redirect the original vcf output of SnpEff to the specified file and keep it (default: do not generate SnpEff vcf file)')
snpeff_group.add_argument('--stats', metavar = 'SUMMARY FILE', default = argparse.SUPPRESS, help = 'generate a results summary file of the specified name')
snpeff_group.add_argument('-m', '--memory', type=int, default=argparse.SUPPRESS, help = 'maximal memory to use in GB (overrides config setting)')
snpeff_group.add_argument('-t', '--threads', action="store_true", default=argparse.SUPPRESS, help = 'number of threads to use (values > 1 are incompatible with the --stats option, default: 1)')
snpeff_group.add_argument('--chr', action="store_true", default=argparse.SUPPRESS, help = 'prepend "chr" to chromosome names, e.g., "chr7" instead of "7"')
snpeff_group.add_argument('--minC', metavar = 'MIN_COV', type=int, default=argparse.SUPPRESS, help = 'do not include variants with a coverage lower than MIN_COV (default: no filtering)')
snpeff_group.add_argument('--minQ', metavar = 'MIN_QUAL', type=int, default=argparse.SUPPRESS, help = 'do not include variants with a quality lower than MIN_QUAL (default: no filtering)')
snpeff_group.add_argument('--no_downstream', action="store_true", default=argparse.SUPPRESS, help = 'do not include downstream effects in the output')
snpeff_group.add_argument('--no_upstream', action="store_true", default=argparse.SUPPRESS, help = 'do not include upstream effects in the output')
snpeff_group.add_argument('--no_intron', action="store_true", default=argparse.SUPPRESS, help = 'do not include intron effects in the output')
snpeff_group.add_argument('--no_intergenic', action="store_true", default=argparse.SUPPRESS, help = 'do not include intergenic effects in the output')
snpeff_group.add_argument('--no_utr', action="store_true", default=argparse.SUPPRESS, help = 'do not include UTR effects in the output')
snpeff_group.add_argument('--ud', type=int, default=argparse.SUPPRESS, help = 'specify the upstream/downstream interval length, i.e., variants more than INTERVAL nts from the next annotated gene are considered to be intergenic')
p_annotate.set_defaults(module = 'variant_annotation', func = ['annotate'])

# ++++++++++++ snpeff_genomes ++++++++++++
p_snpeff_genomes = subparsers.add_parser('snpeff_genomes', usage = '',
                                         help = 'list installed SnpEff genomes')
p_snpeff_genomes.add_argument('-c', '--config', default = argparse.SUPPRESS,
                        help = 'path to SnpEff config file. Only needed if not at default location specified in config.py')
p_snpeff_genomes.add_argument('-o', '--output', metavar = 'OFILE', default = argparse.SUPPRESS,
                        help = 'redirect the output to the specified file (default: stdout)')
p_snpeff_genomes.set_defaults(module = 'variant_annotation', func = ['get_installed_snpeff_genomes'])

# ++++++++++++ cm_seqdict +++++++++++++++
p_cm_seqdict = subparsers.add_parser('cm_seqdict', usage = '',
                                         help = 'generate a Cloudmap sequence dictionary from a vcf file')
p_cm_seqdict.add_argument('ifile', metavar = 'vcf input file')
p_cm_seqdict.add_argument('-o', '--ofile', default = argparse.SUPPRESS,
                          help = 'redirect the output to the specified file (default: stdout)')
p_cm_seqdict.set_defaults(module = 'cloudmap_seqdict', func = ['cloudmap_seqdict_from_vcf'])

# ++++++++++ info +++++++++++
p_info = subparsers.add_parser('info', usage = '',
                               help = 'retrieve information about the samples encoded in a file for various supported formats')
p_info.add_argument('ifile', metavar = 'input file', help = 'input file (supported formats: sam, bam, vcf, cov)')
p_info.add_argument('-o', '--ofile', help = 'redirect the output to the specified file (default: stdout)')
p_info.add_argument('-v', '--verbose', help = 'verbose output', action = 'store_true', default = argparse.SUPPRESS)
p_info.set_defaults(module = 'fileinfo', func = ['print_sampleinfo'])


# PRE-PARSING & DELEGATING
args = parser.parse_args()

if 'module' in args:
    module = importlib.import_module('MiModD.'+args.module)

    funcs = [getattr(module, f) for f in args.func]
    del args.func, args.module
    args = vars(args)

    result = funcs[-1](**args)

    for f in funcs[-2::-1]:
        result = f(result)
elif args.func[0] == 'change_config':
    from MiModD.config import tmpfiles_path as TMPFILES, multithreading_level as THREADS, max_memory as MEMORY, snpEff_path as SNPEFF
    args = vars(args)
    pkg_path = imp.find_module('MiModD')[1]
    if 'view' in args:
        if len(args) > 2:
            print ('no other arguments are allowed with the --view option')
        else:
            settings = OrderedDict((('tmpfiles_path', TMPFILES),
                                   ('multithreading_level', THREADS),
                                   ('max_memory', MEMORY),
                                   ('snpEff_path', SNPEFF)))

            print ()
            print ('CURRENT MIMODD SETTINGS')
            print ('-----------------------')
            print ('PARAMETER : VALUE')
            print ('.......................')

            for param, value in settings.items():
                print ('{0} : {1}'.format(param.upper(), value))
    elif 'change' in args:
        if len(args) == 2:
            print ('additional arguments are required to specify the configuration parameters to be changed')
        else:
            settings = OrderedDict((('tmpfiles_path', os.path.abspath(os.path.expanduser(args.get('tmpfiles'))) if args.get('tmpfiles') else TMPFILES),
                                   ('multithreading_level', args.get('threads', THREADS)),
                                   ('max_memory', args.get('memory', MEMORY)),
                                   ('snpEff_path', os.path.abspath(os.path.expanduser(args.get('snpeff', SNPEFF))) if args.get('snpeff') else SNPEFF)))

            with open(os.path.join(pkg_path, 'config.py'), 'w') as config:
                print ()
                print ('NEW MIMODD SETTINGS')
                print ('-------------------')
                print ('PARAMETER : VALUE')
                print ('...................')

                for param , value in settings.items():
                    config.write('{0} = {1}\n'.format(param, "r'{0}'".format(value) if isinstance(value, str) else value))
                    print ('{0} : {1}'.format(param.upper(), value))
