#!/usr/bin/env python

# Copyright (C) 2011-2012, Aleksey Lim
#
# 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, see <http://www.gnu.org/licenses/>.

import os
import sys
import signal
import logging
from optparse import OptionParser
from os.path import basename
from gettext import gettext as _

import gobject
import dbus.glib
import dbus.mainloop.glib

gobject.threads_init()
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

from sugar_client import env
env.BaseService = env.DBusService

from sugar_client import util, printf, server, backup, system, ipc, stats
from sugar_client import session
from sugar_client.util import enforce


def cmd_session():
    enforce(os.geteuid() != 0, _('User should not be root'))
    _start(dbus.SessionBus(), [server, backup, stats],
            lambda services: session.start(*services))


def cmd_system():
    enforce(os.geteuid() == 0, _('User should be root'))
    _start(dbus.SystemBus(), [system], lambda services: None)


def cmd_config():
    if args:
        opt = args.pop(0)
        enforce(opt in util.Option.items, _('Unknown option "%s"'), opt)
        exit(0 if bool(util.Option.items[opt].value) else 1)
    else:
        print '\n'.join(util.Option.export())


def _start(bus, mods, cb):
    bus_name = type(bus).__name__
    lock = ipc.exclusive_access(
            env.profile_path('sugar-client-%s.lock' % bus_name))
    mainloop = gobject.MainLoop()

    def disconnected_cb():
        mainloop.quit()
        logging.info(_('Server disconnected from the bus'))

    bus.add_signal_receiver(disconnected_cb, signal_name='Disconnected',
            dbus_interface='org.freedesktop.DBus.Local')
    bus.set_exit_on_disconnect(False)

    def SIGTERM_cb(signum, frame):
        logging.info(_('Shutting down server on signal %s'), signum)
        mainloop.quit()

    signal.signal(signal.SIGHUP, SIGTERM_cb)
    signal.signal(signal.SIGTERM, SIGTERM_cb)

    try:
        logging.info(_('Starting server on %s'), bus_name)
        cb([i.Service() for i in mods])
        logging.info(_('Server started successfully'))
        mainloop.run()
    except KeyboardInterrupt:
        logging.info(_('Server shutdown by user'))
    except Exception:
        logging.exception(_('Server shutdown with error'))
        exit(1)
    finally:
        lock.close()


HELP = """
Commands:
  session               start service(s) on session bus
  system                start service(s) on system bus
  config                output current configuration

See http://wiki.sugarlabs.org/go/Sugar_Server_Kit/sugar-client for details."""

parser = OptionParser(
        usage='%prog [OPTIONS]',
        description='sugar-client D-Bus services to interact with ' \
                    'a school server.',
        add_help_option=False)
parser.print_version = \
        lambda: sys.stdout.write('%s\n' % env.VERSION)
parser.add_option('-h', '--help',
        help=_('show this help message and exit'),
        action='store_true')
parser.add_option('-V', '--version',
        help=_('show version number and exit'),
        action='version')
util.Option.bind(parser, [
        '/etc/sugar-client.conf',
        '~/.config/sugar-client/config',
        ])

options, args = parser.parse_args()

if not args and not options.help:
    prog = basename(sys.argv[0])
    print 'Usage: %s [OPTIONS] [COMMAND]' % prog
    print '       %s -h|--help' % prog
    print
    print parser.description
    print HELP
    exit(0)

if options.help:
    parser.print_help()
    print HELP
    exit(0)

command = args.pop(0)
stdout_fd = os.dup(sys.stdout.fileno())
stderr_fd = os.dup(sys.stderr.fileno())

try:
    util.Option.merge(options)

    if not env.debug.value:
        level = logging.WARNING
    elif env.debug.value == 1:
        level = logging.INFO
    else:
        level = logging.DEBUG
    logging.basicConfig(level=level, stream=sys.stderr,
            format='%(asctime)s %(levelname)s %(name)s: %(message)s')

    if os.geteuid():
        util.Option.merge(options, [env.session()])
        log_path = env.profile_path('logs', 'sugar-client-%s.log' % command)
    else:
        log_path = '/var/log/sugar-client.log'

    if command != 'config':
        # Point stdout and stderr to the log file
        # to avoid loosing logs for tty-less service launches
        log_fd = os.open(log_path, os.O_APPEND | os.O_CREAT | os.O_WRONLY)
        os.dup2(log_fd, sys.stdout.fileno())
        os.dup2(log_fd, sys.stderr.fileno())
        os.close(log_fd)

    enforce('cmd_' + command in globals(),
            _('Unknown command "%s"') % command)
    exit(globals()['cmd_' + command]() or 0)

except Exception, error:
    util.exception(_('sugar-client aborted: %s'), error)
    os.dup2(stdout_fd, sys.stdout.fileno())
    os.dup2(stderr_fd, sys.stderr.fileno())
    printf.exception()
    exit(1)
finally:
    printf.flush_hints()
