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

"""Generator for Spider templates.

Title: generate_spider_template.py
Author: Benjamin Yvernault
contact: b.yvernault@ucl.ac.uk
Purpose: Generate your Spider.py following the template for spider describe
         in this file.
"""

from __future__ import print_function

import os
import re
from dax import spiders
from datetime import datetime

__author__ = 'Benjamin Yvernault'
__email__ = 'b.yvernault@ucl.ac.uk'
__purpose__ = "Generate your Spider.py following the template for spider\
describe in this file."
__version__ = '1.0.0'
__modifications__ = """21 August 2015 - Original write
16 December 2016 - Update the template to use new methods"""

DEFAULT_TEMPLATE = '''"""Spider_{name}.

Author:         {author}
contact:        {email_addr}
Spider name:    {name}
Spider version: {version}
Creation date:  {now}
Purpose:        {purpose}
"""

# Python packages import
import os
import sys
from dax import spiders, ScanSpider, SessionSpider

__author__ = "{author}"
__email__ = "{email_addr}"
__purpose__ = "{purpose}"
__spider_name__ = "{name}"
__version__ = "{version}"
__modifications__ = """{now} - Original write"""

'''

SCAN_LEVEL_TEMPLATE = DEFAULT_TEMPLATE + '''
def parse_args():
    """Argument parser for the spider input variables.

    by default (set in get_session_argparser):
        -p       : proj_label
        -s       : subj_label
        -e       : sess_label
        -c       : scan_label
        -d       : temp_dir
        --suffix : suffix (suffix for assessor proctype on XNAT)
        --host : host for XNAT (default: XNAT_HOST env variable)
        --user : user on XNAT (default: XNAT_USER env variable)
    your arguments:
        ...

    :return: argument parser object created by parse_args()
    """
    ap = spiders.get_scan_argparser("{name}", __purpose__)

    #
    # ADD YOUR OTHER OPTIONS FOR THIS SPECIFIC SPIDER
    # example to add one options:
    # ap.add_argument("--option", dest="option_id", default=None,
    #                 help="Option description.", required=True)
    #

    return ap.parse_args()


class Spider_{name}(ScanSpider):
    """Scan Spider: Spider_{name}

    :param spider_path: spider file path
    :param jobdir: directory for temporary files
    :param xnat_project: project ID on XNAT
    :param xnat_subject: subject label on XNAT
    :param xnat_session: experiment label on XNAT
    :param xnat_scan: scan label on XNAT

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

    :param xnat_host: host for XNAT if not set in environment variables
    :param xnat_user: user for XNAT if not set in environment variables
    :param xnat_pass: password for XNAT if not set in environment variables
    :param suffix: suffix to the assessor creation
    """

    def __init__(self, spider_path, jobdir,
                 xnat_project, xnat_subject, xnat_session, xnat_scan,
                 xnat_host=None, xnat_user=None, xnat_pass=None,
                 suffix="", subdir=True, skip_finish=False):
        """Entry point for Spider_{name} Class."""
        super(Spider_{name},
              self).__init__(spider_path, jobdir, xnat_project, xnat_subject,
                             xnat_session, xnat_scan, xnat_host, xnat_user,
                             xnat_pass, suffix, subdir, skip_finish)
        # Inputs to download from XNAT specified by:
        #   type: 'scan' or 'assessor' or 'session' or 'subject' or 'project'
        #   label: label on xnat for the object, e.g: '0002' for a scan
        #   resource: label of the resource on xnat, e.g: NIFTI
        #   dir (optional): directory where to download the data
        #   scan (optional): if using an scan assessor and giving just the
        #                    proctype to the label key, generate the
        #                    assessor_label string.
        self.inputs = [
            {{'type':, 'label':, 'resource': }},
            {{'type':, 'label':, 'resource': }},
        ]
        self.pdf_final = os.path.join(
                self.jobdir, 'Report_{name}_%s_%s.pdf' % (self.xnat_session,
                                                          self.xnat_scan))

    def pre_run(self):
        """Method to download data from XNAT."""
        # Download inputs specified by self.inputs
        self.download_inputs()
        # Or write your own code to download data

    def run(self):
        """Method running the process for the spider on the inputs data."""
        # Run command define by self.cmd_args
        matlab_template = """
addpath('$mpath');
function_to_call('$input1', '$input2', $input3);"""
        self.cmd_args = {{
            'exe': 'matlab',
            'template': matlab_template,
            'args': {{'input1': self.data['0002']['NIFTI'],
                     'input2': self.data['0003']['NIFTI'],
                     'input3': 10,
                     'mpath': '/path',
                     }}
        }}
        self.run_cmd_args()
        # Or write your own code to run your process in this method

    def finish(self):
        """Method to copy the results in dax.RESULTS_DIR."""
        results_dict = {{'PDF': self.pdf_final,
                        #
                        # ADD OTHER RESULTS YOU WANT TO SAVE
                        #
                        }}
        self.upload_dict(results_dict)
        self.end()


if __name__ == '__main__':
    args = parse_args()
    # generate spider object:
    spider_obj = Spider_{name}(
                    spider_path=sys.argv[0],
                    jobdir=args.temp_dir,
                    xnat_project=args.proj_label,
                    xnat_subject=args.subj_label,
                    xnat_session=args.sess_label,
                    xnat_scan=args.scan_label,
                    xnat_host=args.host,
                    xnat_user=args.user,
                    xnat_pass=None,
                    suffix=args.suffix,
                    subdir=args.subdir,
                    skip_finish=args.skipfinish)
    # print some information before starting
    spider_obj.print_init(args, "{author}", "{email_addr}")

    # Pre-run method to download data from XNAT
    spider_obj.pre_run()

    # Run method
    spider_obj.run()

    # Finish method to copy results
    if not spider_obj.skipfinish:
        spider_obj.finish()
'''

