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

'''
Upload data to XNAT following the csv file information

@author: Benjamin Yvernault, Electrical Engineering, Vanderbilt University
'''

from __future__ import print_function

from builtins import str
from builtins import zip

import csv
from datetime import datetime
import glob
import logging
import os

from dax import XnatUtils
from dax.errors import XnatToolsUserError
import dax.xnat_tools_utils as utils


__copyright__ = 'Copyright 2013 Vanderbilt University. All Rights Reserved'
__exe__ = os.path.basename(__file__)
__author__ = 'byvernault'
__purpose__ = 'Print a detailed report from XNAT projects.'
__logger__ = utils.setup_info_logger(__exe__)
__description__ = """What is the script doing :
   * Upload data to XNAT following the csv file information.
     csv header:
     object_type,project_id,subject_label,session_type,session_label,
     as_label,as_type,as_description,quality,resource,fpath

IMPORTANT: YOU NEED TO CREATE THE PROJECT ON XNAT BEFORE UPLOADING.

Examples:
   * See Session type:
        Xnatupload --printmodality
   * Simple upload:
        Xnatupload -c upload_sheet.csv
   * Upload everything with a session type:
        Xnatupload -c upload_sheet.csv --sess PET
   * Check the upload:
        Xnatupload -c upload_sheet.csv --report
   * Force upload:
        Xnatupload -c upload_sheet.csv --force
   * Upload with delete resource before uploading:
        Xnatupload -c upload_sheet.csv --delete
   * Upload with delete every resources for the object (SCAN/ASSESSOR) before \
uploading:
        Xnatupload -c upload_sheet.csv --deleteAll
"""

SCAN_HEADER = ['object_type', 'project_id', 'subject_label', 'session_type',
               'session_label', 'ID', 'type', 'series_description', 'quality']
ASSESSOR_HEADER = ['object_type', 'project_id', 'subject_label',
                   'session_type', 'session_label', 'label', 'proctype',
                   'procstatus', 'qcstatus']
DEFAULT_LABELS_DICT = {'Project': 0, 'Subject': 1, 'Session': 2, 'Scan': 3,
                       'Scan Type': 4, 'Scan Series Description': 5,
                       'Resource': 6}
SNAPSHOT_O = 'snapshot_original.png'
SNAPSHOT_P = 'snapshot_preview.png'
# No ScanData on XNAT for those modalities, using MR
MR_SCAN_FOR_MODALITY = ['RT']
DISPLAY_TEMP = """
----------------------------------
Report information about uploading :
Date: {date}
================================================================
List of the data found in the csv that need to be upload:
---------------------------------------------------------
{header}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
{objects}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

Keys:
  - Description = Job status for assessor or series description for scan
  - Quality     = Job quality control for assessor or quality for scan
                  (usable/unusable/questionable)

Warnings:
  - If one of the column is empty for Project/Subject/SessType/Session/Label/
    resource, the resource will not get upload.
  - By default, quality is set to questionable for scan and  Needs QA for
    assessor.
  - By default, Description (job status) for an assessor will be set to
    COMPLETE.
  - IMPORTANT: a session label needs to be unique for a project.

P.S : Please check that the REC or NII image type that you upload are
    compressed (.rec/.nii), please compress them in .gz like "file.nii.gz".
"""


def extract_objects_one_subject(project, subject, obj_list):
    """
    Method to extract objects for a specific subject from a list

    :param project: project ID on XNAT
    :param subject: subject label on XNAT
    :param obj_list: objects list to filter
    :return: list containing only the desired objects
    """
    return [x for x in obj_list if (x['subject_label'] == subject and
                                    x['project_id'] == project)]


