"""
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWWNNNNNNNWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMWWNNNNXXXXXXXXXXXXXXXKKKXNNWWMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMWWNNXKK0OOO0XNXK0KKXXXXKXNNX0OOOO0KKXXNNWMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMWNXXK0kddxOkxO0KXXKXXXXXXXXXXXNXXXKOxdxkOO00KXWMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMWWNKOxdxxxxxxxO0KNXKXNXKKK0KKKXKKKKKK0kkOkxolodxkk0XWMMMMMMMMMMMMMMM
MMMMMMMMMWNXXKOOkkxxxkOddOXNXKOOO0KKXXXNNWWNXK00OxdkXNX0kdc:coox0WMMMMMMMMMMMMMM
MMMMMMMWNKOOkxxxxxxkOOOOO00kkO0KNNWWNX0OkO0OxxO000KXXNNNWNXOdc:ldkNMMMMMMMMMMMMM
MMMMMMWKOxoc:lddxxxxkO0KKK0OkxkO00Okxlccclooooddooodxxk0KXXNWXkxddxKWMMMMMMMMMMM
MMMMMNklc:;:clooxxxkkkkO00OOOxc,,;;,,,,;;;;::looddxxkdoloxk0KXXNX0xx0WMMMMMMMMMM
MMMMNx:,;coxOXNNWWNNXXXXKOkxdl:,...';:clodkOKXNNNNNNWNXOdoodkkOKNWN0kONMMMMMMMMM
MMMNx:coxO0KKK0OkkkO0KXXKOkxoc;'.',;coxkO0KKKKKKKKKXNWWWX0kdddxkOKNWWKKXWMMMMMMM
MMW0odkOkkkxddxO0XXXXXKK0Odoooc'...,:ccccccccloodxxkOO0KXNNKkxxxxkOKXNXXXNMMMMMM
MNOdxxxdodxk0XNNXKKKXXX0OO00kc......,coxkkOkxxddoooddxxkk0XNNXOxddxkOO0KXXXWMMMM
XxloddoodOXNNXKKKXNWXOkOKX0l'.........;clldkO0KKKKKK0OOkkkO0KNNXOxddxkkO0KXKNWMM
OolllldOKXX0O0XWMWKxoxKNXk;............',;llcldxk0KNWWWWNK0OOOKXNXOxddxkkOKXKXWM
dc:cldk0OkkKNWWWXkox0XXKd'................;oxdlccldxOXNWMMWNX0OOKXNXOxddxxk0XKXM
kc:oxdxddOXWNXKOdx0NNXKl....................lKXKOkdoodxOKXNWMWX0OOKXNKOkxdxOXNXN
Kloxllld0K0KK0doONWNNKl......................:ONWWWNXKOkk0KKXWMNKOOOKNX0kxxk0NXX
W0o:,:xOxdk00xxKWWWWXl........................,o0XNWWMWNXXXKKNWMWKOkkOKNX0Ok0NXX
MWKl:ldlcx0OkOXWMWMXl....,:loxkxdlcc:::::;,'....,lkKXNWNNNXKKXNWMN0kxkOXWX00KNXX
MMMXxc;;cxkxkKWMMMXl..',:okOO0KKKKK00KXXXXK0Okdc,.'lkKKKKXNXKKXNMMXOxxkOXXOO0NXX
MMMMXl,;codx0WMMMXl'',:ldO0KXXNWWNNNNNNXXNNNNNNX0kl,,lk0OO0XXKKXWMWKkxxk0KOOOKXX
MMMMWO:;lookXMWWNd,;loxOXNNNNNKKNWWNXKKXXXNNWWNNNX0d,.'lxkkOKK0KNWMXkxkkOOkOOKKX
MMMMMXoclcxKWWNNk;';:clodk0KNWNXNWNK000kkdoddkKXXXOoc,..,lkkO000XNWKkxkkOkkOO00K
MMMMMXocclOXWWNO:'.',,,'.',:xXWNXOkkko:,''.'':ooloxxol:...cxkOOOKNW0dxxkOkxkO00K
MMMMMXdc:oO0XNXo.,:ccldoc,..,d0KOdodko:;::::;:lxklcdOOkc...:xkkkOXN0dxxkOxxkO0OK
MMMMMWOc;ok0KXKl'::;:oddol;..cOOkdokOd:cccclooooxd:;dKX0l'..:dxdkKX0ddxxkdxkOOON
MMMMMMNx;;oxk00l',;;,..;ddl,.;xOkdodOkdc;,,locxOdldOKWWNO:...:ol:oOkodkxddxOOOKW
MMMMMMMNx;;loxkd;,;;:cldkdc;'lKNN0xOXKOkkk0K000K0OKWNNXX0c.. 'c:';xxlododk0OO0WM
MMMMMMMMNOo:;:colodc:lddl;''cKWWNKkxO0OxxkKXNNNXKXNNNNNXkc.. .,;.'cc::cok0OkONMM
MMMMMMMMMMNOo;,;:xXKOxdoc;,c0WWWNX000KKK0KXXNXXXNWNNXXN0c.....,,..'..;lxxdoONMMM
MMMMMMMMMMMMW0d:;dKXNX0kdll0WMWWNXKXXXXXXXKKKXNNWWWWNXK0o...........;:c::dKWMMMM
MMMMMMMMMMMMMMW0:cOKNWXOllONWWNKK0KXXNNNNNNNXXKKXXXXXKKx:........',,;;:dKWMMMMMM
MMMMMMMMMMMMMMMWx:oO0Kk:,oKNWWXOkO00O0XNWWWWNXK000000Od:'.......'';ld0NMMMMMMMMM
MMMMMMMMMMMMMMMM0;,lxxc',o0XNNKdlOKOxxxOKXNNNNX0Okolxo,....',''cdkKWMMMMMMMMMMMM
MMMMMMMMMMMMMMMXo..:ddc,',lkkxl;,:lldK0xxk0XXXK0Odc',;'''...'',xWMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMNk:.'dko:'',;;;:;',cloxO0K0OO000Odcc:....'....''oNMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMNo..coc;''',;;;:,,,,,,;lxxolodxoc;;c,..;,'.''..oNMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMWO;.';:;:;;:od:cc:;;::,',,;,;;;;,,,c;.'::;'.,,..ckXMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMM0:..,:;:clccl;,:,,,,''...':lc::loclc',::,..'..  .,oKWMMMMMMMMMM
MMMMMMMMMMMMMMMMWk'':lc,,,,coooooddl;......:occlllld:.,:,......     'l0WMMMMMMMM
MMMMMMMMMMMMMMMMNd';loc:;'',:cccclc:,.......:;,cc:cl,'cc;.......      .lONMMMMMM
MMMMMMMMMMMMMMMMXl';cccc;;:,'.. ..''''.....,c;:l:cc,,:c:,'.''.....      .;d0NMMM
MMMMMMMMMMMMMMMMKc'',:loc::;,...'c;''...'',:;';lcc;,c:,,,.''.. ....     ....;d0W
MMMMMMMMMMMMMMWXxlc;',ll:;;:;...;l;''',;:c:,..':;,;;;'.'..''. ............    .:
MMMMMMMMMMMMN0o;:do;',;,;ooc:,',,:;',:::cc;.'',:,.,,,'.....................  .
MMMMMMMMMN0d;...,lc,'.':odl::;;:,';;;clc:;,'..,,''''.'...................... .
MMMMMMWKd;.  ....;cc'.:odollc;::'.';,;l:,,;,.',.'',,.',,,'......................
MMMMWKd,..........;;..,:ooloccol;',:;,lc,,',';:,;:'...''.........''''''''''..'''
MMMW0d:'..........',..';cclooxxdooll::oc;,...:c',:,.,:;'.''''..''',;;,,,,,,,;ccl
MMMWN0d:;,'............';:clodxxxxlc:;:;:,..;;'''::':c,,,;;,',,,;;,;;,'',;ldk00K
MMMMMWN0koc:,,'''..''''.';;;lldkxo::;;;,''.','..,:,''',,,,',,,:::::;;;;;cdKWWMMM
MMMMMMMMMWXKOxlc;,...','...,:;coc;;;;'''''.....,:,...''''',;::clcclddxk0XWMMMMMM
MMMMMMMMMMMMMWNN0d:,........''';;..,,........';;,'','',;;:codxkOO0KNWWMMMMMMMMMM
MMMMMMMMMMMMMMMMMWX0xl;,...........',...,...':;;;;;::codk0XNWWMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMWXOdc,'..',,,,,;;,,,'.,;ccldkOOKXNWMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMWN0xoc:cccccldkxxkOOO0KXNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMN0xc;;::cxXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM


`7MMF'  `7MMF'
  MM      MM
  MM      MM   ,6"Yb.  M""MMV .gP"Ya  7MMpMMMb.
  MMmmmmmmMM  8)   MM  '  AMV ,M'   Yb  MM    MM
  MM      MM   ,pm9MM    AMV  8M~~~~~'  MM    MM
  MM      MM  8M   MM   AMV  ,YM.    ,  MM    MM
.JMML.  .JMML.`Moo9^Yo.AMMmmmM `Mbmmd'.JMML  JMML.



Welcome to the hazen Command Line Interface

The following Tasks are available:
- ACR phantom:
acr_snr | acr_slice_position | acr_slice_thickness | acr_spatial_resolution | acr_uniformity | acr_ghosting | acr_geometric_accuracy
- MagNET Test Objects:
snr | snr_map | slice_position | slice_width | spatial_resolution | uniformity | ghosting
- Caliber phantom:
relaxometry

All tasks can be run by executing 'hazen <task> <folder>'. Optional flags are available for the Tasks; see the General
Options section below. The 'acr_snr' and 'snr' Tasks have additional optional flags, also detailed below.

Usage:
    hazen <task> <folder> [options]
    hazen snr <folder> [--measured_slice_width=<mm>] [options]
    hazen acr_snr <folder> [--measured_slice_width=<mm>] [--subtract=<folder2>] [options]
    hazen relaxometry <folder> --calc=<T1> --plate_number=<4> [--verbose] [options]

    hazen -h | --help
    hazen --version

General Options: available for all Tasks
    --report                     Whether to generate visualisation of the measurement steps.
    --output=<path>              Provide a folder where report images are to be saved.
    --log=<level>                Set the level of logging based on severity. Available levels are "debug", "warning", "error", "critical", with "info" as default.

acr_snr & snr Task options:
    --measured_slice_width=<mm>  Provide a slice width to be used for SNR measurement, by default it is parsed from the DICOM (optional for acr_snr and snr)
    --subtract=<folder2>         Provide a second folder path to calculate SNR by subtraction for the ACR phantom (optional for acr_snr)

relaxometry Task options:
    --calc=<n>                   Choose 'T1' or 'T2' for relaxometry measurement (required)
    --plate_number=<n>           Which plate to use for measurement: 4 or 5 (required)
    --verbose                    Whether to provide additional metadata about the calculation in the result (optional)
"""


