#!/usr/bin/python3.6
# dmliteshell.py
"""
This file implements the DMLite shell.
The shell provides an easy access to many features of the DMLite library
via the Python module pydmlite. In addition some functionalities requires
direct database access.
"""
from __future__ import absolute_import
from __future__ import print_function
from __future__ import division

import os
import sys
import atexit
import argparse
import logging, logging.handlers

try:
    input = raw_input  # Redefine for Python 2
except NameError:
    pass

__version__ = '1.14.0'

_log = logging.getLogger('dmlite-shell')


def main():
    # basic logging configuration
    streamHandler = logging.StreamHandler(sys.stderr)
    streamHandler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s](%(module)s:%(lineno)d) %(message)s", "%d %b %H:%M:%S"))
    _log.addHandler(streamHandler)
    _log.setLevel(logging.WARN)

    # parse options from command line
    class VerbosityAction(argparse.Action):

        def __call__(self, parser, namespace, values, option_string=None):
            loglevel = self.default
            if len(values) > 0:
                loglevel = int({
                    'CRITICAL': logging.CRITICAL,
                    'DEBUG': logging.DEBUG,
                    'ERROR': logging.ERROR,
                    'FATAL': logging.FATAL,
                    'INFO': logging.INFO,
                    'NOTSET': logging.NOTSET,
                    'WARN': logging.WARN,
                    'WARNING': logging.WARNING,
                }.get(values[0].upper(), values[0]))
            _log.setLevel(loglevel)
            setattr(namespace, self.dest, loglevel)

    parser = argparse.ArgumentParser()
    # logging arguments
    parser.add_argument("-v", "--verbose", dest="loglevel", action=VerbosityAction, default=logging.DEBUG, nargs=0, help="set log level to DEBUG (10)")
    parser.add_argument("-q", "--quiet", dest="loglevel", action=VerbosityAction, default=logging.ERROR, nargs=0, help="set log level to ERROR (40)")
    parser.add_argument("--log-level", dest="loglevel", action=VerbosityAction, default=logging.WARN, nargs=1, help="set log level, default: %(default)s")
    parser.add_argument("--log-file", dest="logfile", metavar="FILENAME", default='<stdout>', help="set log file, default: %(default)s")
    parser.add_argument("--log-size", dest="logsize", metavar="FILESIZE", type=int, default=10 * 1024 * 1024, help="maximum size of log file, default: %(default)s")
    parser.add_argument("--log-backup", dest="logbackup", metavar="COUNT", type=int, default=4, help="number of log backup files, default: %(default)s")
    # shell arguments
    parser.add_argument("-c", "--config", dest="configfile", metavar="FILENAME", help="define the configuration file before launching the shell, default: %(default)s", default="/etc/dmlite.conf")
    parser.add_argument("-e", "--execute", dest="commands", action='append', help="execute the given command (or multiple commands) and exit", default=[])
    parser.add_argument("-s", "--script", dest="scriptfile", metavar="FILENAME", help="execute the given script file line by line and exit", default='')

    options = parser.parse_args()

    log_format = logging.Formatter("%(asctime)s [%(levelname)s](%(module)s:%(lineno)d) %(message)s", "%d %b %H:%M:%S")
    log_format_empty = logging.Formatter()
    if options.logfile in ['-', '<stdout>', '<stderr>']:
        if options.logfile != '<stderr>':
            _log.removeHandler(streamHandler)
            streamHandler = logging.StreamHandler(sys.stdout)
            streamHandler.setFormatter(log_format)
            _log.addHandler(streamHandler)
    elif options.logfile == '<syslog>':
        syslogHandler = logging.handlers.SysLogHandler()
        #syslogHandler.setFormatter(logging.Formatter("[%(levelname)s](%(module)s:%(lineno)d) %(message)s"))
        _log.addHandler(syslogHandler)
        _log.removeHandler(streamHandler)
    elif options.logfile != '':
        try:
            #fileHandler = logging.handlers.TimedRotatingFileHandler(options.logfile, 'midnight', 1, 4)
            fileHandler = logging.handlers.RotatingFileHandler(options.logfile, maxBytes=options.logsize, backupCount=options.logbackup)
            fileHandler.setFormatter(log_format)
            _log.addHandler(fileHandler)
            _log.removeHandler(streamHandler)
        except Exception as e:
            _log.error("unable to open log file: %s", str(e))
            sys.exit(1)

    _log.info("command: %s", " ".join(sys.argv))
    _log.debug("script: %s", os.path.abspath(__file__))

    #try:
    #    import pydmlite
    #except ImportError as e:
    #    _log.error("unable to import pydmlite module, missing python*-dmlite package")
    #    sys.exit(1)
    try:
        from dmliteshell import interpreter
    except ImportError as e:
        _log.error("unable to import dmlite module: %s", str(e))
        sys.exit(1)

    #_log.debug("version: %s (DMLite API v%s)", __version__, pydmlite.API_VERSION)
    _log.debug("python: %s", str(sys.version_info))

    # init interpreter
    quietMode = (options.commands) or (options.scriptfile) or (not sys.__stdin__.isatty())
    def log_write(msg):
        streamHandler.setFormatter(log_format_empty)
        _log.warning(msg[:-1])
        streamHandler.setFormatter(log_format)
    interpreter2 = interpreter.DMLiteInterpreter(log_write, options.configfile, quietMode)
    if interpreter2.failed: # initialisation failed
        return

    if options.commands:
        for command in options.commands:
            if interpreter2.failed:
                break

            interpreter2.execute(command)

        print()

    elif options.scriptfile:
        try:
            for line in open(options.scriptfile, 'r'):
                line = line.strip()
                interpreter2.execute(line)

                # in non-interactive mode an error stops execution
                if interpreter2.failed:
                    break

        except Exception as e:
            interpreter2.error('Error while trying to execute file "' + options.scriptfile + '":\n' + e.args[1])

        # exit... clear line
        print()

    else:
        import readline
        # init dmlite shell
        init_history()

        # init the shell auto completion functionality
        readline.set_completer_delims(' \t//')
        readline.set_completer((lambda text, state: interpreter2.completer(readline.get_line_buffer(), state)))
        readline.parse_and_bind('tab: complete')

        # dmlite shell loop
        while not interpreter2.exit:

            # get next command
            try:
                if sys.__stdin__.isatty():
                    cmdline = input('> ')
                else:
                    cmdline = input()
                cmdline = cmdline.strip()
            except EOFError:
                # all commands from input have been executed, exit...
                break
            except KeyboardInterrupt:
                # on CTRL-C
                if readline.get_line_buffer() == '':
                    # exit if CTRL-C on empty line
                    break
                else:
                    print()
                    continue

            # execute next command
            interpreter2.execute(cmdline)

            # in non-interactive mode an error stops execution
            if (not sys.__stdin__.isatty()) and interpreter2.failed:
                break

        # exit... clear line
        print()
    sys.exit(interpreter2.failed)


def init_history():
    import readline
    # init the shell history functionality
    # the history is saved in ~/.dmlite_history
    history_file = os.path.join(os.path.expanduser('~'), '.dmlite_history')
    try:
        readline.read_history_file(history_file)
    except IOError:
        pass

    atexit.register(readline.write_history_file, history_file)


# this is an executable module
if __name__ == '__main__':
    main()