SESSION_LEVEL_TEMPLATE = DEFAULT_TEMPLATE + '''
def parse_args():
    """Argument parser for the spider input variables.

    by default (set in get_session_argparser):
        -p       : proj_label
        -s       : subj_label
        -e       : sess_label
        -d       : temp_dir
        --suffix : suffix (suffix for assessor proctype on XNAT)
        --host : host for XNAT (default: XNAT_HOST env variable)
        --user : user on XNAT (default: XNAT_USER env variable)
    your arguments:
        ...

    :return: argument parser object created by parse_args()
    """
    ap = spiders.get_session_argparser("{name}", __purpose__)

    #
    # ADD YOUR OTHER OPTIONS FOR THIS SPECIFIC SPIDER
    # example to add one options:
    # ap.add_argument("--option", dest="option_id", default=None,
    #                 help="Option description.", required=True)
    #

    return ap.parse_args()


class Spider_{name}(SessionSpider):
    """Session Spider: Spider_{name}.

    :param spider_path: spider file path
    :param jobdir: directory for temporary files
    :param xnat_project: project ID on XNAT
    :param xnat_subject: subject label on XNAT
    :param xnat_session: experiment label on XNAT

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

    :param xnat_host: host for XNAT if not set in environment variables
    :param xnat_user: user for XNAT if not set in environment variables
    :param xnat_pass: password for XNAT if not set in environment variables
    :param suffix: suffix to the assessor creation
    """

    def __init__(self, spider_path, jobdir,
                 xnat_project, xnat_subject, xnat_session,
                 xnat_host=None, xnat_user=None, xnat_pass=None,
                 suffix="", subdir=True, skip_finish=False):
        """Entry point for Spider_{name} Class."""
        super(Spider_{name},
              self).__init__(spider_path, jobdir,
                             xnat_project, xnat_subject, xnat_session,
                             xnat_host, xnat_user, xnat_pass,
                             suffix, subdir, skip_finish)
        # Inputs to download from XNAT specified by:
        #   type: 'scan' or 'assessor' or 'session' or 'subject' or 'project'
        #   label: label on xnat for the object, e.g: '0002' for a scan
        #   resource: label of the resource on xnat, e.g: NIFTI
        #   dir (optional): directory where to download the data
        #   scan (optional): if using an scan assessor and giving just the
        #                    proctype to the label key, generate the
        #                    assessor_label string.
        self.inputs = [
            {{'type':, 'label':, 'resource': }},
            {{'type':, 'label':, 'resource': }},
        ]
        self.pdf_final = os.path.join(
                self.jobdir, 'Report_{name}_%s.pdf' % self.xnat_session)

    def pre_run(self):
        """Method to download data from XNAT."""
        # Download inputs specified by self.inputs
        self.download_inputs()
        # Or write your own code to download data

    def run(self):
        """Method running the process for the spider on the inputs data."""
        # Run command define by self.cmd_args
        matlab_template = """
addpath('$mpath');
function_to_call('$input1', '$input2', $input3);"""
        self.cmd_args = {{
            'exe': 'matlab',
            'template': matlab_template,
            'args': {{'input1': self.data['0002']['NIFTI'],
                     'input2': self.data['0003']['NIFTI'],
                     'input3': 10,
                     'mpath': '/path',
                     }}
        }}
        self.run_cmd_args()
        # Or write your own code to run your process in this method

    def finish(self):
        """Method to copy the results in dax.RESULTS_DIR."""
        results_dict = {{'PDF': self.pdf_final,
                        #
                        # ADD OTHER RESULTS YOU WANT TO SAVE
                        #
                        }}
        self.upload_dict(results_dict)
        self.end()


if __name__ == '__main__':
    args = parse_args()
    # generate spider object:
    spider_obj = Spider_{name}(
                    spider_path=sys.argv[0],
                    jobdir=args.temp_dir,
                    xnat_project=args.proj_label,
                    xnat_subject=args.subj_label,
                    xnat_session=args.sess_label,
                    xnat_host=args.host,
                    xnat_user=args.user,
                    xnat_pass=None,
                    suffix=args.suffix,
                    subdir=args.subdir,
                    skip_finish=args.skipfinish)
    # print some information before starting
    spider_obj.print_init(args, "{author}", "{email_addr}")

    # Pre-run method to download data from XNAT
    spider_obj.pre_run()

    # Run method
    spider_obj.run()

    # Finish method to copy results
    if not spider_obj.skipfinish:
        spider_obj.finish()
'''