import importlib
import inspect
import logging
import sys
import json
import os

from docopt import docopt
import pydicom
from hazenlib.logger import logger
from hazenlib.utils import is_dicom_file, get_dicom_files
from hazenlib._version import __version__


def init_task(selected_task, files, report, report_dir):
    task_module = importlib.import_module(f"hazenlib.tasks.{selected_task}")
    
    try:
        task = getattr(task_module, selected_task.capitalize())(
            data_paths=files, report=report, report_dir=report_dir)
    except:
        class_list = [cls.__name__ for _, cls in inspect.getmembers(
            sys.modules[task_module.__name__],
            lambda x: inspect.isclass(x) and (x.__module__ == task_module.__name__)
            )]
        if len(class_list) == 1:
            task = getattr(task_module, class_list[0])(
                data_paths=files, report=report, report_dir=report_dir)
        else:
            raise Exception(
                f'Task {task_module} has multiple class definitions: {class_list}')

    return task


def main():
    arguments = docopt(__doc__, version=__version__)
    files = get_dicom_files(arguments['<folder>'])

    # Set common options
    log_levels = {
        "critical": logging.CRITICAL,
        "debug": logging.DEBUG,
        "info": logging.INFO,
        "warning": logging.WARNING,
        "error": logging.ERROR
    }
    if arguments['--log'] in log_levels.keys():
        level = log_levels[arguments['--log']]
        logging.getLogger().setLevel(level)
    else:
        # logging.basicConfig()
        logging.getLogger().setLevel(logging.INFO)

    report = arguments['--report']
    report_dir = arguments['--output'] if arguments['--output'] else os.path.join(
                os.getcwd(), 'report')

    # Parse the task and optional arguments:
    if arguments['snr'] or arguments['<task>'] == 'snr':
        selected_task = 'snr'
        task = init_task(selected_task, files, report, report_dir)
        result = task.run(
                measured_slice_width = arguments['--measured_slice_width'])
    elif arguments['acr_snr'] or arguments['<task>'] == 'acr_snr':
        selected_task = 'acr_snr'
        task = init_task(selected_task, files, report, report_dir)
        result = task.run(
                subtract = arguments['--subtract'],
                measured_slice_width = arguments['--measured_slice_width'])
    elif arguments['relaxometry'] or arguments['<task>'] == 'relaxometry':
        # TODO: Refactor Relaxometry task into HazenTask object
        #  - Relaxometry not currently converted to HazenTask object
        #  - Relaxometry task accessible via CLI using the old syntax until it can be refactored
        # relax_task = importlib.import_module(f"hazenlib.relaxometry")
        # dicom_objects = [pydicom.read_file(x, force=True) for x in files if is_dicom_file(x)]
        # relaxometry_args = parse_relaxometry_args(arguments)
        # result = relax_task.main(dicom_objects, **relaxometry_args,
        #                         report=report, report_dir=report_dir)
        selected_task = 'relaxometry'
        task = init_task(selected_task, files, report, report_dir)
        result = task.run(
                    calc = arguments['--calc'],
                    plate_number = arguments['--plate_number'], 
                    verbose = arguments['--verbose'])
    else:
        selected_task = arguments['<task>']
        task = init_task(selected_task, files, report, report_dir)
        result = task.run()

    result_string = json.dumps(result, indent=2)
    print(result_string)


if __name__ == "__main__":
    main()