def read_csv_for_upload(csv_file, session_type, report=False):
    """
    Read the csv describing the data to upload to XNAT

    :param csv_file: csv file containing the information on data to upload
    :param session_type: type of session uploaded
    :param report: reading csv for report (don't generate snapshots)
    :return: list of scans and assessors to upload
    """
    warn_format = '  --> Warning: row %d -- %s'

    # variables
    scans = list()
    assessors = list()
    sresources = dict()
    aresources = dict()

    if not os.path.exists(csv_file):
        err = 'Argument -c/--csvfile - file does not exist: %s'
        raise XnatToolsUserError(__exe__, err % csv_file)
    else:
        msg = 'INFO: Reading CSV -- the script will verify the csv given to \
--csvfile.\n      Any row with a warning will not be uploaded.'
        __logger__.info(msg)
        with open(csv_file, 'rb') as csvfileread:
            csvreader = csv.reader(csvfileread, delimiter=',')
            for ind, row in enumerate(csvreader):
                if len(row) < 11:
                    msg = 'not enough column. Required 11 columns, %d given.'
                    __logger__.info(warn_format % (ind + 1, msg % len(row)))
                else:
                    if row[0] == 'scan':
                        scan = read_row(row, session_type, ind + 1)
                        if scan:
                            scans.append(scan)
                            # Resource:
                            sresources = add_resource(sresources,
                                                      scan['label'],
                                                      row[-2], row[-1])
                    elif row[0] == 'assessor':
                        assessor = read_row(row, session_type, ind + 1,
                                            header=ASSESSOR_HEADER)
                        if assessor:
                            assessors.append(assessor)
                            # Resource:
                            aresources = add_resource(aresources,
                                                      assessor['label'],
                                                      row[-2], row[-1])
                    else:
                        msg = 'does not start with "scan" or "assessor".'
                        __logger__.info(warn_format % (ind + 1, msg))

        __logger__.info('INFO: Reading CSV DONE')
        utils.print_separators()
        # Checking session:
        check_session_id(scans, assessors)

        # Generating SNAPSHOTS for assessor PDF:
        if not report:
            __logger__.info('INFO:Generating snapshots for assessor...')
            aresources = generate_previews(aresources)

        # Combine the resources dictionaries to each lists:
        scans = combine_obj_resource(scans, sresources)
        assessors = combine_obj_resource(assessors, aresources)

    return scans, assessors


def read_row(row, session_type, ind, header=SCAN_HEADER):
    """
    Read the row from csv and return the dictionary or nothing if error

    :param row: row from csv file
    :param session_type: type of session on XNAT
    :param ind: index of row
    :param header: header for the row (scan or assessor)
    :return: dictionary
    """
    is_scan = True if header == SCAN_HEADER else False
    warn_format = 'Warning: row %d -- %s'
    obj_dict = dict()
    is_good_row = False

    if is_scan:
        is_good_row = (row[1] and row[2] and (session_type or row[3]) and
                       row[4] and row[5] and row[-2] and row[-1])
    else:
        is_good_row = (row[1] and row[2] and (session_type or row[3]) and
                       row[4] and row[5] and row[6] and row[-2] and row[-1])

    if not is_good_row:
        msg = 'an element for one or more columns with the index \
2/3/4/5%s/10/11 is empty.'
        if is_scan:
            extra = ''
        else:
            extra = '/6'
        __logger__.info(warn_format % (ind, msg, extra))
    else:
        if not session_type and \
           not row[3] in list(utils.XNAT_MODALITIES.keys()):
            msg = 'wrong session type, not defined on XNAT.'
            __logger__.info(warn_format % (ind, msg))
        else:
            obj_dict = dict(list(zip(header, row[:9])))
            if is_scan:
                label = '-x-'.join([row[1], row[2], row[4], row[5]])
            else:
                label = row[5]

            obj_dict['label'] = label

    return obj_dict


def check_session_id(scans, assessors):
    """
    Check that the session labels given are unique before uploading
     XNAT doesn't allow two sessions to be named with the same label

    :param scans: list of scans
    :param assessors: list of assessors
    :return: None
    """
    sessions_w_subj = dict()

    for _dict in (scans + assessors):
        subject, session = _dict['label'].split('-x-')[1:3]
        if not sessions_w_subj.get(session, None):
            sessions_w_subj[session] = subject
        else:
            if sessions_w_subj.get(session) != subject:
                err = """The csv provided possesses some rows with the same
session label for two different subjects. Session label
needs to be unique. Two subject can not have a session with
the same label. Please fix the csv and call again Xnatupload."""
                raise XnatToolsUserError(__exe__, err)


def add_resource(rs_dict, label, resource_label, fpath):
    """
    Adding resources to the dictionary

    :param rs_dict: dictionary of resource for the objects to upload
    :param label: label of the object to add a resource
    :param resource_label: resource label of the new resource
    :param fpath: path to the resource to add
    :return: list of object with the resources specified by resource
    """
    if label in list(rs_dict.keys()):
        resources_dict = rs_dict[label]
        if resource_label in list(resources_dict.keys()):
            resources_dict[resource_label].append(fpath)
        else:
            resources_dict[resource_label] = [fpath]
        rs_dict[label] = resources_dict
    else:
        rs_dict[label] = {resource_label: [fpath]}
    return rs_dict