def write_spider(templates):
    """Create the Spider path with the proper template.

    :param templates: template to use (scan or session)
    :return: None
    """
    now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    spider_code = templates.format(author=ARGS.author,
                                   email_addr=ARGS.email,
                                   name=ARGS.name,
                                   version=ARGS.version,
                                   now=now_str,
                                   purpose=ARGS.purpose)
    with open(SPIDER_FPATH, 'w') as f_obj:
        f_obj.writelines(spider_code)


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

    :return: parser object parsed
    """
    from argparse import ArgumentParser
    usage = "Generate your Spider.py following the dax template for spider."
    argp = ArgumentParser(prog='GenerateSpiderTemplate', description=usage)
    argp.add_argument('-n', dest='name', required=True,
                      help='Name for Spider. E.G: fMRIQA.')
    argp.add_argument('-v', dest='version', required=True,
                      help='Spider version (format: X.Y.Z). E.G: 1.0.0')
    argp.add_argument('-a', dest='author', help='Author name.', required=True)
    argp.add_argument('-e', dest='email', required=True,
                      help='Author email address.')
    argp.add_argument('-p', dest='purpose', required=True,
                      help='Spider purpose.')
    argp.add_argument('-c', dest='on_scan', action='store_true',
                      help='Use Scan type Spider.')
    argp.add_argument('-d', dest='directory', default=None,
                      help="Directory where the spider file will be \
generated. Default: current directory.")
    return argp.parse_args()


if __name__ == '__main__':
    print('Deprecated executable. Use dax_generator spider 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 "spider" in ARGS.name.lower():
        spider_search = re.compile(re.escape('spider'), re.IGNORECASE)
        ARGS.name = spider_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]

    # Check version
    if not spiders.is_good_version(ARGS.version):
        err = "wrong format version given to script." + \
              "It must follow the X.Y.Z template with X, Y, and Z integers." +\
              "Look at http://semver.org for more information."
        raise ValueError(err)

    TEMP = """Spider_{name}_v{version}.py"""
    SPIDER_NAME = TEMP.format(name=ARGS.name,
                              version=ARGS.version.replace('.', '_'))
    if ARGS.directory and os.path.exists(ARGS.directory):
        SPIDER_FPATH = os.path.join(ARGS.directory, SPIDER_NAME)
    else:
        SPIDER_FPATH = os.path.join(os.getcwd(), SPIDER_NAME)
    if ARGS.on_scan:
        print("Generating file %s for scan spider %s ..."
              % (SPIDER_FPATH, ARGS.name))
        write_spider(SCAN_LEVEL_TEMPLATE)
    else:
        print("Generating file %s for session spider %s ..."
              % (SPIDER_FPATH, ARGS.name))
        write_spider(SESSION_LEVEL_TEMPLATE)
