#!/usr/bin/env python

################################################################################
#
# Copyright (c) 2012 ObjectLabs Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
################################################################################

import dargparse
import sys
import traceback
import datetime
from dex import dex

from dargparse import dargparse
from datetime import  datetime

PARSER_DEFINITION ={
    "prog": "dex",
    "description" : "Scans a provided MongoDB log file or profile collection "
                    "and uses the provided URI to compare queries found in "
                    "the logfile or profile collection to the indexes "
                    "available in the database, recommending indexes for those "
                    "queries which are not indexed. Recommended for MongoDB "
                    "version 2.2.0 or later.",
    "args": [
             {
             "name": "uri",
             "type" : "positional",
             "help": "database connection URI, including database path, and "
                     "authentication if required. If authentication is is "
                     "enabled and multiple databases need to be dexed, "
                     "this URI should point to the admin database. Required "
                     "for profile (-p) mode. Optional for logfile (-f) mode. "
                     "If absent, Dex does not verify its logfile analysis "
                     "against the database and recommends indexes for ALL "
                     "queries.",
             "nargs": "?"
             },
             {
             "name": "logfile_path",
             "type" : "optional",
             "help": "path to a MongoDB log file. If provided, "
                     "the file will be searched for queries.",
             "cmd_arg": [
                         "-f",
                         "--file"
                         ],
             "nargs": 1,
             "default": None
             },
             {
             "name": "use_profile",
             "type" : "optional",
             "help": "flag to examine the MongoDB system.profile collection. "
                     "If set, the profile collection will be searched for "
                     "queries. URI is required for profile mode.",
             "cmd_arg": [
                         "-p",
                         "--profile"
                         ],
             "action": "store_true",
             "nargs": 0,
             "default": False
             },
            {
            "name": "watch",
            "type" : "optional",
            "help": "instructs Dex to watch the system.profile or log "
                    "(depending on which -p/-f is specified) for entries, "
                    "rather than processing existing content. Upon keyboard "
                    "interrupt (Ctrl+C) watch terminates and the accumulated "
                    "output is provided. When using watch mode and profile "
                    "mode together, you must target a specific database using "
                    "-n \"dbname.*\"",
            "cmd_arg": [
                "-w",
                "--watch"
            ],
            "nargs": 1,
            "action": "store_true",
            "default": False
            },
             {
             "name": "namespaces",
             "type" : "optional",
             "help": "a MongoDB namespace (db.collection). Can be provided "
                     "multiple times. This option creates a filter, and "
                     "queries not in the provided namespace(s) will not be "
                     "analyzed. Format: -n ('db.collection' | '*' | 'db.*' | "
                     "'collection'). '*.*' and '*.collection' are redundant "
                     "but also supported. An asterisk is shorthand for "
                     "'all'--actual regexes are not supported. Note that "
                     "-n '*' is equivalent to not providing a -n argument.",
             "cmd_arg": [
                         "-n",
                         "--namespace"
                         ],
             "nargs": 1,
             "action": "append",
             "default": []
             },
             {
             "name": "verbose",
             "type" : "optional",
             "help": "enables provision of additional output information, "
                     "including Dex's query and index analysis structures.",
             "cmd_arg": [
                         "-v",
                         "--verbose"
                         ],
             "action": "store_true",
             "nargs": 0,
             "default": False
             }
             
             ]


}



###############################################################################
# MAIN
###############################################################################
def main(args):
    # build the parse object
    darg_parser = dargparse.build_parser(PARSER_DEFINITION)
    
    # parse the command line
    options = darg_parser.parse_args(args)
    namespaces = options.namespaces

    md = dex.Dex(options.uri, options.verbose, namespaces)

    if options.use_profile:
        if options.uri is None:
            sys.stderr.write("URI is required for profile mode.\n")
            return 1
        if options.watch is False:
            return md.analyze_profile()
        else:
            return md.watch_profile()
    elif options.logfile_path is not None:
        if options.uri is None:
            sys.stderr.write("No URI provided. Only logfile will be processed. "
                             "You may receive recommendations for indexes "
                             "that already exist.\n")
        if options.watch is False:
            return md.analyze_logfile(options.logfile_path)
        else:
            return md.watch_logfile(options.logfile_path)


###############################################################################
########################                   ####################################
########################     BOOTSTRAP     ####################################
########################                   ####################################
###############################################################################

if __name__ == '__main__':
    try:
        # call main with a sub-list starting skipping the
        # command itself (which is the first arg)
        main(sys.argv[1:])
    except (SystemExit, KeyboardInterrupt) , e:
        if e.code == 0:
            pass
        else:
            raise
    except:
        traceback.print_exc()
