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

from __future__ import print_function

from builtins import input
from builtins import range

import os
import re
import sys
from dax import spiders


__usage__ = "Start a folder for a new Spider using AutoSpider template and \
create all the files needed."
TEST_FNAME = "test.sh"
INPUTS_FNAME = "inputs.csv"
OUTPUTS_FNAME = "outputs.csv"
GENSPIDER_FNAME = "genspider.sh"
TEST_TEMPLATE = "python {spider} -a 'assessor_label' -d 'tmp_dir' <args> ..."
GENERATE_TEMPLATE = "dax_generator autospider -n {name} -v {version} -i \
inputs.csv -o outputs.csv -c {script} -d ."
INPUTS_TYPE = {1: 'FILE', 2: 'DIR', 3: 'PATH', 4: 'STRING', 5: 'INT'}
OUTPUTS_TYPE = {1: 'FILE', 2: 'DIR'}
INPUTS_INFO = """
-- inputs.csv --
The csv file contains three values per line. The format is:

<VARIABLE_NAME>,<TYPE>,<DESCRIPTION>

Those values are added to the argparser and copy the inputs for the process.

VARIABLE_NAME will be used in your call.py as follow ${{VARIABLE_NAME}}. It can
only contains underscores as specific characters.

TYPE can take the following values:

FILE   : full path to the input file to copied. XNAT path or local path.
DIR    : full path to a directory that will be copied.
PATH   : full path for an executable or a resource. Won't be copied.
STRING : regular string
INT    : regular integer

DESCRIPTION will be set in the argparser for the AutoSpider.

Examples:
t1_file,FILE,T1 nifti file path
t2_id,STRING,Scan ID of the T2
mat_file,PATH,Path to matlab script
"""
OUTPUTS_INFO = """
-- outputs.csv --
The csv file contains three values per line. The format is:

<PATH>,<TYPE>,<RESOURCE>

Those values are used to copy the output to the upload folder.

PATH corresponds to the path of the resource to copy from the AutoSpider's
jobdir. E.G: DATA/*.nii.gz

TYPE can take the following values:

FILE : full path to the input file to copied. XNAT path or local path.
DIR  : full path to a directory that will be copied.

RESOURCE represents the name of the XNAT resource to store the PATH on the XNAT
assessor.It can only contains underscores as specific characters.

Examples:
DATA,DIR,DATA
DATA/alff.pdf,FILE,PDF
NII/*.nii.gz,FILE,NII
"""


def parse_args():
    """
    Method to parse arguments base on ArgumentParser
    :return: parser object parsed
    """
    from argparse import ArgumentParser
    parser = ArgumentParser(prog='InitAutoSpider', description=__usage__)
    parser.add_argument('-n', dest='name', required=True,
                        help='Name for Spider. e.g. fMRIQA')
    parser.add_argument('-v', dest='version', default='1.0.0',
                        help='Spider version, format: X.Y.Z. Default: 1.0.0')
    parser.add_argument('-d', dest='directory', default=os.getcwd(),
                        help="Directory where the folder will be created.")
    parser.add_argument('-e', dest='exe', default='bash',
                        choices=['bash', 'python', 'matlab', 'ruby'],
                        help='Type of programming language for executable.')
    return parser.parse_args()


def get_code_fname(exe_lang):
    """Return the extension for the call file."""
    if exe_lang == 'python':
        return 'call.py'
    elif exe_lang == 'matlab':
        return 'call.m'
    elif exe_lang == 'ruby':
        return 'call.rb'
    elif exe_lang == 'bash':
        return 'call.sh'
    else:
        return 'call.sh'


def prompt_user_yes_no(question):
    """Prompt the user for a question with answer Y/N.

    :return: True if yes, False if no, ask again if any other answer
    """
    value = ''
    while value.lower() not in ['yes', 'no', 'n', 'y']:
        value = input("%s [yes/no] " % question)
    if value.lower() in ['yes', 'y']:
        return True
    else:
        return False


def remove_special_char(name):
    """ Remove the special character from name."""
    return re.sub('[^a-zA-Z0-9]', '_', name)


def ask_number_inout_puts(is_out=False):
    """ Get number of inputs/outputs to set."""
    good_number = False
    while not good_number:
        arg = 'inputs'
        if is_out == 1:
            arg = 'outputs'
        nb_in_out = input("How many %s do you want to set? [INT] " % arg)
        try:
            nb_in_out = int(nb_in_out)
            good_number = nb_in_out > 0
        except ValueError:
            print("Wrong value. Please enter an integer.")
            good_number = False
    return nb_in_out