def combine_obj_resource(obj_list, r_dict):
    """
    Combine the object list and the resource dictionary

    :param obj_list: list of object to upload
    :param r_dict: dictionary of resource for the objects to upload
    :return: list of object with the resources specified by resource
    """
    # Unique list of dictionaries:
    obj_list = list({v['label']: v for v in obj_list}.values())
    for object_dict in obj_list:
        object_dict['resource'] = r_dict[object_dict['label']]
    return obj_list


def check_pdf_path(pdf_paths_list, label):
    """
    Check that the PDF is a proper .pdf file

    :param pdfpath_list: list of pdf path
    :return: the path to a PDF or None if no PDF found
    """
    _format = ' Warning: generate_previews -- %s -- %s'
    if len(pdf_paths_list) > 1:
        msg = 'more than one PDF file paths given, the first one found will \
be used to create the snapshots.'
        __logger__.info(_format % (label, msg))

    for ppath in pdf_paths_list:
        if os.path.isfile(ppath) and ppath.endswith('.pdf'):
            return ppath
        if os.path.isdir(ppath):
            pdfs_list = glob.glob(os.path.join(ppath, '*.pdf'))
            if pdfs_list:
                return pdfs_list[0]

    return None


def generate_previews(ra_dict):
    """
    Generate Preview for an assessor with a PDF

    :param ra_dict: dictionary of resource attributes for the assessor to
                    upload
    :return: new dictionary containing snapshots original and preview
    """
    _format = ' Warning: generate_previews -- %s -- %s'
    cmd_o = 'gs -q -o %s -sDEVICE=pngalpha -dLastPage=1 %s'
    cmd_p = 'convert %s -resize x200 %s'
    for label, resource_dict in list(ra_dict.items()):
        if 'PDF' in list(resource_dict.keys()):
            pdf_path = check_pdf_path(resource_dict['PDF'], label)
            if not pdf_path:
                msg = 'No proper PDF file found. No snapshots will be created.'
                __logger__.info(_format % (label, msg))
            else:
                # SNAPSHOTS : create it in the /tmp/ folder under the assessor
                # name
                tmp_path = os.path.join('/tmp', '%s_snapshots' % (label))
                if not os.path.exists(tmp_path):
                    os.makedirs(tmp_path)
                # Make the snapshots for the assessors with ghostscript
                snapshot_original = os.path.join(tmp_path, SNAPSHOT_O)
                os.system(cmd_o % (snapshot_original, pdf_path))
                # Name of the preview snapshot
                if os.path.exists(snapshot_original):
                    snapshot_preview = os.path.join(tmp_path, SNAPSHOT_P)
                    # Make the snapshot_thumbnail
                    os.system(cmd_p % (snapshot_original, snapshot_preview))
                if not os.path.exists(snapshot_original) or \
                   not os.path.exists(snapshot_preview):
                    msg = 'Failed to create preview/original for snapshots.'
                    __logger__.info(_format % (label, msg))
                else:
                    if 'SNAPSHOTS' in list(resource_dict.keys()):
                        resource_dict['SNAPSHOTS'].append(snapshot_original)
                        resource_dict['SNAPSHOTS'].append(snapshot_preview)
                    else:
                        resource_dict['SNAPSHOTS'] = [snapshot_original,
                                                      snapshot_preview]
    return ra_dict


def get_display(obj):
    """
    Return tuplet of data to display via string formating.
    """
    _pj = utils.get_proper_str(obj['project_id'])
    _su = utils.get_proper_str(obj['subject_label'])
    _st = utils.get_proper_str(obj['session_type'])
    _se = utils.get_proper_str(obj['session_label'], size=15)
    _re = '/'.join(list(obj['resource'].keys()))

    if 'qcstatus' in list(obj.keys()):  # Assessor
        _as = utils.get_proper_str(obj['label'], size=27)
        _at = utils.get_proper_str(obj['proctype'], size=12)
        _job = utils.get_proper_str(obj['procstatus'], size=12)
        _qc = utils.get_proper_str(obj['qcstatus'], size=12)
        return (-10, 'assessor', -10, _pj, -10, _su, -10, _st, -15, _se,
                -30, _as, -15, _at, -15, _job, -15, _qc, -58, _re)
    else:
        _sc = utils.get_proper_str(obj['ID'], size=27)
        _sct = utils.get_proper_str(obj['type'], size=27)
        _sd = utils.get_proper_str(obj['series_description'], size=12)
        _qc = utils.get_proper_str(obj['quality'], size=12)
        return (-10, 'scan', -10, _pj, -10, _su, -10, _st, -15, _se, -30, _sc,
                -15, _sct, -15, _sd, -15, _qc, -58, _re)


