#!/usr/bin/env python
#                                                            _
# PACS ToolKit movescu wrapper
#
# (c) 2016-2019 Fetal-Neonatal Neuroimaging & Developmental Science Center
#                   Boston Children's Hospital
#
#              http://childrenshospital.org/FNNDSC/
#                        dev@babyMRI.org
#

import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

from    argparse            import RawTextHelpFormatter
from    argparse            import ArgumentParser
from    terminaltables      import SingleTable
from    pfmisc._colors      import Colors
import  json
import  pypx
import  socket
import  pudb

str_defIP   = [l for l in (
                [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2]
                if not ip.startswith("127.")][:1],
                    [[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close())
                for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0]

str_name    = "px-push"
str_version = "3.0.0"
str_desc    = Colors.CYAN + """

                                                     _
                                                    | |
             _ __  __  __ ______  _ __   _   _  ___ | |__
            | '_ \ \ \/ /|______|| '_ \ | | | |/ __|| '_ \.
            | |_) | >  <         | |_) || |_| |\__ \| | | |
            | .__/ /_/\_\        | .__/  \__,_||___/|_| |_|
            | |                  | |
            |_|                  |_|

                        PACS ToolKit Wrapper
                            "move" images

                       -- version """ + \
             Colors.YELLOW + str_version + Colors.CYAN + """ --

    ``px-push`` is a module / script that provides functionality for
    "pushing" specific study/series collections to a remote destination.

    Depending on the context of the CLI flags, ``px-push`` can push to
    either a PACS node or to ChRIS swift storage.

""" + Colors.NO_COLOUR

def synopsis(ab_shortOnly = False):
    scriptName = os.path.basename(sys.argv[0])
    shortSynopsis =  Colors.YELLOW + '''
    NAME

	    %s

        - (image) push

    SYNOPSIS

            _script mode_:
            px-push                                                 \\
                [--db <dblogbasepath>]                              \\
                [--xcrdir|-p <xcrdir>]                              \\
                [--xcrfile|-f <xcrfile>]                            \\
                [--xcrdirfile <xcrdirfile>]                         \\
                [--parseAllFilesWithSubStr <substr>]                \\
                [--aet <AETitle>]                                   \\
                [--aec <CalledAETitle>]                             \\
                [--serverIP <PACSserverIP>]                         \\
                [--serverPort <PACSserverPort>]                     \\
                [--StudyInstanceUID <studyInstanceUID>]             \\
                [--SeriesInstanceUID <seriesInstanceUID>]           \\
                [--swiftIP <swiftStorage>]                          \\
                [--swiftPort <swiftPort>]                           \\
                [--swiftLogin <user>:<password>]                    \\
                [--swiftServicesPACS <PACSname>]                    \\
                [--swiftPackEachDICOM]                              \\
                [--CUBEURL <cubeURL>]                               \\
                [--CUBEusername <cubeusername>]                     \\
                [--CUBEuserpasswd <cubeuserpassword>]               \\
                [-x|--desc]                                         \\
                [-y|--synopsis]                                     \\
                [--json]                                            \\
                [--version]                                         \\
                [--debugToDir <dir>]                                \\
                [--verbosity <level>]

    BRIEF EXAMPLE

        px-push                                                     \\
            --swiftIP 192.168.1.216                                 \\
            --swiftPort 8080                                        \\
            --swiftLogin cube:cube1234                              \\
            --swiftServicesPACS BCH                                 \\
            --StudyInstanceUID 1.2.3.435                            \\
            --SeriesInstanceUID 2.3.5.6.7                           \\

    ''' % scriptName + Colors.NO_COLOUR

    description = Colors.LIGHT_GREEN + '''

    DESCRIPTION

        ``px-push`` is the primary vehicle for transmitting a DICOM file
        to a remote location. The remote location can be either another
        PACS node (in which case the PACS related CLI are used), or
        swift storage (in which the swift related CLI are used). In the
        case of swift storage, and if CUBE related CLI are used, then
        this module will also register the files that have been pushed
        to the CUBE instance.

        While the most typical use case will involve specifying the
        file (set) to transmit using a (study/series) description to
        the internal `smdb` database, it is also possible to indate a
        file (or set of files) directly on the filesystem.

    ARGS

        [--db <dblogbasepath>]
        A path to the base directory of the DB contents/files that track
        received files. This is typically the <logDir> of the `px-repack`
        process that repacks incoming DICOM files. Specifying this path
        allows `px-find` to access db tables needed to track the number
        of DICOM files that are received in the case of a 'retrieve'
        event.

        [--xcrdir|-p <xcrdir>]
        A directory that contains a DICOM file to process.

        [--xcrfile|-f <xcrfile>]
        A specific DICOM file in the <xcrdir>.

        [--xcrdirfile <xcrdirfile>]
        A fully qualified dir and file specifier. If passed, the script
        will separate into dir and file parts.

        [--parseAllFilesWithSubStr <substr>]
        If passed, process all the files in the <xcrdir> that contain the
        <substr> in their filename in one sweep.

        [--aet <AETitle>]
        The AETitle of *this* entity.

        [--aec <CalledAETitle>]
        The called AETitle of *this* entity. Needed for some PACS systems.

        [--serverIP <PACSserverIP>]
        The IP of the PACS server to which a push will be made.

        [--serverPort <PACSserverPort>]
        The port associated with the PACS server.

        [--swiftIP <swiftStorage>]
        [--swiftPort <swiftPort>]
        The IP address and port for the swift storage container.

        [--swiftLogin <user>:<password>]
        The username and password for swift access.

        [--swiftPackEachDICOM]
        If specified, assume DICOMS are unordered and not all of the same
        series. Process a store directive for each DICOM file individually.

        [--swiftServicesPACS <PACSname>]
        The name of the PACS within the swift ``SERVICES/PACS`` location.

        [--CUBEURL <cubeURL>]
        The URL of the CUBE instance.

        [--CUBEusername <cubeusername>]
        The name of a CUBE user.

        [--CUBEuserpasswd <cubeuserpassword>]
        The password for a CUBE user. Note the flag spelling is 'passwd'!

        [--StudyInstanceUID <studyInstanceUID>]
        The <studyInstanceUID> to request.

        [--SeriesDescription <seriesInstanceUID>]
        The <seriesInstanceUID> to request.

        [-x|--desc]
        Provide an overview help page.

        [-y|--synopsis]
        Provide a synopsis help summary.

        [--version]
        Print internal version number and exit.

        [--json]
        If specified, print the JSON structure related to the process event. This is
        useful when used in a pipeline fashion with other px-* modules.

        [--debugToDir <dir>]
        A directory to contain various debugging output -- these are typically
        JSON object strings capturing internal state. If empty string (default)
        then no debugging outputs are captured/generated. If specified, then
        ``pfcon`` will check for dir existence and attempt to create if
        needed.

        [-v|--verbosity <level>]
        Set the verbosity level. "0" typically means no/minimal output. Allows for
        more fine tuned output control as opposed to '--quiet' that effectively
        silences everything.

    EXAMPLES

        px-push                                                     \\
            --swiftIP 192.168.1.216                                 \\
            --swiftPort 8080                                        \\
            --swiftLogin chris:chris1234                            \\
            --swiftBaseLocation SERVICES/PACS                       \\
            --CUBEURL https://localhost:8000/api/v1/                \\
            --CUBEusername chris                                    \\
            --CUBEuserpasswd chris1234                              \\
            --StudyInstanceUID 1.2.3.435                            \\
            --SeriesInstanceUID 2.3.5.6.7                           \\

        px-push                                                     \\
            --swiftIP 192.168.1.216                                 \\
            --swiftPort 8080                                        \\
            --swiftLogin chris:chris1234                            \\
            --swiftServicesPACS BCH                                 \\
            --swiftPackEachDICOM                                    \\
            --CUBEURL https://localhost:8000/api/v1/                \\
            --CUBEusername chris                                    \\
            --CUBEuserpasswd chris1234                              \\
            --xcrdir /tmp/dicom                                     \\
            --parseAllFilesWithSubStr dcm

    ''' + Colors.LIGHT_PURPLE + '''

    DOCKERIZED EXAMPLES


    ''' + Colors.NO_COLOUR

    if ab_shortOnly:
        return shortSynopsis
    else:
        return shortSynopsis + description


parser = ArgumentParser(
            description         = str_desc,
            formatter_class     = RawTextHelpFormatter
        )

# db access settings
parser.add_argument(
    '--db',
    action  = 'store',
    dest    = 'dblogbasepath',
    type    = str,
    default = '/tmp/log',
    help    = 'path to base dir of receipt database')

# File/dir settings
parser.add_argument(
    '-p', '--xcrdir',
    action  = 'store',
    dest    = 'str_xcrdir',
    type    = str,
    default = '/tmp',
    help    = 'Directory containing a received study'
    )
parser.add_argument(
    '-f', '--xcrfile',
    action  = 'store',
    dest    = 'str_xcrfile',
    type    = str,
    default = '',
    help    = 'File in <xcrdir> to process'
    )
parser.add_argument(
    '--xcrdirfile',
    action  = 'store',
    dest    = 'str_xcrdirfile',
    type    = str,
    default = '',
    help    = 'Fully qualified file to process'
    )
parser.add_argument(
    '--parseAllFilesWithSubStr',
    action  = 'store',
    dest    = 'str_filesubstr',
    type    = str,
    default = '',
    help    = 'Parse all files in <xcrdir> that contain <substr>'
    )

# PACS settings
parser.add_argument(
    '--aet',
    action  = 'store',
    dest    = 'aet',
    type    = str,
    default = 'CHRIS-ULTRON-AET',
    help    = 'aet')
parser.add_argument(
    '--aec',
    action  = 'store',
    dest    = 'aec',
    type    = str,
    default = 'CHRIS-ULTRON-AEC',
    help    = 'aec')
parser.add_argument(
    '--PACSserverIP',
    action  = 'store',
    dest    = 'str_PACSserverIP',
    type    = str,
    default = '192.168.1.110',
    help    = 'PACS server IP')
parser.add_argument(
    '--PACSserverPort',
    action  = 'store',
    dest    = 'str_PACSserverPort',
    type    = str,
    default = '4242',
    help    = 'PACS server port')
parser.add_argument(
    '--movescu',
    action  = 'store',
    dest    = 'movescu',
    type    = str,
    default = '/usr/bin/movescu',
    help    = '"movescu"" executable absolute location')

# Swift settings
parser.add_argument(
    '--swiftIP',
    action  = 'store',
    dest    = 'str_swiftIP',
    type    = str,
    default = '',
    help    = 'swift IP')
parser.add_argument(
    '--swiftPort',
    action  = 'store',
    dest    = 'str_swiftPort',
    type    = str,
    default = '',
    help    = 'swift port')
parser.add_argument(
    '--swiftLogin',
    action  = 'store',
    dest    = 'str_swiftLogin',
    type    = str,
    default = '',
    help    = 'swift login')
parser.add_argument(
    '--swiftServicesPACS',
    action  = 'store',
    dest    = 'str_swiftServicesPACS',
    type    = str,
    default = '',
    help    = 'swift PACS location within SERVICE/PACS to push files')
parser.add_argument(
    "--swiftPackEachDICOM",
    help    = "If specified, determine the pack location of _each_ DICOM file",
    dest    = 'b_swiftPackEachDICOM',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--swiftBaseLocation',
    action  = 'store',
    dest    = 'str_swiftBaseLocation',
    type    = str,
    default = '',
    help    = 'swift base location to push files')

# CUBE settings
parser.add_argument(
    '--CUBEURL',
    action  = 'store',
    dest    = 'str_CUBEURL',
    type    = str,
    default = 'http://localhost:8000/api/v1/',
    help    = 'CUBE URL'
    )
parser.add_argument(
    '--CUBEusername',
    action  = 'store',
    dest    = 'str_CUBEusername',
    type    = str,
    default = 'chris',
    help    = 'Username with which to log into CUBE'
    )
parser.add_argument(
    '--CUBEuserpasswd',
    action  = 'store',
    dest    = 'str_CUBEuserpasswd',
    type    = str,
    default = 'chris1234',
    help    = 'CUBE user password'
    )


# Data settings
parser.add_argument(
    '--SeriesInstanceUID',
    action  = 'store',
    dest    = 'SeriesInstanceUID',
    type    = str,
    default = '',
    help    = 'Series Instance UID')
parser.add_argument(
    '--StudyInstanceUID',
    action  = 'store',
    dest    = 'StudyInstanceUID',
    type    = str,
    default = '',
    help    = 'Study Instance UID')

parser.add_argument(
    "-v", "--verbosity",
    help    = "verbosity level for app",
    dest    = 'verbosity',
    type    = int,
    default = 1)
parser.add_argument(
    "--json",
    help    = "return a JSON payload",
    dest    = 'json',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    "-x", "--desc",
    help    = "long synopsis",
    dest    = 'desc',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    "-y", "--synopsis",
    help    = "short synopsis",
    dest    = 'synopsis',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--version',
    help    = 'if specified, print version number',
    dest    = 'b_version',
    action  = 'store_true',
    default = False
)

args        = parser.parse_args()

if args.desc or args.synopsis:
    print(str_desc)
    if args.desc:
        str_help     = synopsis(False)
    if args.synopsis:
        str_help     = synopsis(True)
    print(str_help)
    sys.exit(1)

if args.b_version:
    print("Version: %s" % str_version)
    sys.exit(1)

# Return the JSON result as a serialized string:
output = pypx.push(vars(args))

if int(args.verbosity):
    if args.json:
        try:
            print(json.dumps(output, indent = 4))
        except Exception as e:
            print(json.dumps({
                'status'    : False,
                'error'     : '%s' % e
            }))
