#!/usr/bin/env python3
#                                                            _
# DICOM storescp exec-on-reception processor
#
# (c) 2021 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        pfmisc._colors      import Colors
import      pypx
from        pypx                import repack

import      json
from        pypx.repack         import parser_setup, parser_interpret, parser_JSONinterpret

import      pudb
from        pudb.remote         import set_trace

str_name    = "px-repack"
str_version = "3.2.4"
str_desc    = Colors.CYAN + """
                                                              _
                                                             | |
         _ __  __  __ ______  _ __   ___  _ __    __ _   ___ | | __
        | '_ \ \ \/ /|______|| '__| / _ \| '_ \  / _` | / __|| |/ /
        | |_) | >  <         | |   |  __/| |_) || (_| || (__ |   <
        | .__/ /_/\_\        |_|    \___|| .__/  \__,_| \___||_|\_\.
        | |                              | |
        |_|                              |_|


                                PACS ToolKit Wrapper
                                    px-repack

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

    ``px-repack``   is  an  end-of-(DICOM)reception  processing  script and
    module. It is designed to be called by some initiator program and works
    on a directory and file to process.

    The typical initiator is a ``storescp`` "listener" (often under control
    of an  xinetd servce). Usually xinetd will fire up ``storescp`` on  the
    receipt  of traffic on a  listening port.  Assuming these are DICOM data
    stream files, ``storescp`` will save the DICOM file on success, and then
    call ``px-repack`` which will repack the file in a filesystem expressive
    location. This organization pattern can be specified by a set of string
    templates.

""" + Colors.NO_COLOUR

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

	    %s

        - DICOM file "repack" to FS location based on various
          DICOM tag values.

    SYNOPSIS

            px-repack                                               \\
                [--JSONargs <JSONargStringStructure>]               \\
                [--xcrdir|-p <xcrdir>]                              \\
                [--xcrfile|-f <xcrfile>]                            \\
                [--xcrdirfile <xcrdirfile>]                         \\
                [--parseAllFilesWithSubStr <substr>]                \\
                [--logdir|-l <logdir>]                              \\
                [--datadir|-d <datadir>]                            \\
                [--rootDirTemplate <rootTemplate>]                  \\
                [--studyDirTemplate <studyTemplate>]                \\
                [--seriesDirTemplate <seriesTemplate>]              \\
                [--imageFileTemplate <imageTemplate>]               \\
                [--cleanup]                                         \\
                [-x|--desc]                                         \\
                [-y|--synopsis]                                     \\
                [--version]                                         \\
                [--debug]                                           \\
                [--verbosity <level>]

    ''' % scriptName + Colors.NO_COLOUR

    description = Colors.LIGHT_GREEN + '''

    ARGS

        [--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.

        [--logdir|-l <logdir>]
        The directory containing log files relevant to px-repack operation.

        [--datadir|-d <datadir>]
        The directory that will contain the root of the file tree of packed
        image files.

        [--cleanup]
        If specified, clean up nicely like a good little script should. This
        removes the originally received DICOM files that are stored in the
        initial holding directory.

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

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

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

        [--debug]
        If specified, then log any debugging noise also to the <logdir>.

        [-v|--verbosity <level>]
        Set the verbosity level:

            * "0"   :   no output -- quiet
            * "1"   :   JSON dump output from the run call
            * "2"   :   Dump internal intermediary output

        [--rootDirTemplate <rootTemplate>]
        A string template for the root directory name in which to pack a
        given DICOM.

        Default: "%PatientID-%PatientName-%PatientBirthDate"

        [--studyDirTemplate <studyTemplate>]
        A string template for the study directory name in which to pack a
        given DICOM.

        Default: "%StudyDescription-%AssessionNumber-%StudyDate"

        [--seriesDirTemplate <seriesTemplate>]
        A string template for the series directory name in which to pack a
        given DICOM.

        Default: "%_pad|5,0_SeriesNumber-%SeriesDescription"

        [--imageFileTemplate <imageTemplate>]
        A string template for the image file name in which to copy a
        given DICOM.

        Default: "%_pad|4,0_InstanceNumber-%SOPInstanceUID"

    DESCRIPTION

        This script and module are used to process (typically) single
        DICOM files from one location on a filesystem and pack them to
        a different location.

        Essentially, ``px-repack`` is meant to operate on a single DICOM
        file, and based on the meta/header information, save that same file
        elsewhere in the filesystem under the following structure:

        <dataDir>
            |
            └─<rootTemplate>
                    |
                    └─<studyTemplate>
                             |
                             └─<seriesTemplate>
                                      |
                                      └─<imageTemplate>.dcm

        The purpose of course is to provide some meaningful structure to
        what would otherwise be an opaque and flat set of meaningless DICOM
        file names.

        ``px-repack`` is designed to be called by an different process,
        usually a ``storescp`` listener, on a per-file-received basis.

        However, as a convenience, ``px-repack`` can also be called directly
        on a single directory containing DICOMS and be instructed to repack
        all the files in the above template structure.

    TEMPLATES

        A template is a pattern for a string, based on DICOM tags. For example,
        a template that is specified as the following string:

            %PatientAge-%PatientID-output.txt

        will be parsed to

            006Y-4412364-ouptut.txt

        It is also possible to apply certain permutations/functions
        to a tag. For example, a function is identified by an underscore
        prefixed and suffixed string as part of the DICOM tag. If
        found, this function is applied to the tag value. For example,

            %PatientAge-%_md5|4_PatientID-output.txt

        will apply an md5 hash to the PatientID and use the first 4
        characters:

            006Y-7f38-output.txt

''' + Colors.PURPLE + '''

    EXAMPLES

        px-repack                                                   \\
                --xcrdir /dicom/tmp                                 \\
                --xcrfile file0001.dcm                              \\
                --logdir /dicom/log                                 \\
                --datadir /dicom/data                               \\
                --debug

''' + Colors.NO_COLOUR

    if ab_shortOnly:
        return shortSynopsis
    else:
        return shortSynopsis + description

parser          = parser_setup(str_desc)
args, unknown   = parser_interpret(parser)
if len(args.JSONargString):
    # If the user has specified args in the JSONargString, then
    # interpret this structure.
    #
    # NOTE:
    # This will OVERWRITE any non-JSON args that may have been specified
    # as per normal. Using the JSONargString is an either-or choice!
    d_JSONargs  : dict  = json.loads(args.JSONargString)
    args                = parser_JSONinterpret(parser, d_JSONargs)

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

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

handler     = repack.Process(args)
d_handler   = handler.run()

if args.verbosity:
    print(json.dumps(d_handler, indent = 4))