def print_report(csv_file, session_type):
    """
    Print report on the upload to verify the options from the user

    :param csv_file: csv file containg the upload information
    :param session_type: type of session to upload to XNAT
    :return: None
    """
    _fmt = ' %*s | %*s | %*s | %*s | %*s | %*s | %*s | %*s | %*s | %*s '
    scans, assessors = read_csv_for_upload(csv_file, session_type, report=True)
    _header = _fmt % (
        -10, 'ObjectType', -10, 'Project', -10, 'Subject', -10, 'SessType',
        -15, 'Session', -30, 'Label', -15, 'Type', -15, 'Description',
        -15, 'Quality', -58, 'Resource')
    _objects = list()
    _list = scans + assessors
    for obj in sorted(_list, key=lambda k: k['session_label']):
        _display = get_display(obj)
        _objects.append(_fmt % _display)

    __logger__.info(DISPLAY_TEMP.format(date=str(datetime.now()),
                                        header=_header,
                                        objects='\n'.join(_objects)))


def print_modality():
    """
    Print modality available for upload to XNAT using Xnatupload

    :return: None
    """
    print('INFO: Printing the modality available for a Session')
    sort_list = sorted(utils.XNAT_MODALITIES,
                       key=lambda key: utils.XNAT_MODALITIES[mod_key])
    for mod_key, mod_dict in list(sort_list.items()):
        print(' %*s : %*s ' % (-17, mod_key, -30, mod_dict['info']))
    print('==================================================================')


def get_files_in_folder(folder, label=''):
    """
    Get all the files and subfolder inside a folder

    :param folder: folder path to search
    :param label: prefix path to happen to the path
    :return: list of files/folder
    """
    f_list = list()
    for fpath in os.listdir(folder):
        ffpath = os.path.join(folder, fpath)
        if os.path.isfile(ffpath):
            fpath = check_image_format(fpath)
            if label:
                filename = os.path.join(label, fpath)
            else:
                filename = fpath
            f_list.append(filename)
        else:
            label = os.path.join(label, fpath)
            f_list.extend(get_files_in_folder(ffpath, label))
    return f_list


def check_folder_resources(resource_obj, folder, force=False):
    """
    Check that the files don't exist on the XNAT resource before uploading

    :param resource_obj: pyxnat resource Eobject
    :param folder: folder containing the images
    :param force: remove the previous resource file
    :return: True if not upload, False otherwise
    """
    _format = '     - WARNING: filepath %s -- %s'
    for fpath in get_files_in_folder(folder):  # RECURSIVELY
        if resource_obj.file(fpath).exists():
            if force:
                resource_obj.file(fpath).delete()
            else:
                msg = 'already found on XNAT. Use --force to upload this file.'
                __logger__.info(_format % (fpath, msg))
                return True

    return False


def check_image_format(fpath):
    """
    Check that the NII and REC are gzip before uploading

    :param fpath: path that need to be check
    :return: path for the image
    """
    cmd = 'gzip %s'
    if fpath.endswith('.nii') or fpath.endswith('.rec'):
        os.system(cmd % (fpath))
        fpath = '%s.gz' % (fpath)
    return fpath


def is_file(fpath):
    """
    Verify if the path is a file and if it's a folder,
     if there is only one file in the folder, return the file

    :param fpath: path that need to be check
    :return: True if it's a file or False otherwise, path of the file
    """
    if os.path.isfile(fpath):
        return True, fpath
    else:
        if len(glob.glob(os.path.join(fpath, '*'))) == 1:
            return True, glob.glob(os.path.join(fpath, '*'))[0]
        else:
            if fpath[-1] == '/':
                fpath = fpath[:-1]
            return False, fpath


