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

import sys
import os
# sys.path.insert(1, os.path.join(os.path.dirname(__file__), '..'))
from    distutils.sysconfig import get_python_lib

from    argparse            import RawTextHelpFormatter
from    argparse            import ArgumentParser
import  socket
import  threading

sys.path.insert(1, os.path.join(os.path.dirname(__file__), '../pman'))
try:
    import pman
except:
    sys.path.insert(1, os.path.join(get_python_lib(True, True), 'site-packages'))
    sys.path.insert(1, os.path.join(get_python_lib(True, True), 'site-packages/pman'))
    import pman

from pfmisc._colors         import Colors

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_version = "2.1.0.4"
str_name    = 'pman'
str_desc    = Colors.CYAN + """

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


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


                                Process-MANager

           A simple compute process tracking and management framework.

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

    'pman' is a server that spawns and tracks processes executed on an underlying
    shell/system. Internally, 'pman' provides services using Zero MQ and not
    http-based services.

    'pman' listens for REST comms and provides facilities for running, stopping,
    querying, and managing command-line processes.

    'pman' is also container-aware and can run containerized compute
    using either swarm or openshift (assuming suitable installed underlying
    infrastructure).

""" + \
           Colors.BLINK_RED +  """

              +---------------------------------------------------------+
              | Use '--enableTokenAuth' and '--tokenPath <tokenPath>'   |
              |         arguments for secure communication.             |
              +---------------------------------------------------------+

""" + Colors.NO_COLOUR

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

	    pman

        - process manager

    SYNOPSIS

            pman                                                    \\
                [--ip <IPofServer>]                                 \\
                [--port <port>]                                     \\
                [--protocol <protocol>]                             \\
                [--rawmode <modenumber>]                            \\
                [--listeners <numberOfListeners>]                   \\
                [--http]                                            \\
                [--debugToFile <fileName>]                          \\
                [--DBpath <str_location>]                           \\
                [--DBSavePeriod <seconds>]                          \\
                [--listenerSleep <amount>]                          \\
                [--container-env openshift|swarm]                   \\
                [--enableTokenAuth]                                 \\
                [--tokenPath <tokenPath>]                           \\
                [--synopsis]                                        \\
                [--desc]                                            \\
                [--verbosity <level>]

    BRIEF EXAMPLE

            pman                                                    \\
                --port 5010                                         \\
                --rawmode 1                                         \\
                --http                                              \\
                --listeners 12                                      \\
                --verbosity 1                                       \\
                --ip %s

    ''' % str_defIP

    description =  '''
    DESCRIPTION

        ``pman`` is a process manager, part of the ``pf`` suite of
        applications, and used mostly in the context of ChRIS.

        Typically ``pman`` is launched on a "remote" host where it
        listens for directives to launch and control processes.

    ARGS

        [--ip <IP>]

        The IP interface on which to listen.

        [--port <port>]
        The port on which to listen. Defaults to '5010'.

        [--protocol <protocol>]
        The protocol to interpret. Defaults to 'tcp'.

        [--rawmode]
        Internal zmq socket server mode. A value of '1' is usually used
        here.

        [--listeners <numberOfListenerThreads>]
        The number of internal threads to which requests are dispatched.

        [--http]
        Send return strings as HTTP formatted replies with content-type html.

        [--debugToFile]
        If specified, send debugging results to file.

        [--debugToFile <file>]
        In conjunction with --debugToFile, file which will receive debugging info.

        [--listenerSleep <time>]
        A small delay in the listener loop to prevent busy-wait.
        Default is 0.1 seconds.

        [--DBpath <str_location>]
        The location on persistent storage of the state "data base".
        Typically this is `/tmp/pman`.

        [--DBsavePeriod <time>]
        The periodicity in seconds for the internal DB save.

        [--enableTokenAuth]
        Enables token based authorization and can be configured to look for a .ini
        file or an openshift secret.

        [--tokenPath <tokenPath>]
        Specify the absolute path to the token in the file system.
        By default, this looks for the pfiohConfig.ini file in the current working directory.

        [-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. Allows for
        more fine tuned output control as opposed to '--quiet' that effectively
        silences everything.

        [--container-env <env>]
        The container env within which to run.

    EXAMPLES

    Start ``pman`` with 12 listeners:

        pman                                                        \\
                --port 5010                                         \\
                --rawmode 1                                         \\
                --http                                              \\
                --listeners 12                                      \\
                --verbosity 1                                       \\
                --ip %s

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

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

parser.add_argument(
    '--ip',
    action  = 'store',
    dest    = 'ip',
    default = str_defIP,
    help    = 'Set the IP to expose.'
)
parser.add_argument(
    '--port',
    action  = 'store',
    dest    = 'port',
    default = '5010',
    help    = 'Set the port to use.'
)
parser.add_argument(
    '--protocol',
    action  = 'store',
    dest    = 'protocol',
    default = 'tcp',
    help    = 'Set the protocol to use.'
)
parser.add_argument(
    '--rawmode',
    action  = 'store',
    dest    = 'raw',
    default = '1',
    help    = 'Set the router raw mode.'
)
parser.add_argument(
    '--listeners',
    action  = 'store',
    dest    = 'listeners',
    default = '1',
    help    = 'Set the number of listeners.'
)
parser.add_argument(
    '--http',
    action  = 'store_true',
    dest    = 'http',
    default = False,
    help    = """If specified, send HTTP formatted replies with content-type 'html';
    otherwise content-type 'json'."""
)
parser.add_argument(
    '--debugToFile',
    action  = 'store_true',
    dest    = 'debugToFile',
    default = False,
    help    = 'If true, stream debugging info to file.'
)
parser.add_argument(
    '--debugFile',
    action  = 'store',
    dest    = 'debugFile',
    default = '%s/tmp/debug-pman.log' % os.environ['HOME'],
    help    = 'In conjunction with --debugToFile, stream debugging info to specified file.'
)
parser.add_argument(
    '--listenerSleep',
    action  = 'store',
    dest    = 'listenerSleep',
    default = '0.1',
    help    = 'Set the delay in each listener loop to prevent busy-wait.'
)
parser.add_argument(
    '--DBpath',
    help    = "location of the DB path",
    dest    = 'DBpath',
    default = "/tmp/pman"
)
parser.add_argument(
    '--DBsavePeriod',
    action  = 'store',
    dest    = 'DBsavePeriod',
    default = '60',
    help    = 'The periodicity, in seconds, for the internal DB to be flushed to disk.'
)
parser.add_argument(
    '--version',
    help    = 'if specified, print version number',
    dest    = 'b_version',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--container-env',
    help    = 'The container environment to run against (openshift, swarm)',
    dest    = 'containerEnv',
    action  = 'store',
    default = 'swarm'
)
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
)
parser.add_argument(
    '--enableTokenAuth',
    action  = 'store_true',
    help    = 'Enables token based authorization and can be configured to look for a .ini file or an openshift secret',
    dest    = 'b_tokenAuth',
    default = False
)
parser.add_argument(
    '--tokenPath',
    action  = 'store',
    help    = 'Specify the absolute path to the token in the file system.\nBy default, this looks for the pmanConfig.ini file in the current working directory',
    dest    = 'str_tokenPath',
    default = ''
)

