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

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

from    pfstorage           import PfStorage, swiftStorage, StoreHandler, ThreadedHTTPServer
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_name    = "pfstorage"
str_version = "1.1.0.0"
str_desc    = Colors.CYAN + """

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


                            Path-File-storage

           An interface to open storage -- part of the pf* family.

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

    Most simply, ``pfstorage`` is a module that offers a regularized interface
    to some other backend object storage. While currently supporting ``swift``, 
    the long term idea is to support a multitude of backends. By providing its 
    own interface to several storage backends, this module removes the need for 
    client code to change when a different object storage backend is used.

    While at its core a module/library, ``pfstorage`` also provides two modes of 
    stand-alone access: (1) a command line script interface mode to the library, 
    and (2) a persistent http server mode. In the command line mode, the main 
    module functions are exposed to appropriate CLI. In the http server mode, a 
    client can use curl-type http calls to call the underlying library functions.
    
""" + \
        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

	    pfstorage

        - path-and-file object-storage

    SYNOPSIS

            _script mode_:
            pfstorage                                               \\
                [--ipSwift <swiftServerIP>]                         \\
                [--portSwift <swiftServerPort>]                     \\
                [--verbosity <level>]                               \\
                [--configFileLoad <file>]                           \\
                [--configFileSave <file>]                           \\
                [--test]                                            \\
                [--httpResponse]                                    \\
                [-x|--desc]                                         \\
                [-y|--synopsis]                                     \\
                [--type <storageBackendType>]                       \\
                --msg '<JSON_formatted_message>'
            
            _server mode_:
            pfstorage                                               \\
                [--ipSelf <storageServerIP>]                        \\
                [--portSelf <storageServerPort>]                    \\
                [--ipSwift <swiftServerIP>]                         \\
                [--portSwift <swiftServerPort>]                     \\
                [--verbosity <level>]                               \\
                [--configFileLoad <file>]                           \\
                [--configFileSave <file>]                           \\
                [--test]                                            \\
                [--httpResponse]                                    \\
                [-x|--desc]                                         \\
                [-y|--synopsis]                                     \\
                [--version]                                         \\
                [--debugToDir <dir>]                                \\
                [--verbosity <level>]                               \\
                [--type <storageBackendType>]                       \\
                --msg '<JSON_formatted_message>'

    BRIEF EXAMPLE

            _script mode_:
            pfstorage                                               \\
                --ipSwift %s                                   \\
                --portSwift 8080                                    \\
                --verbosity 1                                       \\
                --debugToDir /tmp                                   \\
                --type swift                                        \\
                --msg ' 
                { "action": "ls",
                "meta": {
                                "path":         "",       
                                "retSpec":      ["name", "bytes"]
                        }                                   
                }
                '

            _server mode_:
            pfstorage                                               \\
                --ipSwift %s                                \\
                --portSwift 8080                                    \\
                --ipSelf %s                                \\
                --portSelf 4055                                     \\
                --httpResponse                                      \\
                --verbosity 1                                       \\
                --debugToDir /tmp                                   \\
                --type swift                                        \\
                --server                                            \\
                --forever 

    ''' % (str_defIP, str_defIP, str_defIP)

    description =  '''
    DESCRIPTION

        ``pfstorage`` is a module/script that provides unifed access
        to some object storage backend (typically swift) and it is part 
        of the ``pf`` suite of applications.

        ``pfstorage`` provides:

            * A library/module for internal API access to some
              object storage backend;

            * A stand alone command line mode using this module;

            * An http server mode, in which it offers an http REST-like API
              for using the library/module.

    ARGS

        --msg '<JSON_formatted>'
        The action to perform. This can be one of:

            * objPull -- pull data from storage to file system
            * objPush -- push data from file system to storage
            * ls      -- listing of data within storage

        with a JSON formatted string similar to:

            * ls:
            { "action": "ls",
              "meta": {
                            "path":         "",       
                            "retSpec":      ["name", "bytes"]
                      }                                   
            }

            * objPut:
            {  "action": "objPut",
                "meta": {
                            "putSpec":              "./data",
                            "inLocation":           "storage",
                            "mapLocationOver":      "./data"
                        }
            } 

            * objPull:
            {  "action": "objPull",
                "meta": {
                            "path":                 "chris",
                            "substr":               "/018",
                            "fromLocation":         "chris/uploads/DICOM",
                            "mapLocationOver":      "./data"
                        }
            }                     

        [--type <storageBackendType>]
        The type of object storage. Currently this is 'swift'.

        [--ipSwift <swiftIP>]                            
        The IP interface of the object storage service. Default %s.

        [--portSwift <swiftPort>]
        The port of the object storage service. Defaults to '8080'.

        [--ipSelf <selfIP>]                            
        The IP interface of the pfstorage service for server mode. Default %s.

        [--portSelf <selfPort>]
        The port of the pfstorage service for server mode. Defaults to '4055'.

        [--httpResponse]
        In servier mode, send return strings as HTTP formatted replies 
        with content-type html.

        [--configFileLoad <file>]
        Load configuration information from the JSON formatted <file>.

        [--configFileSave <file>]
        Save configuration information to the JSON formatted <file>.

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

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

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

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

    _script mode_:
    pfstorage                                               \\
        --ipSwift %s                              \\
        --portSwift 8080                                    \\
        --verbosity 1                                       \\
        --debugToDir /tmp                                   \\
        --type swift                                        \\
        --msg ' 
        {
            "action":   "ls",
            "meta": {
                "path":         "",       
                "retSpec":      ["name", "bytes"]
            }
        }
        '

    _server mode_:
    pfstorage                                               \\
        --ipSwift %s                              \\
        --portSwift 8080                                    \\
        --ipSelf %s                               \\
        --portSelf 4055                                     \\
        --httpResponse                                      \\
        --verbosity 1                                       \\
        --debugToDir /tmp                                   \\
        --type swift                                        \\
        --server                                            \\
        --forever 

    ''' % (str_defIP, str_defIP, str_defIP, str_defIP, 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(
    '--action',
    action  = 'store',
    dest    = 'str_action',
    default = '',
    help    = 'Action to perform.'
)
parser.add_argument(
    '--args',
    action  = 'store',
    dest    = 'str_args',
    default = '',
    help    = 'Arguments for action.'
)

parser.add_argument(
    '--httpProxy',
    action  = 'store',
    dest    = 'httpProxy',
    default = '',
    help    = 'Optional proxy to use.'
)
parser.add_argument(
    '--forever',
    help    = 'if specified, serve forever, otherwise terminate after single service.',
    dest    = 'b_forever',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--httpResponse',
    help    = 'if specified, return HTTP responses',
    dest    = 'b_httpResponse',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--ipSelf',
    action  = 'store',
    dest    = 'ipSelf',
    default = str_defIP,
    help    = 'IP to connect.'
)
parser.add_argument(
    '--portSelf',
    action  = 'store',
    dest    = 'portSelf',
    default = '4055',
    help    = 'Port to use.'
)
parser.add_argument(
    '--ipSwift',
    action  = 'store',
    dest    = 'ipSwift',
    default = str_defIP,
    help    = 'IP to connect.'
)
parser.add_argument(
    '--portSwift',
    action  = 'store',
    dest    = 'portSwift',
    default = '8080',
    help    = 'Port to use.'
)
parser.add_argument(
    '--version',
    help    = 'if specified, print version number',
    dest    = 'b_version',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--test',
    help    = 'if specified, perform internal tests',
    dest    = 'b_test',
    action  = 'store_true',
    default = False
)
parser.add_argument(
    '--configFileLoad',
    help    = 'a file containing configuration information',
    dest    = 'str_configFileLoad',
    action  = 'store',
    default = ''
)
parser.add_argument(
    '--configFileSave',
    help    = 'a file to store configuration information',
    dest    = 'str_configFileSave',
    action  = 'store',
    default = ''
)
parser.add_argument(
    '--debugToDir',
    help    = 'a destination directory to contain debugging info',
    dest    = 'str_debugToDir',
    action  = 'store',
    default = ''
)
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(
    '--type',
    help    = 'the type of openstorage',
    dest    = 'str_type',
    action  = 'store',
    default = 'swift'
)
parser.add_argument(
    "-s", "--server",
    help    = "run in server mode",
    dest    = 'server',
    action  = 'store_true',
    default = False
)

args            = parser.parse_args()
args.portSelf   = int(args.portSelf)

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)

if args.b_test:
    handler     = StoreHandler(test = True)
    handler.do_POST(
        d_msg = {
            "action": "hello",
            "meta": {
                "askAbout":     "sysinfo",
                "echoBack":     "Hi there!"
                }
            }
    )
    sys.exit(0)

if not args.server:
    # pudb.set_trace()
    if args.str_type == 'swift':
        storage         = swiftStorage(
            msg         = args.msg,
            args        = vars(args),
            desc        = str_desc,
            version     = str_version
        )
        print(
            json.dumps(
                storage.run(args.msg),
                indent  = 4
            )
        )
else:
    server              = ThreadedHTTPServer((args.ipSelf, args.portSelf), StoreHandler)
    server.setup(  
                args    = vars(args), 
                name    = str_name, 
                desc    = str_desc, 
                ver     = str_version
    )
    if args.b_forever and not args.b_test:
        server.serve_forever()
    else:
        server.handle_request()