def get_xnat_obj(xnat, obj_dict, session_type=None):
    """
    Generate the tree architecture for the object or select the xnat object

    :param xnat: pyxnat interface
    :param obj_dict: dictionary for attributes of a XNAT object
    :param session_type: type of session to create
    :return: None
    """
    obj = None

    # Session type
    stype = obj_dict['session_type']
    if session_type:
        stype = session_type
    date = datetime.now()
    c_date = '%s-%s-%s' % (str(date.year), str(date.month), str(date.day))

    # Project:
    project_obj = xnat.select('/project/%s' % (obj_dict['project_id']))
    if not project_obj.exists():
        err = 'Project %s does not exists on XNAT.'
        raise XnatToolsUserError(__exe__, err % (obj_dict['project_id']))

    # Scan or Assessors
    if obj_dict['object_type'] == 'scan':
        obj = XnatUtils.select_obj(
            xnat, obj_dict['project_id'], obj_dict['subject_label'],
            obj_dict['session_label'], scan_id=obj_dict['ID'])
        if not obj.exists():
            obj = create_scan(obj, obj_dict, stype, c_date)
    elif obj_dict['object_type'] == 'assessor':
        obj = XnatUtils.select_obj(
            xnat, obj_dict['project_id'], obj_dict['subject_label'],
            obj_dict['session_label'], assessor_id=obj_dict['label'])
        if not obj.exists():
            obj = create_assessor(xnat, obj, obj_dict, stype, c_date)
    else:
        msg = 'Warning: object_type not recognize between scan and assessor \
for %s'
        __logger__.info(msg % str(obj_dict))

    return obj


def create_scan(scan, scan_dict, session_type, creation_date):
    """
    Create the scans on XNAT

    :param scan: pyxnat scan Eobject
    :param scan_dict: attributes to set for the scan
    :param session_type: type of session
    :param creation_date: string representing date at creation
    :return: pyxnat scan Eobject
    """
    if session_type in MR_SCAN_FOR_MODALITY:
        scan_datatype = 'xnat:mrScanData'
    else:
        scan_datatype = 'xnat:%sScanData' % session_type.lower()

    kwargs = {
        'scans': scan_datatype,
        'type': scan_dict['type'],
        'series_description': scan_dict['series_description'],
        'quality': scan_dict['quality'],
        'note': "Upload with %s" % __exe__,
        # experiment
        'experiments': utils.XNAT_MODALITIES[session_type]['xsitype'],
        'xnat:experimentData/date': creation_date,
    }
    scan.insert(**kwargs)

    return scan


def create_assessor(xnat, assessor, assessor_dict, session_type,
                    creation_date):
    """
    Create the assessors on XNAT

    :param xnat: pyxnat interface
    :param assessor: pyxnat assessor Eobject
    :param assessor_dict: attributes to set for the assessors
    :param session_type: type of session
    :param creation_date: string representing date at creation
    :return: pyxnat assessor Eobject
    """
    now = datetime.now()
    today = '{}-{}-{}'.format(str(now.year), str(now.month), str(now.day))

    if assessor_dict['proctype'].lower() in ['freesurfer', 'fs'] and \
       XnatUtils.has_fs_datatypes(xnat):
        xsitype = XnatUtils.DEFAULT_FS_DATATYPE
        assessor_dict['proctype'] = 'FreeSurfer'
    elif XnatUtils.has_genproc_datatypes(xnat):
        xsitype = XnatUtils.DEFAULT_DATATYPE

    kwargs = {
        'assessors': xsitype,
        '%s/procstatus' % xsitype: assessor_dict['procstatus'],
        '%s/validation/status' % xsitype: assessor_dict['qcstatus'],
        '%s/proctype' % xsitype: assessor_dict['proctype'],
        '%s/date' % xsitype: today,
        # experiment
        'experiments': utils.XNAT_MODALITIES[session_type]['xsitype'],
        'xnat:experimentData/date': creation_date,
    }

    # For FreeSurfer Assessor add version
    if assessor_dict['proctype'].lower() in ['freesurfer', 'fs'] and \
       XnatUtils.has_fs_datatypes(xnat):
        kwargs['%s/fsversion' % xsitype] = '0'

    assessor.insert(**kwargs)

    return assessor