args = parser.parse_args()

# server          = ThreadedHTTPServer((args.ip, int(args.port)), StoreHandler)
# server.setup(args = vars(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)

comm = pman.pman(
    IP              = args.ip,
    port            = args.port,
    protocol        = args.protocol,
    raw             = args.raw,
    listeners       = args.listeners,
    http            = args.http,
    listenerSleep   = args.listenerSleep,
    DBpath          = args.DBpath,
    DBsavePeriod    = args.DBsavePeriod,
    debugToFile     = args.debugToFile,
    debugFile       = args.debugFile,
    containerEnv    = args.containerEnv,
    desc            = str_desc,
    name            = str_name,
    version         = str_version,
    verbosity       = args.verbosity,
    b_tokenAuth     = args.b_tokenAuth,
    str_tokenPath   = args.str_tokenPath
)

# This starts the main thread and "detaches" execution from the main
# program.
t1 = threading.Thread(target = comm.thread_serve)
t1.start()

# Execution will block until pman receives an asynchronous http quit
# request, i.e.
# pfurl   --content-type application/vnd.collection+json      \
#         --verb POST                                         \
#         --rawmode --http 192.168.1.189:5010/api/v1/cmd      \
#         --jsonwrapper 'payload'                             \
#         --msg '{    "action": "quit",
#                     "meta": {
#                         "when":         "now",
#                         "saveDB":       true
#                     }
#                 }'

