#!/usr/bin/env python
'''
Basepair CLI, wraps the Python API

[2015] - [2018] Basepair INC
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Basepair INC and its suppliers,
if any. The intellectual and technical concepts contained
herein are proprietary to Basepair INC
and its suppliers and may be covered by U.S. and Foreign Patents,
patents in process, and are protected by trade secret or copyright law.
Dissemination of this information or reproduction of this material
is strictly forbidden unless prior written permission is obtained
from Basepair INC.
'''

from __future__ import print_function

import argparse
import json
import sys
import os

import basepair

def main():
    args = read_args()

    if args.config:
        conf = json.load(open(args.config))
    else:
        conf = None

    # will override default and use these as owner for all actions
    # if args.username:
    #     conf['api']['username'] = args.username
    # if args.api_key:
    #     conf['api']['api_key'] = args.api_key
    bp = basepair.connect(conf=conf, scratch=args.scratch, verbose=args.verbose)

    if args.subparser_name == 'createSample':
        create_sample(bp, args)
    elif args.subparser_name == 'createAnalysis':
        create_analysis(bp, args)
    elif args.subparser_name == 'updateSample':
        update_sample(bp, args.sample[0], args.key, args.val)
    elif args.subparser_name == 'updateAnalysis':
        update_analysis(bp, args.analysis, args.key, args.val)
    elif args.subparser_name == 'downloadAnalysis':
        bp.download_analysis(args.uid,args.tags,args.tagkind,args.outdir)
    elif args.subparser_name == 'delete':
        delete(bp, args.type[0], args.uid)
    elif args.subparser_name == 'list':
        bp.print_data(data_type=args.type,uid=args.uid,json=args.json)


def create_sample(bp, args):
    data = {
        'name': args.name,
        'genome': args.genome,
        'datatype': args.datatype,
        'filepaths1': args.file1,
        'filepaths2': args.file2,
        'platform': args.platform,
        'default_workflow': int(args.workflow) if args.workflow else None,
        'projectId': int(args.project) if args.project else None,
    }

    if args.key and args.val:
        for key, val in zip(args.key, args.val):
            data[key] = val

    bp.create_sample(data, upload=True, source='cli')


def create_analysis(bp, args):
    """Create and submit an analysis"""

    params = {'node':{}}

    if args.params is not None:

        for param in args.params:
            node_id, arg, val = param.split(':')

            if node_id not in params['node']:
                params['node'][node_id] = {}

            params['node'][node_id][arg] = val

    else:
        print( 'You specified no parameters, submitting with default ones...', file=sys.stderr)

    bp.create_analysis(
        workflow_id=args.workflow, sample_ids=args.sample,
        control_ids=args.control, params=params)


def update_info(bp, kind, uid, keys, vals):
    data = {}
    if keys and vals:
        for key, val in zip(keys, vals):
            data[key] = val

    if kind == 'sample':
        res = bp.update_sample(uid, data)
    elif kind == 'analysis':
        res = bp.update_analysis(uid, data)
    if res.status_code != 204:
        print('error {}: {}'.format(res.status_code, res.reason), file=sys.stderr)


def update_sample(bp, sample_id, keys, vals):
    update_info(bp, 'sample', sample_id, keys, vals)


def update_analysis(bp, analysis_id, keys, vals):
    update_info(bp, 'analysis', analysis_id, keys, vals)


def delete(bp, data_type, uid):

    if not uid:
        print('Please add one or more uid', file=sys.stderr)
        return

    if data_type not in ['sample','analysis']:
        print(
            ('\'--type {}\' is invalid, choose from: ' +
             'sample or analysis').format(data_type))
        return

    for uid in uid:
        if data_type=='sample':
            bp.delete_sample(uid)
        elif data_type=='analysis':
            bp.delete_analysis(analysis_id)


