#!/usr/bin/python3
#
# Licensed under the GNU General Public License Version 3
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright 2013 Aron Parsons <aronparsons@gmail.com>
#

""" spacecmd - a command line interface to Spacewalk """

import logging
import os
import re
import sys
try:
    from xmlrpc import client as xmlrpclib
except ImportError:
    import xmlrpclib
import codecs
import locale
import argparse
try: # python 3
    from configparser import SafeConfigParser
except ImportError: # python 2
    from ConfigParser import SafeConfigParser
from socket import getfqdn

_INTRO = '''Welcome to spacecmd, a command-line interface to Spacewalk.

Type: 'help' for a list of commands
      'help <cmd>' for command-specific help
      'quit' to quit
'''

_SYSTEM_CONF_FILE = '/etc/spacecmd.conf'

if __name__ == '__main__':
    # disable no-member error message
    # pylint: disable=E1101

    if not sys.stdout.isatty():
        sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)

    usage = '%(prog)s [options] [command]'
    parser = argparse.ArgumentParser(usage=usage)
    parser.add_argument('-c', '--config',
                         help='config file to use [default: ~/.spacecmd/config]')
    parser.add_argument('-u', '--username',
                        help='use this username to connect to the server')
    parser.add_argument('-p', '--password',
                        help='use this password to connect to the server (insecure). Use config instead or spacecmd will ask.')
    parser.add_argument('-s', '--server',
                        help='connect to this server [default: local hostname]')
    parser.add_argument('--nossl', action='store_true',
                        help='use HTTP instead of HTTPS')
    parser.add_argument('--nohistory', action='store_true',
                        help='do not store command history')
    parser.add_argument('-y', '--yes', action='store_true',
                        help='answer yes for all questions')
    parser.add_argument('-q', '--quiet', action='store_true',
                        help='print only error messages')
    parser.add_argument('-d', '--debug', action='count', default=0,
                        help='print debug messages (can be passed multiple times)')
    parser.add_argument('command', nargs='*',
                        help=argparse.SUPPRESS)

    options = parser.parse_args()
    if options.command:
        args = options.command
    else:
        args = []

    # determine the logging level
    if options.debug:
        level = logging.DEBUG
    elif options.quiet:
        level = logging.ERROR
    else:
        level = logging.INFO

    # configure logging
    logging.basicConfig(level=level, format='%(levelname)s: %(message)s')

    # files are loaded from ~/.spacecmd/
    conf_dir = os.path.expanduser('~/.spacecmd')
    user_conf_file = os.path.join(conf_dir, 'config')

    # server-specifics will be loaded from the configuration file later
    config = SafeConfigParser()

    # prevent readline from outputting escape sequences to non-terminals
    if not sys.stdout.isatty():
        logging.debug('stdout is not a TTY, setting TERM=dumb')
        os.environ['TERM'] = 'dumb'

    # import our Cmd subclass after we settle our TERM value
    from spacecmd.shell import SpacewalkShell, UnknownCallException

    # create an instance of the shell
    shell = SpacewalkShell(options, conf_dir, config)

    # set the default server to local hostname
    if shell.options.server:
        shell.config['server'] = shell.options.server
    else:
        shell.config['server'] = getfqdn()

    # don't automatically create config files passed via --config
    if shell.options.config:
        if not os.path.isfile(shell.options.config):
            logging.error('Config file %s does not exist.', shell.options.config)
            sys.exit(1)
    else:
    # create an empty configuration file if one's not present
        if not os.path.isfile(user_conf_file):
            try:
                # create ~/.spacecmd
                if not os.path.isdir(conf_dir):
                    logging.debug('Creating %s', conf_dir)
                    os.mkdir(conf_dir, int('0700', 8))

                # create a template configuration file
                logging.debug('Creating configuration file: %s', user_conf_file)
                handle = open(user_conf_file, 'w')
                handle.write('[spacecmd]\n')
                handle.close()
            except IOError:
                logging.error('Could not create %s', user_conf_file)

    # load options from configuration files
    if shell.options.config:
        files_read = config.read([_SYSTEM_CONF_FILE, user_conf_file, shell.options.config])
    else:
        files_read = config.read([_SYSTEM_CONF_FILE, user_conf_file])

    for item in files_read:
        logging.debug('Read configuration from %s', item)

    # load the default configuration section
    shell.load_config_section('spacecmd')

    # run a single command from the command line
    if len(args):
        try:
            # rebuild the command and quote all arguments to be safe
            # except for help command
            command = args[0]

            if command == 'help':
                command = ' '.join(args)
            if len(args) > 1:
                command += ' %s' % ' '.join('%s' % s if not True in [ c.isspace() for c in s ] else "'%s'" % s for s in args[1:])

            # run the command
            precmd = shell.precmd(command)
            if precmd == '':
                sys.exit(1)
            shell.print_result(shell.onecmd(precmd), precmd)
        except KeyboardInterrupt:
            print
            print('User Interrupt')
        except UnknownCallException:
            sys.exit(1)
        except Exception as detail:
            # get the relevant part of a XML-RPC fault
            if isinstance(detail, xmlrpclib.Fault):
                detail = detail.faultString

            if shell.options.debug:
                # print(the traceback when debugging)
                logging.exception(detail)
            else:
                logging.error(detail)

            sys.exit(1)
    else:
        if not shell.options.quiet:
            print(_INTRO)

        if not shell.do_login(''):
            sys.exit(1)

        # stay in the interactive shell forever
        while True:
            try:
                shell.cmdloop()
            except KeyboardInterrupt:
                print
            except SystemExit:
                sys.exit(0)
            except UnknownCallException:
                pass
            except Exception as detail:
                # get the relevant part of a XML-RPC fault
                if isinstance(detail, xmlrpclib.Fault):
                    detail = detail.faultString

                    # the session expired
                    if re.search('Could not find session', detail, re.I):
                        shell.session = ''

                if shell.options.debug:
                    # print(the traceback when debugging)
                    logging.exception(detail)
                else:
                    logging.error(detail)
