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

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

import  socket
import  json
import  ast
import  sys
import  pfurl
import  pudb
from    argparse            import RawTextHelpFormatter
from    argparse            import ArgumentParser
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_defPort = '5055'

str_name    = 'pfurl'
str_version = "2.2.4.4"
str_desc    = Colors.CYAN + """

        __            _
       / _|          | |
 _ __ | |_ _   _ _ __| |
| '_ \|  _| | | | '__| |
| |_) | | | |_| | |  | |
| .__/|_|  \__,_|_|  |_|
| |
|_|


                            Process-File-over-URL

           A simple URL-based communication and control script.

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

    'pfurl' sends REST conforming commands and data to remote services,
    similar in some ways to the well-known CLI tool, 'curl' or the Python
    tool, 'httpie'.

    'pfurl' not only sends curl type payloads, but can also zip/unzip
    entire directories of files for transmission and reception.

    'pfurl' is designed to be part of the ChRIS framework but can also be
    used in similar use cases to 'curl' or 'httpie'.

""" + \
           Colors.RED +  """

              +---------------------------------------------------------+
              | Use --auth <user>:<password> and --authToken <token>    |
              |         arguments for secure communication.             |
              +---------------------------------------------------------+

""" + Colors.NO_COLOUR

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

	    pfurl

        - curl-type http communication client.

    SYNOPSIS

            pfurl                                                   \\
                    [--verb <RESTVERB>]                             \\
                    [--http <IP>:<port>]                            \\
                    [--httpProxy [<proto>://]<IP>[:<port>]]         \\
                    [--auth <user>:<passwd>]                        \\
                    [--jsonwrapper <outerMsgJSONwrapper>]           \\
                    [--quiet]                                       \\
                    [--raw]                                         \\
                    [--oneShot]                                     \\
                    [--man <help>]                                  \\
                    [-x|--desc]                                     \\
                    [-y|--synopsis]                                 \\
                    [--content-type <type>]                         \\
                    [--jsonpprintindent <indent>]                   \\
                    [--httpResponseBodyParse]                       \\
                    [--unverifiedCerts]                             \\
                    [--authToken <token>]                           \\
                    [--version]                                     \\
                    [-v|--verbosity <verbosity>]                    \\
                    --msg <JSONpayload>

    BRIEF EXAMPLE

            pfurl                                                   \\
                --verb POST --raw                                   \\
                --http %s:5005/api/v1/cmd                    \\
                --jsonwrapper 'payload'                             \\
                --msg                                               \\
                    '{  "action": "hello",
                            "meta": {
                                    "askAbout":     "sysinfo",
                                    "echoBack":     "Hi there!",
                                    "service":      "host"
                            }
                    }' --quiet --jsonpprintindent 4
    ''' % str_defIP

    description =  '''
    DESCRIPTION

        `pfurl` is a communications program that sends http-type curl
        data to a remote service. Although it is mostly used in the
        "pf" family of programs and in the ChRIS suite, it can be also
        used a general-purpose curl replacement.

        In addition to sending JSON-formatted strings to a service,
        `pfurl` can also send files and whole directories -- the latter
        being a zip compression of a directory tree.

        Various authentication options for verifying identify with the
        remote service are also available.

    ARGS

        [--verb <RESTVERB>]
        The REST verb to use for the remote service.

        [--http <IP>:<port>]
        The address of the remote service.

        [--httpProxy [<proto>://]<IP>[:<port>]]
        If specified, instruct ``pfurl`` to use the proxy as specified.
        Currently, only 'http' is supported. Valid values for this flag
        include, for example:

            --httProxy http://proxy.host.org:1234

            --httpProxy proxy.host.org:1234

        [--auth <user>:<passwd>]
        A user name and password authentication string.

        [--jsonwrapper <outerMsgJSONwrapper>]
        An optional outer wrapper for the JSON payload.

        [--quiet]
        If specified, only echo the final JSON payload returned
        from remote server.

        [--raw]
        If specified, do not wrap return data from remote call in a
        JSON wrapper.

        [--oneShot]
        If specified, transmit a shutdown control sequence to remote server
        after communicating. This of course only works for services that
        understand the shutdown protocol.

        [--man <help>]
        Provide detailed help on various topics.

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

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

        [--content-type <type>]
        Curl content-type descriptor.

        [--jsonpprintindent <indent>]
        If specified, print return JSON payload from remote service using
        <indent> indentation.

        [--httpResponseBodyParse]
        If specified, interpret the return payload as encapsulated in an
        http response.

        [--unverifiedCerts]
        If specified, allows transmission of https requests with self signed SSL
        certificates.

        [--authToken <token>]
        A token to transmit with an http request.

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

        --msg <JSONpayload>
        The actual JSON formatted payload to transmit to remote service.


    EXAMPLES

    Say 'hello' to a ``pfcon`` service listening on the localhost at port 5005:

            pfurl                                                   \\
                --verb POST --raw                                   \\
                --http %s:5005/api/v1/cmd                     \\
                --jsonwrapper 'payload'                             \\
                --msg                                               \\
                    '{  "action": "hello",
                            "meta": {
                                    "askAbout":     "sysinfo",
                                    "echoBack":     "Hi there!",
                                    "service":      "host"
                            }
                    }' --quiet --jsonpprintindent 4

    and print response "prettily" using an indent of 4.

    ''' % str_defIP

    if ab_shortOnly:
        return shortSynopsis
    else:
        return shortSynopsis + description

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

parser.add_argument(
    '--msg',
    action  = 'store',
    dest    = 'msg',
    default = '',
    help    = 'Message to send to pman or similar listener.'
)
parser.add_argument(
    '--verb',
    action  = 'store',
    dest    = 'verb',
    default = 'POST',
    help    = 'REST verb.'
)
parser.add_argument(
    '--http',
    action  = 'store',
    dest    = 'http',
    default = '%s:%s' % (str_defIP, str_defPort),
    help    = 'HTTP string: http://<IP>[:<port>]</some/path/>'
)
parser.add_argument(
    '--httpProxy',
    action  = 'store',
    dest    = 'httpProxy',
    default = '',
    help    = 'HTTP proxy string: [http://]<IP>[:<port>]'
)
parser.add_argument(
    '--auth',
    action  = 'store',
    dest    = 'auth',
    default = '',
    help    = 'user:passwd authorization'
)
parser.add_argument(
    '--jsonwrapper',
    action  = 'store',
    dest    = 'jsonwrapper',
    default = '',
    help    = 'wrap msg in optional field'
)
parser.add_argument(
    '--quiet',
    help    = 'if specified, only echo final JSON output returned from server',
    dest    = 'b_quiet',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--raw',
    help    = 'if specified, do not wrap return data from remote call in json field',
    dest    = 'b_raw',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--oneShot',
    help    = 'if specified, transmit a shutdown ctl to the remote service after event',
    dest    = 'b_oneShot',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--man',
    help    = 'request help: --man commands',
    dest    = 'man',
    action  = 'store',
    default = ''
)
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(
    '--content-type',
    help    = 'content type',
    dest    = 'contentType',
    action  = 'store',
    default = ''
)
parser.add_argument(
    '--jsonpprintindent',
    help    = 'pretty print json-formatted payloads',
    dest    = 'jsonpprintindent',
    action  = 'store',
    default = 0
)
parser.add_argument(
    '--httpResponseBodyParse',
    help    = 'if specified, assume full HTTP responses and parse only the body',
    dest    = 'b_httpResponseBodyParse',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--version',
    help    = 'if specified, print version number',
    dest    = 'b_version',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--unverifiedCerts',
    help    = "Allows transmission of https requests with self signed ssl certificates",
    dest    = 'unverifiedCerts',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--authToken',
    help    = 'Send a token for authentication with an http request',
    dest    = 'authToken',
    action  = 'store',
    default = ''
)
parser.add_argument(
    "-v", "--verbosity",
    help    = "verbosity level for app",
    dest    = 'verbosity',
    default = "0")

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)

pfurl  = pfurl.Pfurl(
    msg                         = args.msg,
    http                        = args.http,
    httpProxy                   = args.httpProxy,
    verb                        = args.verb,
    contentType                 = args.contentType,
    auth                        = args.auth,
    b_raw                       = args.b_raw,
    b_quiet                     = args.b_quiet,
    b_oneShot                   = args.b_oneShot,
    b_httpResponseBodyParse     = args.b_httpResponseBodyParse,
    jsonwrapper                 = args.jsonwrapper,
    man                         = args.man,
    desc                        = str_desc,
    name                        = str_name,
    version                     = str_version,
    startFromCLI                = True,
    verbosity                   = args.verbosity,
    unverifiedCerts             = args.unverifiedCerts,
    authToken                   = args.authToken
    )

str_response  = pfurl()

try:
    # Is this string response a dictionary?
    d_response = ast.literal_eval(str_response)
except:
    d_response = str_response

if not args.jsonpprintindent:
    print(str_response)
else:
    print(json.dumps(d_response, indent=int(args.jsonpprintindent)))

sys.exit(0)