def add_common_args(parser):

    parser.add_argument('--config', help='API config info')
    parser.add_argument('--scratch', help='Scratch dir for files')
    parser.add_argument('--quiet', action='store_true')
    parser.add_argument('--verbose', action='store_true')

    return parser

def add_common_download_args(parser):

    parser.add_argument(
        '-u',
        '--uid',
        nargs='+',
        default=None,
        help='The unique analysis id(s) to download')
    parser.add_argument(
        '-o',
        '--outdir',
        required=False,
        default='.',
        help='Base directory to save files to (default \'.\').')
    parser.add_argument(
        '--tags',
        nargs='+',
        action='append',
        default=None)
    parser.add_argument(
        '--tagkind',
        default='exact',
        help='''
             Tag filtering strategy. Options:\n
             (1) exact (default): only get files with exactly these tags.
             (2) diff: exclude files with this tag.
             (3) subset: any files with one or more of these tags.
             ''')

    return parser

def read_args():

    parser = argparse.ArgumentParser(
        description='Basepair CLI, API version ' + basepair.__version__,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
Examples:

basepair createSample --name "Sample 5" \
--datatype chip-seq --genome mm9 --file1 sample_5.1.fastq.gz \
--file2 sample_5.2.fastq.gz

basepair createAnalysis -w 14 -s 4111 4112 \
-c 5112 5113 5114

basepair createAnalysis -w 8 -s 371 372 374 -c 368 369 370 \
        --params deseq:group1_name:Sh2 deseq:group1_ids:371,372,374 \
        deseq:group2_name:EV deseq:group2_ids:368,369,370 \
        deseq:slug:Sh2_vs_EV node_13:slug:Sh2_vs_EV node_22:title:Sh2_vs_EV

basepair updateSample -s 1587 --key genome \
--val mm10

basepair updateAnalysis -a 2877 --key name \
--val "Analysis w/ p 0.05"

basepair download -s 2879 --tags bam dedup

basepair deleteSample -s 1789

basepair deleteAnalysis -a 2987

'''
    )

    subparsers = parser.add_subparsers(
        help='Basepair actions.',
        dest='subparser_name')

    #create sample parser

    create_sample_parser = subparsers.add_parser(
        'createSample',
        help='Add a sample to your account on Basepair.'
    )
    create_sample_parser = add_common_args(create_sample_parser)
    create_sample_parser.add_argument('--name')
    create_sample_parser.add_argument('--genome')
    create_sample_parser.add_argument('--platform')
    create_sample_parser.add_argument('--datatype')
    create_sample_parser.add_argument('--file1', nargs='+')
    create_sample_parser.add_argument('--file2', nargs='+')
    create_sample_parser.add_argument('-w', '--workflow', help='Workflow id')
    create_sample_parser.add_argument('-p', '--project', help='Project id')
    create_sample_parser.add_argument('--key', action='append')
    create_sample_parser.add_argument('--val', action='append')

    #create analysis parser

    create_analysis_parser = subparsers.add_parser(
        'createAnalysis',
        help='Create and run an analysis.'
    )
    create_analysis_parser = add_common_args(create_analysis_parser)
    create_analysis_parser.add_argument('-w', '--workflow', help='Workflow id')
    create_analysis_parser.add_argument('-s', '--sample', nargs='+', help='Sample id')
    create_analysis_parser.add_argument('-c', '--control', nargs='+', help='Control id')
    create_analysis_parser.add_argument('--params', nargs='+')

    #update sample parser

    update_sample_parser = subparsers.add_parser(
        'updateSample',
        help='Update information associated with a sample.'
    )
    update_sample_parser = add_common_args(update_sample_parser)
    update_sample_parser.add_argument('-s', '--sample', nargs='+', help='Sample id')
    update_sample_parser.add_argument('--key', action='append')
    update_sample_parser.add_argument('--val', action='append')

    #update analysis parser

    update_analysis_parser = subparsers.add_parser(
        'updateAnalysis',
        help='Update information associated with an analysis.'
    )
    update_analysis_parser = add_common_args(update_analysis_parser)
    update_analysis_parser.add_argument('-a', '--analysis', help='Analysis id')
    update_analysis_parser.add_argument('--key', action='append')
    update_analysis_parser.add_argument('--val', action='append')

    #download parsers

    # download_sample_parser = subparsers.add_parser(
    #     'downloadSample',
    #     help='Download files for one or more sample. ' +
    #          'Can filter by file and analysis tags'
    # )
    # download_sample_parser = add_common_args(download_sample_parser)
    # download_sample_parser = add_common_download_args(download_sample_parser)
    # download_sample_parser.add_argument(
    #     '--analysis_tags',
    #     nargs='+',
    #     default=None,
    #     help='One or more analysis tags')

    # download_file_parser = subparsers.add_parser(
    #     'downloadFile',
    #     help='Download one or more files. ' +
    #          'Can filter by file and analysis tags.'
    # )
    # download_file_parser = add_common_args(download_file_parser)
    # download_file_parser=  add_common_download_args(download_file_parser)
    # download_file_parser.add_argument(
    #     '--analysis_tags',
    #     nargs='+',
    #     default=None,
    #     help='One or more analysis tags')

    download_analysis_parser = subparsers.add_parser(
        'downloadAnalysis',
        help='Download files for one or more analyses. ' +
             'Can filter by file tags.'
    )
    download_analysis_parser = add_common_args(download_analysis_parser)
    download_analysis_parser = add_common_download_args(download_analysis_parser)

    #delete parser

    delete_parser = subparsers.add_parser(
        'delete',
        help='Delete a sample or analysis from your Basepair account.'
    )
    delete_parser = add_common_args(delete_parser)
    delete_parser.add_argument(
        '-t',
        '--type',
        required=True,
        help='Type of data to delete. Options: sample, analysis')
    delete_parser.add_argument(
        '-u',
        '--uid',
        default=None,
        nargs='+',
        help=' The unique sample or analysis id(s)')

    #list parser

    list_parser = subparsers.add_parser(
        'list',
        help='List various types of data on Basepair or on your account'
    )
    list_parser = add_common_args(list_parser)
    list_parser.add_argument(
        '-t',
        '--type',
        required=True,
        help='Type of data to list. ' +
             'Options: {}'.format(', '.join(
                 ['samples','analyses','genomes','workflows','analysis'])))
    list_parser.add_argument(
        '-u',
        '--uid',
        required=False,
        default=None,
        nargs='+',
        type=int,
        help='(Optional) The unique id(s) for item you want.')
    list_parser.add_argument(
        '--json',
        action='store_true',
        help='(Optional) Print the data in JSON format ' +
             '(this shows all data associated with each item).'
    )
    parser.set_defaults(
        params=[],
    )
    args = parser.parse_args()

    if not args.config:
        if not 'BP_CONFIG_FILE' in os.environ:
            print('Please either use the --config option or set ' + \
                  'the environment variable BP_CONFIG_FILE!', file=sys.stderr)
            sys.exit(1)
        else:
            print('Using config file' + os.environ['BP_CONFIG_FILE'], file=sys.stderr)
    else:
        print('Using config file', args.config, file=sys.stderr)


    # if neither set, be verbose
    if not args.verbose and not args.quiet:
        args.verbose = True

    if hasattr(args,'datatype'):
        if args.datatype and args.datatype not in bp.DATATYPES:
            raise argparse.ArgumentTypeError(
                'datatype has to be among:' + ', '.join(bp.DATATYPES))

    if hasattr(args,'key') and hasattr(args,'val'):
        if args.key and not args.val:
            raise argparse.ArgumentTypeError('val required for key')
        elif not args.key and args.val:
            raise argparse.ArgumentTypeError('key required for val')
        elif args.key and args.val and len(args.key) != len(args.val):
            raise argparse.ArgumentTypeError('number of key and val are not equal')

    return args


if __name__ == '__main__':
    main()
