#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Module that contains the command line app.

Why does this file exist, and why not put this in __main__?

  You might be tempted to import things from __main__ later, but that will cause
  problems: the code will get executed twice:

  - When you run `python -m nibetaseries` python will execute
    ``__main__.py`` as a script. That means there won't be any
    ``nibetaseries.__main__`` in ``sys.modules``.
  - When you import __main__ it will get executed again (as a module) because
    there's no ``nibetaseries.__main__`` in ``sys.modules``.

  Also see (1) from http://click.pocoo.org/5/setuptools/#setuptools-integration
"""
from __future__ import absolute_import
import os
import argparse
from argparse import RawTextHelpFormatter
from glob import glob
from multiprocessing import cpu_count
from nipype import config as ncfg


def get_parser():
    """Build parser object"""
    from ..__init__ import __version__
    import sys

    verstr = 'nibs v{}'.format(__version__)

    parser = argparse.ArgumentParser(description='NiBetaSeries BIDS arguments',
                                     formatter_class=RawTextHelpFormatter)
    parser.add_argument('bids_dir', help='The directory with the input dataset '
                        'formatted according to the BIDS standard.')
    parser.add_argument('derivatives_pipeline', help='The pipeline that contains '
                        'minimally preprocessed img, brainmask, and confounds.tsv')
    parser.add_argument('output_dir', help='The directory where the output directory '
                        'and files should be stored. If you are running group level analysis '
                        'this folder should be prepopulated with the results of the'
                        'participant level analysis.')
    parser.add_argument('analysis_level', choices=['participant', 'group'],
                        help='Level of the analysis that will be performed '
                        'Multiple participant level analyses can be run independently '
                        '(in parallel) using the same output_dir')
    parser.add_argument('-v', '--version', action='version',
                        version=verstr)

    # Atlas Arguments (Required Options)
    atlas_args = parser.add_argument_group('Required Atlas Arguments')
    atlas_args.add_argument('-a', '--atlas-img', action='store',
                            required=('-l' in sys.argv or '--atlas-lut' in sys.argv),
                            help='input atlas nifti where each voxel within a "region" '
                                 'is labeled with the same integer and there is a unique '
                                 'integer associated with each region of interest.')
    atlas_args.add_argument('-l', '--atlas-lut', action='store',
                            required=('-a' in sys.argv or '--atlas-img' in sys.argv),
                            help='atlas look up table (tsv) formatted with the columns: '
                                  'index, regions which correspond to the regions in the '
                                  'nifti file specified by --atlas-img.')

    # preprocessing options
    proc_opts = parser.add_argument_group('Options for processing')
    proc_opts.add_argument('--estimator', default='lss',
                           choices=['lss', 'lsa'],
                           help='beta series modeling method')
    proc_opts.add_argument('-sm', '--smoothing-kernel', action='store', type=float, default=6.0,
                           help='select a smoothing kernel (mm)')
    proc_opts.add_argument('-hp', '--high-pass', action='store', type=float,
                           default=0.0078125, help='high pass filter (Hz)')
    proc_opts.add_argument('-c', '--confounds', help='The confound column names '
                           'that are to be included in nuisance regression. '
                           'write the confounds you wish to include separated by a space',
                           nargs="+")
    proc_opts.add_argument('--hrf-model', default='glover',
                           choices=['glover', 'spm', 'fir',
                                    'glover + derivative',
                                    'glover + derivative + dispersion',
                                    'spm + derivative',
                                    'spm + derivative + dispersion'],
                           help='convolve your regressors '
                                'with one of the following hemodynamic response functions')
    proc_opts.add_argument('--fir-delays', default=None,
                           nargs='+', type=int, help='FIR delays in volumes',
                           metavar='VOL')
    proc_opts.add_argument('-w', '--work-dir', help='directory where temporary files '
                           'are stored (i.e. non-essential files). '
                           'This directory can be deleted once you are reasonably '
                           'certain nibs finished as expected.')

    # Image Selection options
    image_opts = parser.add_argument_group('Options for selecting images')
    parser.add_argument('--participant-label', nargs="+",
                        help='The label(s) of the participant(s) '
                             'that should be analyzed. The label '
                             'corresponds to sub-<participant_label> from the BIDS spec '
                             '(so it does not include "sub-"). If this parameter is not '
                             'provided all subjects should be analyzed. Multiple '
                             'participants can be specified with a space separated list.')
    image_opts.add_argument('--session-label', action='store',
                            default=None, help='select a session to analyze')
    image_opts.add_argument('-t', '--task-label', action='store',
                            default=None, help='select a specific task to be processed')
    image_opts.add_argument('--run-label', action='store',
                            default=None, help='select a run to analyze')
    image_opts.add_argument('-sp', '--space-label', action='store', default='MNI152NLin2009cAsym',
                            choices=['MNI152NLin2009cAsym'],
                            help='select a bold derivative in a specific space to be used')
    image_opts.add_argument('--description-label', action='store',
                            default=None, help='select a bold file with particular '
                                               '`desc` label to process')
    image_opts.add_argument('--exclude-description-label', action='store_true',
                            default=False, help='exclude this `desc` label from nibetaseries')

    # performance options
    g_perfm = parser.add_argument_group('Options to handle performance')
    g_perfm.add_argument('--nthreads', '-n-cpus', action='store', type=int,
                         help='maximum number of threads across all processes')
    g_perfm.add_argument('--use-plugin', action='store', default=None,
                         help='nipype plugin configuration file')

    # misc options
    misc = parser.add_argument_group('misc options')
    misc.add_argument('--graph', action='store_true', default=False,
                      help='generates a graph png of the workflow')

    return parser


def main():
    from ..workflows.base import init_nibetaseries_participant_wf

    # get commandline options
    opts = get_parser().parse_args()

    # check inputs
    if (opts.hrf_model == 'fir') and (opts.fir_delays is None):
        raise ValueError('If the FIR HRF model is selected, '
                         'FIR delays must be provided.')

    # Set up directories
    # TODO: set up some sort of versioning system
    bids_dir = os.path.abspath(opts.bids_dir)

    derivatives_pipeline_dir = os.path.join(bids_dir, 'derivatives', opts.derivatives_pipeline)

    output_dir = os.path.abspath(opts.output_dir)
    os.makedirs(output_dir, exist_ok=True)

    log_dir = os.path.join(output_dir, 'logs')
    os.makedirs(log_dir, exist_ok=True)

    if opts.work_dir:
        work_dir = os.path.abspath(opts.work_dir)
    else:
        work_dir = os.path.join(os.getcwd(), 'nibetaseries_work')

    os.makedirs(work_dir, exist_ok=True)

    # only for a subset of subjects
    if opts.participant_label:
        subject_list = opts.participant_label
    # for all subjects
    else:
        subject_dirs = glob(os.path.join(bids_dir, "sub-*"))
        subject_list = [subject_dir.split("-")[-1] for subject_dir in subject_dirs]

    # Nipype plugin configuration
    # Load base plugin_settings from file if --use-plugin
    if opts.use_plugin is not None:
        from yaml import load as loadyml
        with open(opts.use_plugin) as f:
            plugin_settings = loadyml(f)
        plugin_settings.setdefault('plugin_args', {})
    else:
        # Defaults
        plugin_settings = {
            'plugin': 'MultiProc',
            'plugin_args': {
                'raise_insufficient': False,
                'maxtasksperchild': 1,
            }
        }

    # Resource management options
    # Note that we're making strong assumptions about valid plugin args
    # This may need to be revisited if people try to use batch plugins
    nthreads = plugin_settings['plugin_args'].get('n_procs')
    # Permit overriding plugin config with specific CLI options
    if nthreads is None or opts.nthreads is not None:
        nthreads = opts.nthreads
        if nthreads is None or nthreads < 1:
            nthreads = cpu_count()
        plugin_settings['plugin_args']['n_procs'] = nthreads

    # Nipype config (logs and execution)
    ncfg.update_config({
        'logging': {'log_directory': log_dir,
                    'log_to_file': True},
        'execution': {'crashdump_dir': log_dir,
                      'crashfile_format': 'txt',
                      'parameterize_dirs': False},
    })

    # running participant level
    if opts.analysis_level == "participant":
        nibetaseries_participant_wf = init_nibetaseries_participant_wf(
            estimator=opts.estimator,
            atlas_img=os.path.abspath(opts.atlas_img),
            atlas_lut=os.path.abspath(opts.atlas_lut),
            bids_dir=bids_dir,
            derivatives_pipeline_dir=derivatives_pipeline_dir,
            exclude_description_label=opts.exclude_description_label,
            fir_delays=opts.fir_delays,
            hrf_model=opts.hrf_model,
            high_pass=opts.high_pass,
            output_dir=output_dir,
            run_label=opts.run_label,
            selected_confounds=opts.confounds,
            session_label=opts.session_label,
            smoothing_kernel=opts.smoothing_kernel,
            space_label=opts.space_label,
            subject_list=subject_list,
            task_label=opts.task_label,
            description_label=opts.description_label,
            work_dir=work_dir,
        )

        if opts.graph:
            nibetaseries_participant_wf.write_graph(graph2use='colored',
                                                    format='svg',
                                                    simple_form=True)
        try:
            nibetaseries_participant_wf.run(**plugin_settings)
        except RuntimeError as e:
            if "Workflow did not execute cleanly" in str(e):
                print("Workflow did not execute cleanly")
            else:
                raise e

    elif opts.analysis_level == "group":
        raise NotImplementedError('group analysis not currently implemented')


def init():
    if __name__ == "__main__":
        raise RuntimeError("NiBetaSeries/cli/run.py should not be run directly;\n"
                           "Please `pip install` NiBetaSeries and use the `nibs` command")


init()

[((35, 13, 36, 74), 'argparse.ArgumentParser', 'argparse.ArgumentParser', (), '', False, 'import argparse\n'), ((146, 15, 146, 45), 'os.path.abspath', 'os.path.abspath', ({(146, 31, 146, 44): 'opts.bids_dir'}, {}), '(opts.bids_dir)', False, 'import os\n'), ((148, 31, 148, 95), 'os.path.join', 'os.path.join', ({(148, 44, 148, 52): 'bids_dir', (148, 54, 148, 67): '"""derivatives"""', (148, 69, 148, 94): 'opts.derivatives_pipeline'}, {}), "(bids_dir, 'derivatives', opts.derivatives_pipeline)", False, 'import os\n'), ((150, 17, 150, 49), 'os.path.abspath', 'os.path.abspath', ({(150, 33, 150, 48): 'opts.output_dir'}, {}), '(opts.output_dir)', False, 'import os\n'), ((151, 4, 151, 42), 'os.makedirs', 'os.makedirs', (), '', False, 'import os\n'), ((153, 14, 153, 46), 'os.path.join', 'os.path.join', ({(153, 27, 153, 37): 'output_dir', (153, 39, 153, 45): '"""logs"""'}, {}), "(output_dir, 'logs')", False, 'import os\n'), ((154, 4, 154, 39), 'os.makedirs', 'os.makedirs', (), '', False, 'import os\n'), ((161, 4, 161, 40), 'os.makedirs', 'os.makedirs', (), '', False, 'import os\n'), ((200, 4, 206, 6), 'nipype.config.update_config', 'ncfg.update_config', ({(200, 23, 206, 5): "{'logging': {'log_directory': log_dir, 'log_to_file': True}, 'execution': {\n    'crashdump_dir': log_dir, 'crashfile_format': 'txt',\n    'parameterize_dirs': False}}"}, {}), "({'logging': {'log_directory': log_dir, 'log_to_file': \n    True}, 'execution': {'crashdump_dir': log_dir, 'crashfile_format':\n    'txt', 'parameterize_dirs': False}})", True, 'from nipype import config as ncfg\n'), ((157, 19, 157, 49), 'os.path.abspath', 'os.path.abspath', ({(157, 35, 157, 48): 'opts.work_dir'}, {}), '(opts.work_dir)', False, 'import os\n'), ((159, 32, 159, 43), 'os.getcwd', 'os.getcwd', ({}, {}), '()', False, 'import os\n'), ((168, 28, 168, 59), 'os.path.join', 'os.path.join', ({(168, 41, 168, 49): 'bids_dir', (168, 51, 168, 58): '"""sub-*"""'}, {}), "(bids_dir, 'sub-*')", False, 'import os\n'), ((176, 30, 176, 40), 'yaml.load', 'loadyml', ({(176, 38, 176, 39): 'f'}, {}), '(f)', True, 'from yaml import load as loadyml\n'), ((196, 23, 196, 34), 'multiprocessing.cpu_count', 'cpu_count', ({}, {}), '()', False, 'from multiprocessing import cpu_count\n'), ((212, 22, 212, 53), 'os.path.abspath', 'os.path.abspath', ({(212, 38, 212, 52): 'opts.atlas_img'}, {}), '(opts.atlas_img)', False, 'import os\n'), ((213, 22, 213, 53), 'os.path.abspath', 'os.path.abspath', ({(213, 38, 213, 52): 'opts.atlas_lut'}, {}), '(opts.atlas_lut)', False, 'import os\n')]