#!/usr/bin/python
#
# 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
import xmlrpclib
import codecs
import locale
from ConfigParser import SafeConfigParser
from optparse import Option, OptionParser
from socket import gethostname

_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)

    optionsTable = [
        Option('-u', '--username', action='store',
               help='use this username to connect to the server'),
        Option('-p', '--password', action='store',
               help='use this password to connect to the server'),
        Option('-s', '--server', action='store',
               help='connect to this server [default: local hostname]'),
        Option('--nossl', action='store_true',
               help='use HTTP instead of HTTPS'),
        Option('--nohistory', action='store_true',
               help='do not store command history'),
        Option('-y', '--yes', action='store_true',
               help='answer yes for all questions'),
        Option('-q', '--quiet', action='store_true',
               help='print only error messages'),
        Option('-d', '--debug', action='count',
               help='print debug messages (can be passed multiple times)'),
    ]

    usage = 'usage: %prog [options] [command]'

    parser = OptionParser(option_list=optionsTable, usage=usage)
    (options, args) = parser.parse_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

    # 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'] = gethostname()

    # 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, 0700)

            # 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
    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
            command = args[0]

            if len(args) > 1:
                command += ' %s' % ' '.join('%s' % s for s in args[1:])

            # run the command
            precmd = shell.precmd(command)
            shell.print_result(shell.onecmd(precmd), precmd)
        except KeyboardInterrupt:
            print
            print 'User Interrupt'
        except Exception, 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 Exception, 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)