def upload_data_xnat(xnat, args, scans, assessors):
    """
    Main function to upload data from XNAT
       looping over the project/subject to be in order

    :param xnat: pyxnat interface
    :param args: arguments from argparser
    :param scans: list of scan dictionaries to upload
    :param assessors: list of assessor dictionaries to upload
    :return: None
    """
    _format = ' * project: %s - subject: %s - session: %s'
    previous = {'project': None, 'subject': None, 'session': None}

    # Upload:
    __logger__.info('INFO: Uploading data ...')
    _list = scans + assessors
    _nb_objs = len(_list)
    for ind, obj in enumerate(sorted(_list, key=lambda k: k['session_label'])):
        if utils.new_tree_object(previous, obj):
            previous = {'project': obj['project_id'],
                        'subject': obj['subject_label'],
                        'session': obj['session_label']}
            __logger__.info(_format % (obj['project_id'], obj['subject_label'],
                                       obj['session_label']))

        xnat_obj = get_xnat_obj(xnat, obj, args.session_type)
        if xnat_obj:
            __logger__.info(utils.get_obj_info(ind + 1, _nb_objs, obj))
            upload_resources(xnat_obj, args, xnat_obj, obj)


def upload_resources(xnat, args, obj, obj_dict):
    """
    Method to upload resources

    :param xnat: pyxnat interface
    :param args: arguments from argparser
    :param obj: pyxnat Eobject
    :param obj_dict: dictionary describing the pyxnat Eobject
    :return: None
    """
    warn_format = '     - File %s: WARNING -- path not found.'
    if args.delete_all:
        delete_all_resources(xnat, obj, obj_dict)

    # Upload each resources:
    for resource_label, fpath_list in list(obj_dict['resource'].items()):
        __logger__.info('   > Resource %s' % (resource_label))
        if resource_label == 'SNAPSHOTS' and \
           obj_dict['object_type'] == 'assessor':
            # Special upload for snapshots for assessor
            resource_obj = obj.out_resource(resource_label)
            upload_snaptshot(resource_obj, fpath_list, args.force, args.delete,
                             extract=not args.no_extract)
        else:
            for fpath in fpath_list:
                if not os.path.exists(fpath):
                    __logger__.info(warn_format % (fpath))
                else:
                    if obj_dict['object_type'] == 'scan':
                        resource_obj = obj.resource(resource_label)
                    else:
                        resource_obj = obj.out_resource(resource_label)

                    upload_path(resource_obj, resource_label, fpath,
                                args.force, args.delete,
                                extract=not args.no_extract)


def upload_path(resource_obj, resource_label, fpath, force, delete, extract):
    """
    Upload path to XNAT: either a file or folder

    :param resource_obj: pyxnat resource obj
    :param resource_label: label of the resource
    :param force: force the upload if file exists
    :param delete: delete the resource and all files before uploading
    :param extract: extract the files if it's a zip
    :return: None
    """
    _format_f = '     - File %s: uploading file...'
    _format_p = '     - Folder %s: uploading folder...'
    isfile, fpath = is_file(fpath)
    if isfile:
        __logger__.info(_format_f % (os.path.basename(fpath)))
        XnatUtils.upload_file_to_obj(
            fpath, resource_obj, remove=force, removeall=delete)
    else:
        __logger__.info(_format_p % (os.path.basename(fpath)))
        XnatUtils.upload_folder_to_obj(
            fpath, resource_obj, resource_label,
            remove=force, removeall=delete, extract=extract)


def delete_all_resources(xnat, obj, obj_dict):
    """
    Method to delete all resources for an object

    :param xnat: pyxnat interface
    :param obj: pyxnat Eobject
    :param obj_dict: dictionary describing the pyxnat Eobject
    :return: None
    """
    if obj_dict['object_type'] == 'scan':
        resources = XnatUtils.list_scan_resources(
            xnat, obj_dict['project_id'], obj_dict['subject_label'],
            obj_dict['session_label'], obj_dict['ID'])
        for resource_label in resources:
            obj.resource(resource_label).delete()
    else:
        resources = XnatUtils.list_assessor_out_resources(
            xnat, obj_dict['project_id'], obj_dict['subject_label'],
            obj_dict['session_label'], obj_dict['label'])
        for resource_label in resources:
            obj.out_resource(resource_label).delete()


