#!/usr/bin/env python3
#
# (c) 2020 Fetal-Neonatal Neuroimaging & Developmental Science Center
#                   Boston Children's Hospital
#
#              http://childrenshospital.org/FNNDSC/
#                        dev@babyMRI.org
#

import  sys
import  os
from    distutils.sysconfig     import get_python_lib

from    argparse                import RawTextHelpFormatter
from    argparse                import ArgumentParser
import  shutil

import  pfmisc
from    pfmisc._colors          import Colors
from    pfmisc.C_snode          import  *

import  json
import  time


str_version = "2.0.0.0"
str_name    = 'pman_do'
str_desc    = Colors.CYAN + """


                                              _       
                                             | |      
             _ __  _ __ ___   __ _ _ __    __| | ___  
            | '_ \| '_ ` _ \ / _` | '_ \  / _` |/ _ \ 
            | |_) | | | | | | (_| | | | || (_| | (_) |
            | .__/|_| |_| |_|\__,_|_| |_| \__,_|\___/ 
            | |                       ______          
            |_|                      |______|         
            

    font generated by:
    http://patorjk.com/software/taag/#p=display&f=Doom&t=pman_do


                            pman_do

    A simple stand alone script that can instruct a `pman` instance
    to perform directives contained in JSON msg files located on a
    filesystem

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

    'pman_do' is very simple app that directs a `pman`
    instance to perform certain operations through a directive
    file stored temporarily on a filesystem that `pman` runs on.

    Essentially, 'pman_do' simply stores a conformant JSON msg
    to a specific file location. In turn, `pman`, periodically 
    scans this location. If it finds a directive file, `pman`
    will read and parse the JSON contents.

    The intial need for the script was simply to address a quick 
    and simple mechanism for effecting a `pman` data base clean 
    after CUBE integration tests. The script has since grown to be
    a bit more general and can be used as a vehicle to send any
    instructions to a `pman` process via files on the filesystem.

    The script also allows for communication with a `pman` process
    if the network/port of `pman` listener is unknown or hard to 
    find. Of course, this assumes that 'pman_do' has access to the
    same filesystem that `pman` runs on. In the case of dockerized
    compute, writing to the `pman` docker filesystem might at times
    be easier than trying to speak to the `pman` network interface.

    The 'pman_do' mechanism address one approach to communicate
    with a `pman` process outside of using the network interface, and
    instead using the `pman` filesystem as payload delivery system.

    Once `pman` has read the directive from disk, it will delete 
    any trace of the directive.

    Yes, it's all very cloak-and-dagger!

""" + Colors.NO_COLOUR

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

	    pman_do

        - Save certain JSON directives to a `pman` filesystem.

    SYNOPSIS

            pman_do                                                 \\
                [--op <somePrecannedOperation>]                     \\
                [--msg <JSONmsgFile>]                               \\
                [--saveTo <directiveFile>]                          \\
                [--wait <seconds>]                                  \\
                [--synopsis]                                        \\
                [--desc]                                            \\
                [--version]                                         \\
                [--verbosity <level>]

    BRIEF EXAMPLE

            pman_do --op DBclean

    '''

    description =  '''
    DESCRIPTION

        `pman_do` provides directives to `pman` using the filesystem
        as communications channel.

    ARGS
        [--op <somePrecannedOperation>]
        A string name of some "precanned" directive to store. Current
        "precanned" directives are:

                DBclean - clean the internal `pman` database.

        [--msg <JSONmsgFile>]
        A `pman` conformant JSON message that can direct `pman` to
        perform a valid operation.

        [--saveTo <directiveFile>]
        The location on persistent storage of the directive file that
        `pman` will, execute, and then delete.

        [--wait <seconds>]
        If specified, wait <seconds> seconds. This is useful to force
        some execution thread to idle and give `pman` time to
        asynchronously read and execute the clean directive.

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

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

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

        [-v|--verbosity <level>]
        Set the verbosity level. "0" typically means no/minimal output.


    EXAMPLES

            pman_do --saveTo /tmp/d_msg.json        \\
                    --op DBclean

                    --- OR ---

            pman_do --saveTo /tmp/d_msg.json        \\
                    --msg                           \\
                        '{  "action": "DBctl",
                                "meta": {
                                        "do":     "clear"
                                }
                        }'

    '''
    if ab_shortOnly:
        return shortSynopsis
    else:
        return shortSynopsis + description

class Pman_do:
    def __init__(self, **kwargs):
        """
        Simple constructor
        """
        self.saveTo             = "/tmp/d_msg.json"
        self.__name__           = "db_clean"

        # Debug parameters
        self.str_debugFile      = '/dev/null'
        self.b_debugToFile      = True
        self.verbosity          = 1
        self.wait               = 0

        for key,val in kwargs.items():
            if key == 'saveTo':         self.saveTo         = val
            if key == 'verbosity':      self.verbosity      = int(val)
            if key == 'wait':           self.wait           = int(val)


        self.dp                 = pfmisc.debug(
                                            verbosity   = self.verbosity,
                                            within      = self.__name__,
                                            syslog      = False
                                )
        self.dp.methodcol       = 20

    def precanned_opParse(self, str_op):
        """
        Given the passed str_op, return a d_msg structure.

        Currently supported ops:

            * DBclean   - clean the internal pman database

        """

        d_ret = {
            'status':   False,
            'd_msg':    {}
        }

        if str_op == 'DBclean':
            d_ret['d_msg'] = {
                "action": "DBctl",
                "meta": {
                    "do":     "clear"
                }
            }
            d_ret['status'] = True

        return d_ret

    def run(self, args):
        """
        Main run method.

        Essentially, this simply deposits a message directive
        on the file system -- this provides a mechanism to inject 
        commands to a `pman` instance without sending the JSON 
        payload to the listener.

        If `pman` finds the directive file it will read and execute
        the contents and then delete it.
        """

        b_canSave       = False
        d_msg           = {}

        if len(args.op):
            d_precanned = self.precanned_opParse(args.op)
            b_canSave   = d_precanned['status']
            d_msg       = d_precanned['d_msg']

        if len(args.msg):
            b_canSave   = True
            d_msg       = json.loads(args.msg)

        if b_canSave:
            with open(self.saveTo, "w") as saveFile:
                json.dump(d_msg, saveFile, indent = 4)

            self.dp.qprint('msg to write = \n%s' % json.dumps(
                                        d_msg,
                                        indent  = 4),
                                        comms   = 'tx')

            self.dp.qprint('%50s%20s' % ('Writing directive file...',
                                        '[ %s ]' % self.saveTo))

            if self.wait:
                self.dp.qprint('%50s%20s' % ('Starting wait timer for ...',
                                        '[ %d seconds ]'% self.wait))
                time.sleep(self.wait)
                self.dp.qprint('%50s%8s' % ('Timer ...',
                                        '[ done ]'))

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

parser.add_argument(
    "-s", "--saveTo",
    action  = 'store',
    help    = "location of the directive file",
    dest    = 'saveTo',
    default = "/tmp/d_msg.json"
)
parser.add_argument(
    '--msg',
    action  = 'store',
    dest    = 'msg',
    default = '',
    help    = 'Message to store on disk.'
)
parser.add_argument(
    '--op',
    action  = 'store',
    dest    = 'op',
    default = '',
    help    = 'Precanned message lookup.'
)
parser.add_argument(
    "-w", "--wait",
    help    = "wait an amount of <seconds>",
    dest    = 'wait',
    default = "0"
)
parser.add_argument(
    '--version',
    help    = 'if specified, print version number',
    dest    = 'b_version',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    "-v", "--verbosity",
    help    = "verbosity level for app",
    dest    = 'verbosity',
    default = "1")
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
)

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)

pman_do = Pman_do(
            saveTo      = args.saveTo,
            wait        = args.wait,
            verbosity   = args.verbosity
        )
pman_do.run(args)