def get_user_variable(question, index, not_null=False, for_type=False,
                      is_out=False):
    """ Get value from user for question.

    :param question: string to display to ask user
    :param index: input or output index
    :param not_null: check that value is not None
    :param for_type: if checking for input or output type
    :param is_out: True if it's output, False if input
    :return: value
    """
    if not_null:
        value = None
        while value is None:
            value = input("%s - %s " % (index, question))
    elif for_type:
        good_type = False
        while not good_type:
            value = input("%s - %s " % (index, question))
            nb = 6
            io_type = INPUTS_TYPE
            if is_out:
                nb = 3
                io_type = OUTPUTS_TYPE
            try:
                value = int(value)
                good_type = value in range(1, nb)
                value = io_type[value]
            except ValueError:
                if value.upper() in list(io_type.values()):
                    value = value.upper()
                    good_type = True
                else:
                    print("Wrong value.")
                    good_type = False
    else:
        value = input("%s - %s " % (index, question))
    return value


def generate_inputs():
    """Function to help the user generate the inputs.csv."""
    yes = prompt_user_yes_no('Do you want to write the spider inputs?')
    if not yes:
        print('Writing empty format inputs.csv')
        open(os.path.join(spider_path, INPUTS_FNAME), 'a').close()
    else:
        inputs_lines = list()
        print(INPUTS_INFO)
        nb_inputs = ask_number_inout_puts()
        for index in range(1, nb_inputs + 1):
            print('\n -- setting input %s --' % index)
            var_name = get_user_variable('What is the variable name?', index,
                                         not_null=True)
            var_name = remove_special_char(var_name)
            mg = "What type of data? [Select integer: 1.FILE / 2.DIR / 3\
.PATH / 4.STRING / 5.INT]"
            var_type = get_user_variable(mg, index, for_type=True)
            var_desc = get_user_variable("What is the description?", index)
            required = prompt_user_yes_no('Is this input required?')
            template = '{0},{1},{2}'
            if not required:
                template = '{0},{1},{2},F'
            inputs_lines.append(template.format(var_name, var_type, var_desc))

        with open(os.path.join(spider_path, INPUTS_FNAME), 'a') as f_in:
            f_in.writelines('\n'.join(inputs_lines))


def generate_outputs():
    """Function to help the user generate the inputs.csv."""
    yes = prompt_user_yes_no('Do you want to write the spider outputs?')
    if not yes:
        print('Writing empty format outputs.csv')
        open(os.path.join(spider_path, OUTPUTS_FNAME), 'a').close()
    else:
        outputs_lines = list()
        print(OUTPUTS_INFO)
        nb_outputs = ask_number_inout_puts(is_out=True)
        for index in range(1, nb_outputs + 1):
            print('\n -- setting output %s --' % index)
            res_path = get_user_variable(
                'What is the output path in the jobdir?', index, not_null=True)
            mg = "What type of output? [Select integer: 1.FILE / 2.DIR] "
            res_type = get_user_variable(mg, index, for_type=True)
            res_name = get_user_variable('What is the resource name?', index,
                                         not_null=True)
            required = prompt_user_yes_no('Is this output required?')
            template = '{0},{1},{2}'
            if not required:
                template = '{0},{1},{2},F'
            outputs_lines.append(template.format(res_path, res_type, res_name))

        with open(os.path.join(spider_path, OUTPUTS_FNAME), 'a') as f_out:
            f_out.writelines('\n'.join(outputs_lines))


if __name__ == '__main__':
    ARGS = parse_args()
    # TODO: error-checking on the files

    # Get directory - if name of spider at the end remove it.
    directory = os.path.abspath(ARGS.directory)
    if os.path.basename(directory) == ARGS.name:
        directory = os.path.dirname(directory)

    # Check spider name
    if ARGS.name.endswith('.py') or "spider" in ARGS.name.lower() or \
       not re.compile('^\w+$').match(ARGS.name):
        err = "Invalid spider name"
        raise ValueError(err)

    # Check version
    if not spiders.is_good_version(ARGS.version):
        err = "Invalid format for version. \
Must be X.Y.Z. See http://semver.org."
        raise ValueError(err)

    # Check directory:
    if not os.path.isdir(directory):
        err = "Directory does not exist: %s." % directory
        raise ValueError(err)
    else:
        spider_path = os.path.join(directory, ARGS.name, 'v%s' % ARGS.version)
        if os.path.isdir(spider_path):
            print('Warning: AutoSpider Folder already found: %s. exit.'
                  % spider_path)
            sys.exit()
        else:
            os.makedirs(spider_path)

    # Generate file
    code_fname = get_code_fname(ARGS.exe)
    spider_file = 'Spider_{0}_v{1}.py'.format(ARGS.name,
                                              ARGS.version.replace('.', '_'))
    generate_str = GENERATE_TEMPLATE.format(
        name=ARGS.name,
        version=ARGS.version,
        script=code_fname)
    test_str = TEST_TEMPLATE.format(spider=spider_file)

    # Write the files
    with open(os.path.join(spider_path, GENSPIDER_FNAME), 'w') as f:
        f.write(generate_str)
    with open(os.path.join(spider_path, TEST_FNAME), 'w') as f:
        f.write(test_str)
    generate_inputs()
    generate_outputs()
    open(os.path.join(spider_path, code_fname), 'a').close()
    print('Folder %s ready.' % spider_path)
