#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Generator for Processor templates.

Title: GenerateProcessorTemplate
Author: Benjamin Yvernault
contact: b.yvernault@ucl.ac.uk
Purpose: Generate your processor.py following the template for processor
         described in this file.
"""

from __future__ import print_function

from builtins import str

import os
import re
from datetime import datetime

__author__ = 'Benjamin Yvernault'
__email__ = 'b.yvernault@ucl.ac.uk'
__purpose__ = "Generate your processor.py following the template for processor\
described in this file."
__version__ = '1.0.0'
__modifications__ = '24 August 2015 - Original write'

DEFAULT_TEMPLATE = '''"""Processor associated to Spider_{name}.

Author:         {author}
contact:        {email_addr}
Processor name: Processor_{name}
Creation date:  {now}
Purpose:        {purpose}
"""

# Python packages import
import os
import logging
from dax import XnatUtils, ScanProcessor, SessionProcessor

__author__ = "{author}"
__email__ = "{email_addr}"
__purpose__ = "{purpose}"
__processor_name__ = "Processor_{name}"
__modifications__ = """{now} - Original write"""

# set-up logger for printing statements
LOGGER = logging.getLogger('dax')

# Default values for arguments:
# EDIT PARAMETERS FOR YOUR SPIDER CASE (SPIDER_PATH, WALLTIME, etc...)
DEFAULT_SPIDER_PATH = os.path.join(SPIDER_PATH, 'Spider_{name}_v1_0_0.py')
DEFAULT_WALLTIME = '01:00:00'
DEFAULT_MEM = 2048
'''

SCAN_LEVEL_TEMPLATE = DEFAULT_TEMPLATE + '''
DEFAULT_SCAN_TYPES = [] # ADD SCAN TYPES

# Format for the spider command line
SPIDER_FORMAT = """python {{spider}} \\
-p {{proj}} \\
-s {{subj}} \\
-e {{sess}} \\
-c {{scan}} \\
-d {{dir}} \\
--suffix "{{suffix_proc}}"
"""


class Processor_{name}(ScanProcessor):
    """Processor class for {name} that runs on a scan.

    :param spider_path: spider path on the system
    :param version: version of the spider
    :param walltime: walltime required by the spider
    :param mem_mb: memory in Mb required by the spider
    :param scan_types: scan types on XNAT that the spider should run on

    #
    # ADD MORE PARAMETERS AS NEEDED HERE AND IN __INIT__
    #

    :param suffix: suffix to the spider
    """

    def __init__(self, spider_path=DEFAULT_SPIDER_PATH, version=None,
                 walltime=DEFAULT_WALLTIME, mem_mb=DEFAULT_MEM,
                 scan_types=DEFAULT_SCAN_TYPES, suffix_proc=''):
        """Entry point for Processor_{name} Class."""
        super(Processor_{name},
              self).__init__(scan_types, walltime, mem_mb, spider_path,
                             version, suffix_proc=suffix_proc)
        #
        # ADD MORE PARAMETERS AS NEEDED HERE LIKE self.param = param
        #

    def has_inputs(self, cscan):
        """Method overridden from base class.

        By definition:
            status = 0  -> NEED_INPUTS,
            status = 1  -> NEED_TO_RUN
            status = -1 -> NO_DATA
            qcstatus needs a value only when -1 or 0.
        You need to set qcstatus to a short string that explain
        why it's no ready to run. e.g: No NIFTI

        :param cscan: object cscan define in dax.XnatUtils
                      (see XnatUtils in dax for information)
        :return: status, qcstatus
        """

        #
        # CODE TO CHECK IF THE PROCESS HAS THE INPUTS NEEDED FROM XNAT
        # CHECK FUNCTION FROM XnatUtils IN dax:
        #  get_good_cscans / has_resource / etc...
        #  get_good_cassr / is_cassessor_good_type / etc...
        #
        # EXAMPLE:
        #  if XnatUtils.is_cscan_unusable(cscan):
        #      return -1, 'Scan unusable'
        #
        #  if XnatUtils.has_resource(cscan, 'NIFTI'):
        #      return 1, None
        #
        #  LOGGER.debug('{name}: No NIFI found.')
        #  return 0, 'No NIFTI'
        #

        return status, qcstatus

    def get_cmds(self, assessor, jobdir):
        """Method to generate the spider command for cluster job.

        :param assessor: pyxnat assessor object
        :param jobdir: jobdir where the job's output will be generated
        :return: command to execute the spider in the job script
        """
        proj_label = assessor.parent().parent().parent().label()
        subj_label = assessor.parent().parent().label()
        sess_label = assessor.parent().label()
        assr_label = assessor.label()
        scan_label = assr_label.split('-x-')[3]

        #
        # ADD CUSTOM PARAMETERS TO THE SPIDER TEMPLATE \
(don't forgot the SPIDER_FORMAT (top))
        #

        cmd = SPIDER_FORMAT.format(spider=self.spider_path,
                                   proj=proj_label,
                                   subj=subj_label,
                                   sess=sess_label,
                                   scan=scan_label,
                                   dir=jobdir,
                                   suffix_proc=self.suffix_proc)

        return [cmd]
'''

SESSION_LEVEL_TEMPLATE = DEFAULT_TEMPLATE + '''
# Format for the spider command line
SPIDER_FORMAT = """python {{spider}} \\
-p {{proj}} \\
-s {{subj}} \\
-e {{sess}} \\
-d {{dir}} \\
--suffix "{{suffix_proc}}"
"""


class Processor_{name}(SessionProcessor):
    """Processor class for {name} that runs on a session.

    :param spider_path: spider path on the system
    :param version: version of the spider
    :param walltime: walltime required by the spider
    :param mem_mb: memory in Mb required by the spider
    #
    # ADD MORE PARAMETERS AS NEEDED HERE AND IN THE __INIT__
    #
    :param suffix: suffix to the spider
    """

    def __init__(self, spider_path=DEFAULT_SPIDER_PATH, version=None,
                 walltime=DEFAULT_WALLTIME, mem_mb=DEFAULT_MEM,
                 suffix_proc=''):
        """Entry point for Processor_{name} Class."""
        super(Processor_{name},
              self).__init__(walltime, mem_mb, spider_path, version,
                             suffix_proc=suffix_proc)
        #
        # ADD MORE PARAMETERS AS NEEDED HERE LIKE self.param = param
        #

    def has_inputs(self, csess):
        """Method overridden from base class.

        By definition:
            status = 0  -> NEED_INPUTS,
            status = 1  -> NEED_TO_RUN
            status = -1 -> NO_DATA
            qcstatus needs a value only when -1 or 0.
        You need to set qcstatus to a short string that explain
        why it's no ready to run. e.g: No NIFTI

        :param csess: object csess define in dax.XnatUtils
                      (see XnatUtils in dax for information)
        :return: status, qcstatus
        """

        #
        # CODE TO CHECK IF THE PROCESS HAS THE INPUTS NEEDED FROM XNAT
        # CHECK FUNCTION FROM XnatUtils IN dax:
        #  get_good_cscans / has_resource / etc...
        #  get_good_cassr / is_cassessor_good_type / etc...
        #

        return status, qcstatus

    def get_cmds(self, assessor, jobdir):
        """Method to generate the spider command for cluster job.

        :param assessor: pyxnat assessor object
        :param jobdir: jobdir where the job's output will be generated
        :return: command to execute the spider in the job script
        """
        proj_label = assessor.parent().parent().parent().label()
        subj_label = assessor.parent().parent().label()
        sess_label = assessor.parent().label()

        #
        # ADD CUSTOM PARAMETERS TO THE SPIDER TEMPLATE \
(don't forgot the SPIDER_FORMAT (top))
        #

        cmd = SPIDER_FORMAT.format(spider=self.spider_path,
                                   proj=proj_label,
                                   subj=subj_label,
                                   sess=sess_label,
                                   dir=jobdir)

        return [cmd]
'''


def write_processor(templates):
    """Write the Processor.py with the proper template.

    :param processor_fpath: path where the processor script will be saved
    :param templates: template to use (scan or session)
    :param args: arguments parser
    """
    processor_code = templates.format(author=ARGS.author,
                                      email_addr=ARGS.email,
                                      name=ARGS.name,
                                      now=str(datetime.now()),
                                      purpose=ARGS.purpose)
    f_obj = open(PROCESSOR_FPATH, "w")
    f_obj.writelines(processor_code)
    f_obj.close()


def parse_args():
    """Method to parse arguments base on ArgumentParser.

    :return: parser object parsed
    """
    from argparse import ArgumentParser
    argp = ArgumentParser(prog='GenerateProcessorTemplate',
                          description=__purpose__)
    argp.add_argument('-n', dest='name', required=True,
                      help='Name for Processor. E.G: fMRIQA.')
    argp.add_argument('-a', dest='author', required=True, help='Author name.')
    argp.add_argument('-e', dest='email', required=True,
                      help='Author email address.')
    argp.add_argument('-p', dest='purpose', required=True,
                      help='Processor purpose.')
    argp.add_argument('--onScan', dest='on_scan', action='store_true',
                      help='Use Scan type Spider.')
    argp.add_argument('-d', dest='directory', default=None,
                      help='Directory where the processor file will be \
generated. Default: current directory.')
    return argp.parse_args()


if __name__ == '__main__':
    print('Deprecated executable. Use dax_generator processor instead.')
    ARGS = parse_args()

    # Get a proper name from the input
    # remove .py if present at the end of the file
    if ARGS.name.endswith('.py'):
        ARGS.name = ARGS.name[:-3]
    # remove settings if present in name
    if "processor" in ARGS.name.lower():
        processor_search = re.compile(re.escape('processor'), re.IGNORECASE)
        ARGS.name = processor_search.sub('', ARGS.name)
    # remove any particular character and change it by an underscore
    ARGS.name = re.sub('[^a-zA-Z0-9]', '_', ARGS.name)
    if ARGS.name[-1] == '_':
        ARGS.name = ARGS.name[:-1]

    PROCESSOR_NAME = """Processor_{name}.py""".format(name=ARGS.name)
    if ARGS.directory and os.path.exists(ARGS.directory):
        PROCESSOR_FPATH = os.path.join(ARGS.directory, PROCESSOR_NAME)
    else:
        PROCESSOR_FPATH = os.path.join(os.getcwd(), PROCESSOR_NAME)
    if ARGS.on_scan:
        print("Generating file %s for scan processor %s ..."
              % (PROCESSOR_FPATH, ARGS.name))
        write_processor(SCAN_LEVEL_TEMPLATE)
    else:
        print("Generating file %s for session processor %s ..."
              % (PROCESSOR_FPATH, ARGS.name))
        write_processor(SESSION_LEVEL_TEMPLATE)