def upload_snaptshot(resource_obj, fpath_list, force=False, delete=True,
                     extract=True):
    """
    Method to upload snapshots to a resource

    :param resource_obj: resource object to upload snapshots to
    :param fpath_list: paths of the files to be uploaded
    :param force: remove file if exists to force upload
    :param delete: delete previous resource
    :param extract: extract the files if it's a zip
    :return: None
    """
    # check if the resource exist, if yes remove it
    if delete and resource_obj.exists():
        resource_obj.delete()

    # Previews
    snapshot_preview = None
    snapshot_original = None
    for fpath in fpath_list:
        if os.path.basename(fpath) == SNAPSHOT_P:
            snapshot_preview = fpath
        elif os.path.basename(fpath) == SNAPSHOT_O:
            snapshot_original = fpath
        else:
            upload_path(resource_obj, 'SNAPSHOTS', fpath,
                        force, delete, extract=extract)

    if snapshot_preview and snapshot_original:
        XnatUtils.upload_assessor_snapshots(
            resource_obj.parent(), snapshot_original, snapshot_preview)


def run_xnat_upload(args):
    """
    Main function for xnat upload.

    :param args: arguments parse by argparse
    """
    if args.output_file:
        handler = logging.FileHandler(args.output_file, 'w')
        __logger__.addHandler(handler)

    if not os.path.exists(args.csv_file):
        err = "Argument -c/--csvfile: path '%s' does not exist."
        raise XnatToolsUserError(__exe__, err % args.csvfile)

    if args.session_type and \
       args.session_type not in list(utils.XNAT_MODALITIES.keys()):
        err = 'Argument --sess does not exist on XNAT: %s' % args.session_type
        raise XnatToolsUserError(__exe__, err)

    if args.print_modality:
        utils.print_separators()
        print_modality()

    if args.report:
        utils.print_separators()
        print_report(args.csv_file, args.session_type)
    else:

        if args.host:
            host = args.host
        else:
            host = os.environ['XNAT_HOST']
        user = args.username
        with XnatUtils.get_interface(host=host, user=user) as xnat:
            utils.print_separators()
            print('INFO: connection to xnat <%s>:' % (host))

            # has_fs_datatypes = XnatUtils.has_fs_datatypes(xnat)

            scans, assessors = read_csv_for_upload(args.csv_file,
                                                   args.session_type)
            if assessors and not XnatUtils.has_dax_datatypes(xnat):
                err = 'Your XNAT instance does not have the assessor \
datatypes required by dax. Please install them. Check \
https://github.com/VUIIS/dax wiki.'
                raise XnatToolsUserError(__exe__, err)
            upload_data_xnat(xnat, args, scans, assessors)

    utils.print_end(__exe__)


def add_to_parser(parser):
    """
    Method to add arguments to default parser for xnat_tools in utils.

    :param parser: parser object
    :return: parser object with new arguments
    """
    _h = 'CSV file with the information for uploading data to XNAT. Header: %s'
    parser.add_argument("-c", "--csv", dest="csv_file", required=True,
                        help=_h % ','.join(utils.CSV_HEADER))
    _h = 'Session type on Xnat. Use printmodality to see the options.'
    parser.add_argument("--sess", dest="session_type", default=None, help=_h)
    parser.add_argument("--report", dest="report", action="store_true",
                        help="Print a report to verify inputs.")
    parser.add_argument("--force", dest="force", action="store_true",
                        help="Force the upload and remove previous resources.")
    parser.add_argument("--delete", dest="delete", action="store_true",
                        help="Delete resource files prior to upload.")
    parser.add_argument("--deleteAll", dest="delete_all", action="store_true",
                        help="Delete all resources in object prior to upload.")
    _h = "Do not extract the zip files on XNAT when uploading a folder."
    parser.add_argument("--noextract", dest="no_extract", action="store_true",
                        help=_h)
    _h = "Display the different modality available on XNAT for a session."
    parser.add_argument("--printmodality", dest="print_modality", help=_h,
                        action="store_true")
    parser.add_argument("-o", "--output", dest="output_file", default=None,
                        help="File path to store the script logs.")
    return parser


if __name__ == '__main__':
    extra_display = """IMPORTANT WARNING FOR ALL USERS ABOUT XNAT:
   session_label needs to be unique for each session.
   Two subjects can NOT have the same session_label"""
    utils.run_tool(__exe__, __description__, add_to_parser, __purpose__,
                   run_xnat_upload, extra_display=extra_display)
